Merge "Refactor UBMS to use LifecycleOperationStorage."
diff --git a/Android.bp b/Android.bp
index 95cdea0..3b8ef61 100644
--- a/Android.bp
+++ b/Android.bp
@@ -69,7 +69,7 @@
// Java/AIDL sources under frameworks/base
":framework-annotations",
":framework-blobstore-sources",
- ":framework-connectivity-nsd-sources",
+ ":framework-connectivity-tiramisu-sources",
":framework-core-sources",
":framework-drm-sources",
":framework-graphics-nonupdatable-sources",
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index 005c447..a6a007f 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -46,6 +46,7 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArrayMap;
+import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -681,8 +682,8 @@
final long minBalance = mIrs.getMinBalanceLocked(userId, pkgName);
final double perc = batteryLevel / 100d;
// TODO: maybe don't give credits to bankrupt apps until battery level >= 50%
- if (ledger.getCurrentBalance() < minBalance) {
- final long shortfall = minBalance - getBalanceLocked(userId, pkgName);
+ final long shortfall = minBalance - ledger.getCurrentBalance();
+ if (shortfall > 0) {
recordTransactionLocked(userId, pkgName, ledger,
new Ledger.Transaction(now, now, REGULATION_BASIC_INCOME,
null, (long) (perc * shortfall)), true);
@@ -1170,5 +1171,57 @@
void dumpLocked(IndentingPrintWriter pw) {
pw.println();
mBalanceThresholdAlarmQueue.dump(pw);
+
+ pw.println();
+ pw.println("Ongoing events:");
+ pw.increaseIndent();
+ boolean printedEvents = false;
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ for (int u = mCurrentOngoingEvents.numMaps() - 1; u >= 0; --u) {
+ final int userId = mCurrentOngoingEvents.keyAt(u);
+ for (int p = mCurrentOngoingEvents.numElementsForKey(userId) - 1; p >= 0; --p) {
+ final String pkgName = mCurrentOngoingEvents.keyAt(u, p);
+ final SparseArrayMap<String, OngoingEvent> ongoingEvents =
+ mCurrentOngoingEvents.get(userId, pkgName);
+
+ boolean printedApp = false;
+
+ for (int e = ongoingEvents.numMaps() - 1; e >= 0; --e) {
+ final int eventId = ongoingEvents.keyAt(e);
+ for (int t = ongoingEvents.numElementsForKey(eventId) - 1; t >= 0; --t) {
+ if (!printedApp) {
+ printedApp = true;
+ pw.println(appToString(userId, pkgName));
+ pw.increaseIndent();
+ }
+ printedEvents = true;
+
+ OngoingEvent ongoingEvent = ongoingEvents.valueAt(e, t);
+
+ pw.print(EconomicPolicy.eventToString(ongoingEvent.eventId));
+ if (ongoingEvent.tag != null) {
+ pw.print("(");
+ pw.print(ongoingEvent.tag);
+ pw.print(")");
+ }
+ pw.print(" runtime=");
+ TimeUtils.formatDuration(nowElapsed - ongoingEvent.startTimeElapsed, pw);
+ pw.print(" delta/sec=");
+ pw.print(ongoingEvent.deltaPerSec);
+ pw.print(" refCount=");
+ pw.print(ongoingEvent.refCount);
+ pw.println();
+ }
+ }
+
+ if (printedApp) {
+ pw.decreaseIndent();
+ }
+ }
+ }
+ if (!printedEvents) {
+ pw.print("N/A");
+ }
+ pw.decreaseIndent();
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 20a300a..36895a5 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -567,20 +567,23 @@
final String pkgName = event.getPackageName();
if (DEBUG) {
Slog.d(TAG, "Processing event " + event.getEventType()
+ + " (" + event.mInstanceId + ")"
+ " for " + appToString(userId, pkgName));
}
final long nowElapsed = SystemClock.elapsedRealtime();
switch (event.getEventType()) {
case UsageEvents.Event.ACTIVITY_RESUMED:
mAgent.noteOngoingEventLocked(userId, pkgName,
- EconomicPolicy.REWARD_TOP_ACTIVITY, null, nowElapsed);
+ EconomicPolicy.REWARD_TOP_ACTIVITY, String.valueOf(event.mInstanceId),
+ nowElapsed);
break;
case UsageEvents.Event.ACTIVITY_PAUSED:
case UsageEvents.Event.ACTIVITY_STOPPED:
case UsageEvents.Event.ACTIVITY_DESTROYED:
final long now = getCurrentTimeMillis();
mAgent.stopOngoingActionLocked(userId, pkgName,
- EconomicPolicy.REWARD_TOP_ACTIVITY, null, nowElapsed, now);
+ EconomicPolicy.REWARD_TOP_ACTIVITY, String.valueOf(event.mInstanceId),
+ nowElapsed, now);
break;
case UsageEvents.Event.USER_INTERACTION:
case UsageEvents.Event.CHOOSER_ACTION:
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
index a234ae6..f4917ad 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
@@ -138,6 +138,11 @@
dumpTime(pw, transaction.endTimeMs);
pw.print(": ");
pw.print(EconomicPolicy.eventToString(transaction.eventId));
+ if (transaction.tag != null) {
+ pw.print("(");
+ pw.print(transaction.tag);
+ pw.print(")");
+ }
pw.print(" --> ");
pw.println(narcToString(transaction.delta));
}
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index af4053f..05a06619 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -105,6 +105,7 @@
static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
static const char PROGRESS_PROP_NAME[] = "service.bootanim.progress";
static const char DISPLAYS_PROP_NAME[] = "persist.service.bootanim.displays";
+static const char CLOCK_ENABLED_PROP_NAME[] = "persist.sys.bootanim.clock.enabled";
static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1;
static constexpr size_t TEXT_POS_LEN_MAX = 16;
static const int DYNAMIC_COLOR_COUNT = 4;
@@ -1320,6 +1321,8 @@
}
if (!anyPartHasClock) {
mClockEnabled = false;
+ } else if (!android::base::GetBoolProperty(CLOCK_ENABLED_PROP_NAME, false)) {
+ mClockEnabled = false;
}
// Check if npot textures are supported
diff --git a/core/api/current.txt b/core/api/current.txt
index cc8b638..ee44198 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8757,10 +8757,10 @@
method public android.bluetooth.BluetoothDevice getRemoteDevice(byte[]);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public int getScanMode();
method public int getState();
- method public int isCisCentralSupported();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean isDiscovering();
method public boolean isEnabled();
method public boolean isLe2MPhySupported();
+ method public int isLeAudioSupported();
method public boolean isLeCodedPhySupported();
method public boolean isLeExtendedAdvertisingSupported();
method public boolean isLePeriodicAdvertisingSupported();
@@ -10127,8 +10127,10 @@
ctor public CompanionDeviceService();
method @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public final void dispatchMessage(int, int, @NonNull byte[]);
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
- method @MainThread public abstract void onDeviceAppeared(@NonNull String);
- method @MainThread public abstract void onDeviceDisappeared(@NonNull String);
+ method @Deprecated @MainThread public void onDeviceAppeared(@NonNull String);
+ method @MainThread public void onDeviceAppeared(@NonNull android.companion.AssociationInfo);
+ method @Deprecated @MainThread public void onDeviceDisappeared(@NonNull String);
+ method @MainThread public void onDeviceDisappeared(@NonNull android.companion.AssociationInfo);
method @MainThread public void onDispatchMessage(int, int, @NonNull byte[]);
field public static final String SERVICE_INTERFACE = "android.companion.CompanionDeviceService";
}
@@ -12804,7 +12806,8 @@
method @Nullable public abstract android.graphics.drawable.Drawable getActivityBanner(@NonNull android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.graphics.drawable.Drawable getActivityIcon(@NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.graphics.drawable.Drawable getActivityIcon(@NonNull android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException;
- method @NonNull public abstract android.content.pm.ActivityInfo getActivityInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @Deprecated @NonNull public abstract android.content.pm.ActivityInfo getActivityInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public android.content.pm.ActivityInfo getActivityInfo(@NonNull android.content.ComponentName, @NonNull android.content.pm.PackageManager.ComponentInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract android.graphics.drawable.Drawable getActivityLogo(@NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract android.graphics.drawable.Drawable getActivityLogo(@NonNull android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract java.util.List<android.content.pm.PermissionGroupInfo> getAllPermissionGroups(int);
@@ -12813,7 +12816,8 @@
method public abstract int getApplicationEnabledSetting(@NonNull String);
method @NonNull public abstract android.graphics.drawable.Drawable getApplicationIcon(@NonNull android.content.pm.ApplicationInfo);
method @NonNull public abstract android.graphics.drawable.Drawable getApplicationIcon(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
- method @NonNull public abstract android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @Deprecated @NonNull public abstract android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, @NonNull android.content.pm.PackageManager.ApplicationInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract CharSequence getApplicationLabel(@NonNull android.content.pm.ApplicationInfo);
method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull android.content.pm.ApplicationInfo);
method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -12824,9 +12828,11 @@
method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo);
method public void getGroupOfPlatformPermission(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.String>);
method @NonNull public android.content.pm.InstallSourceInfo getInstallSourceInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
- method @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
+ method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
+ method @NonNull public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(@NonNull android.content.pm.PackageManager.ApplicationInfoFlags);
method @NonNull public java.util.List<android.content.pm.ModuleInfo> getInstalledModules(int);
- method @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
+ method @Deprecated @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
+ method @NonNull public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(@NonNull android.content.pm.PackageManager.PackageInfoFlags);
method @Deprecated @Nullable public abstract String getInstallerPackageName(@NonNull String);
method @NonNull public abstract byte[] getInstantAppCookie();
method public abstract int getInstantAppCookieMaxBytes();
@@ -12837,15 +12843,21 @@
method @NonNull public java.util.Set<java.lang.String> getMimeGroup(@NonNull String);
method @NonNull public android.content.pm.ModuleInfo getModuleInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract String getNameForUid(int);
- method @Nullable public android.content.pm.PackageInfo getPackageArchiveInfo(@NonNull String, int);
+ method @Deprecated @Nullable public android.content.pm.PackageInfo getPackageArchiveInfo(@NonNull String, int);
+ method @Nullable public android.content.pm.PackageInfo getPackageArchiveInfo(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags);
method public abstract int[] getPackageGids(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
- method public abstract int[] getPackageGids(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public abstract android.content.pm.PackageInfo getPackageInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public abstract android.content.pm.PackageInfo getPackageInfo(@NonNull android.content.pm.VersionedPackage, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @Deprecated public abstract int[] getPackageGids(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @Nullable public int[] getPackageGids(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @Deprecated public abstract android.content.pm.PackageInfo getPackageInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public android.content.pm.PackageInfo getPackageInfo(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @Deprecated public abstract android.content.pm.PackageInfo getPackageInfo(@NonNull android.content.pm.VersionedPackage, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public android.content.pm.PackageInfo getPackageInfo(@NonNull android.content.pm.VersionedPackage, @NonNull android.content.pm.PackageManager.PackageInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.pm.PackageInstaller getPackageInstaller();
- method public abstract int getPackageUid(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @Deprecated public abstract int getPackageUid(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public int getPackageUid(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract String[] getPackagesForUid(int);
- method @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(@NonNull String[], int);
+ method @Deprecated @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(@NonNull String[], int);
+ method @NonNull public java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(@NonNull String[], @NonNull android.content.pm.PackageManager.PackageInfoFlags);
method @NonNull public abstract android.content.pm.PermissionGroupInfo getPermissionGroupInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.pm.PermissionInfo getPermissionInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public void getPlatformPermissionsForGroup(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<java.lang.String>>);
@@ -12853,14 +12865,18 @@
method @Deprecated @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int);
method @NonNull public android.content.pm.PackageManager.Property getProperty(@NonNull String, @NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public android.content.pm.PackageManager.Property getProperty(@NonNull String, @NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
- method @NonNull public abstract android.content.pm.ProviderInfo getProviderInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method @NonNull public abstract android.content.pm.ActivityInfo getReceiverInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @Deprecated @NonNull public abstract android.content.pm.ProviderInfo getProviderInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public android.content.pm.ProviderInfo getProviderInfo(@NonNull android.content.ComponentName, @NonNull android.content.pm.PackageManager.ComponentInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @Deprecated @NonNull public abstract android.content.pm.ActivityInfo getReceiverInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public android.content.pm.ActivityInfo getReceiverInfo(@NonNull android.content.ComponentName, @NonNull android.content.pm.PackageManager.ComponentInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.res.Resources getResourcesForActivity(@NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.res.Resources getResourcesForApplication(@NonNull android.content.pm.ApplicationInfo) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public android.content.res.Resources getResourcesForApplication(@NonNull android.content.pm.ApplicationInfo, @Nullable android.content.res.Configuration) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.res.Resources getResourcesForApplication(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
- method @NonNull public abstract android.content.pm.ServiceInfo getServiceInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method @NonNull public abstract java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
+ method @Deprecated @NonNull public abstract android.content.pm.ServiceInfo getServiceInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public android.content.pm.ServiceInfo getServiceInfo(@NonNull android.content.ComponentName, @NonNull android.content.pm.PackageManager.ComponentInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @Deprecated @NonNull public abstract java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
+ method @NonNull public java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(@NonNull android.content.pm.PackageManager.PackageInfoFlags);
method @Nullable public android.os.Bundle getSuspendedPackageAppExtras();
method public boolean getSyntheticAppDetailsActivityEnabled(@NonNull String);
method @NonNull public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
@@ -12888,13 +12904,19 @@
method public abstract boolean isSafeMode();
method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryActivityProperty(@NonNull String);
method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryApplicationProperty(@NonNull String);
- method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(@NonNull android.content.Intent, int);
- method @NonNull public abstract java.util.List<android.content.pm.ProviderInfo> queryContentProviders(@Nullable String, int, int);
+ method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(@NonNull android.content.Intent, int);
+ method @NonNull public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
+ method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ProviderInfo> queryContentProviders(@Nullable String, int, int);
+ method @NonNull public java.util.List<android.content.pm.ProviderInfo> queryContentProviders(@Nullable String, int, @NonNull android.content.pm.PackageManager.ComponentInfoFlags);
method @NonNull public abstract java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(@NonNull String, int);
- method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentActivities(@NonNull android.content.Intent, int);
- method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentActivityOptions(@Nullable android.content.ComponentName, @Nullable android.content.Intent[], @NonNull android.content.Intent, int);
- method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(@NonNull android.content.Intent, int);
- method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentServices(@NonNull android.content.Intent, int);
+ method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentActivities(@NonNull android.content.Intent, int);
+ method @NonNull public java.util.List<android.content.pm.ResolveInfo> queryIntentActivities(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
+ method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentActivityOptions(@Nullable android.content.ComponentName, @Nullable android.content.Intent[], @NonNull android.content.Intent, int);
+ method @NonNull public java.util.List<android.content.pm.ResolveInfo> queryIntentActivityOptions(@Nullable android.content.ComponentName, @Nullable java.util.List<android.content.Intent>, @NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
+ method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(@NonNull android.content.Intent, int);
+ method @NonNull public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
+ method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentServices(@NonNull android.content.Intent, int);
+ method @NonNull public java.util.List<android.content.pm.ResolveInfo> queryIntentServices(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
method @NonNull public abstract java.util.List<android.content.pm.PermissionInfo> queryPermissionsByGroup(@Nullable String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryProviderProperty(@NonNull String);
method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryReceiverProperty(@NonNull String);
@@ -12903,9 +12925,12 @@
method public abstract void removePermission(@NonNull String);
method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.pm.PackageManager.OnChecksumsReadyListener) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
- method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
- method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int);
- method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
+ method @Deprecated @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
+ method @Nullable public android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
+ method @Deprecated @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int);
+ method @Nullable public android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, @NonNull android.content.pm.PackageManager.ComponentInfoFlags);
+ method @Deprecated @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
+ method @Nullable public android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
method public abstract void setApplicationCategoryHint(@NonNull String, int);
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public abstract void setApplicationEnabledSetting(@NonNull String, int, int);
method @RequiresPermission(value="android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS", conditional=true) public boolean setAutoRevokeWhitelisted(@NonNull String, boolean);
@@ -13107,6 +13132,11 @@
field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
}
+ public static final class PackageManager.ApplicationInfoFlags {
+ method public long getValue();
+ method @NonNull public static android.content.pm.PackageManager.ApplicationInfoFlags of(long);
+ }
+
public static final class PackageManager.ComponentEnabledSetting implements android.os.Parcelable {
ctor public PackageManager.ComponentEnabledSetting(@NonNull android.content.ComponentName, int, int);
method public int describeContents();
@@ -13117,6 +13147,11 @@
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.PackageManager.ComponentEnabledSetting> CREATOR;
}
+ public static final class PackageManager.ComponentInfoFlags {
+ method public long getValue();
+ method @NonNull public static android.content.pm.PackageManager.ComponentInfoFlags of(long);
+ }
+
public static class PackageManager.NameNotFoundException extends android.util.AndroidException {
ctor public PackageManager.NameNotFoundException();
ctor public PackageManager.NameNotFoundException(String);
@@ -13126,6 +13161,11 @@
method public void onChecksumsReady(@NonNull java.util.List<android.content.pm.ApkChecksum>);
}
+ public static final class PackageManager.PackageInfoFlags {
+ method public long getValue();
+ method @NonNull public static android.content.pm.PackageManager.PackageInfoFlags of(long);
+ }
+
public static final class PackageManager.Property implements android.os.Parcelable {
method public int describeContents();
method public boolean getBoolean();
@@ -13145,6 +13185,11 @@
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.PackageManager.Property> CREATOR;
}
+ public static final class PackageManager.ResolveInfoFlags {
+ method public long getValue();
+ method @NonNull public static android.content.pm.PackageManager.ResolveInfoFlags of(long);
+ }
+
@Deprecated public class PackageStats implements android.os.Parcelable {
ctor @Deprecated public PackageStats(String);
ctor @Deprecated public PackageStats(android.os.Parcel);
@@ -17843,6 +17888,7 @@
field public static final int RGB_565 = 4; // 0x4
field public static final int RGB_888 = 3; // 0x3
field public static final int S_UI8 = 53; // 0x35
+ field public static final long USAGE_COMPOSER_OVERLAY = 2048L; // 0x800L
field public static final long USAGE_CPU_READ_OFTEN = 3L; // 0x3L
field public static final long USAGE_CPU_READ_RARELY = 2L; // 0x2L
field public static final long USAGE_CPU_WRITE_OFTEN = 48L; // 0x30L
@@ -31934,8 +31980,8 @@
method @Deprecated public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
method public <K, V> void readMap(@NonNull java.util.Map<? super K,? super V>, @Nullable ClassLoader, @NonNull Class<K>, @NonNull Class<V>);
method @Deprecated @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
- method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
- method @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
+ method @Nullable public <T> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
+ method @Deprecated @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
method @Nullable public <T> T[] readParcelableArray(@Nullable ClassLoader, @NonNull Class<T>);
method @Deprecated @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
method @Nullable public <T> android.os.Parcelable.Creator<T> readParcelableCreator(@Nullable ClassLoader, @NonNull Class<T>);
@@ -31944,7 +31990,7 @@
method @Nullable public android.os.PersistableBundle readPersistableBundle();
method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader);
method @Deprecated @Nullable public java.io.Serializable readSerializable();
- method @Nullable public <T extends java.io.Serializable> T readSerializable(@Nullable ClassLoader, @NonNull Class<T>);
+ method @Nullable public <T> T readSerializable(@Nullable ClassLoader, @NonNull Class<T>);
method @NonNull public android.util.Size readSize();
method @NonNull public android.util.SizeF readSizeF();
method @Deprecated @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader);
@@ -32477,7 +32523,7 @@
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.CREATE_USERS"}) public int getUserCount();
method public long getUserCreationTime(android.os.UserHandle);
method public android.os.UserHandle getUserForSerialNumber(long);
- method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, "android.permission.CREATE_USERS"}, conditional=true) public String getUserName();
+ method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.CREATE_USERS", "android.permission.QUERY_USERS", android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public String getUserName();
method public java.util.List<android.os.UserHandle> getUserProfiles();
method public android.os.Bundle getUserRestrictions();
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
@@ -40455,6 +40501,7 @@
field public static final String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
field public static final String EXTRA_IS_RTT_AUDIO_PRESENT = "android.telecom.extra.IS_RTT_AUDIO_PRESENT";
field public static final String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+ field public static final String EXTRA_LAST_KNOWN_CELL_IDENTITY = "android.telecom.extra.LAST_KNOWN_CELL_IDENTITY";
field public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
field public static final int PROPERTY_ASSISTED_DIALING = 512; // 0x200
field public static final int PROPERTY_CROSS_SIM = 8192; // 0x2000
@@ -43236,6 +43283,7 @@
method public int getPhoneType();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
method @Nullable @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
+ method @Nullable @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState(boolean, boolean);
method @Nullable public android.telephony.SignalStrength getSignalStrength();
method public int getSimCarrierId();
method @Nullable public CharSequence getSimCarrierIdName();
@@ -43288,8 +43336,10 @@
method public boolean isWorldPhone();
method @Deprecated public void listen(android.telephony.PhoneStateListener, int);
method public void registerTelephonyCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyCallback);
+ method public void registerTelephonyCallback(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyCallback);
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
+ method @Nullable @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(boolean, @NonNull android.telephony.NetworkScanRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyScanManager.NetworkScanCallback);
method public void sendDialerSpecialCode(String);
method public String sendEnvelopeWithStatus(String);
method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
@@ -48997,6 +49047,7 @@
method @NonNull public android.view.SurfaceControl build();
method @NonNull public android.view.SurfaceControl.Builder setBufferSize(@IntRange(from=0) int, @IntRange(from=0) int);
method @NonNull public android.view.SurfaceControl.Builder setFormat(int);
+ method @NonNull public android.view.SurfaceControl.Builder setHidden(boolean);
method @NonNull public android.view.SurfaceControl.Builder setName(@NonNull String);
method @NonNull public android.view.SurfaceControl.Builder setOpaque(boolean);
method @NonNull public android.view.SurfaceControl.Builder setParent(@Nullable android.view.SurfaceControl);
@@ -49011,11 +49062,18 @@
method @NonNull public android.view.SurfaceControl.Transaction merge(@NonNull android.view.SurfaceControl.Transaction);
method @NonNull public android.view.SurfaceControl.Transaction reparent(@NonNull android.view.SurfaceControl, @Nullable android.view.SurfaceControl);
method @NonNull public android.view.SurfaceControl.Transaction setAlpha(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0, to=1.0) float);
+ method @NonNull public android.view.SurfaceControl.Transaction setBuffer(@NonNull android.view.SurfaceControl, @Nullable android.hardware.HardwareBuffer);
method @NonNull public android.view.SurfaceControl.Transaction setBufferSize(@NonNull android.view.SurfaceControl, @IntRange(from=0) int, @IntRange(from=0) int);
+ method @NonNull public android.view.SurfaceControl.Transaction setBufferTransform(@NonNull android.view.SurfaceControl, int);
+ method @NonNull public android.view.SurfaceControl.Transaction setCrop(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect);
+ method @NonNull public android.view.SurfaceControl.Transaction setDamageRegion(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Region);
method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int);
method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int, int);
method @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int);
method @NonNull public android.view.SurfaceControl.Transaction setLayer(@NonNull android.view.SurfaceControl, @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int);
+ method @NonNull public android.view.SurfaceControl.Transaction setOpaque(@NonNull android.view.SurfaceControl, boolean);
+ method @NonNull public android.view.SurfaceControl.Transaction setPosition(@NonNull android.view.SurfaceControl, float, float);
+ method @NonNull public android.view.SurfaceControl.Transaction setScale(@NonNull android.view.SurfaceControl, float, float);
method @NonNull public android.view.SurfaceControl.Transaction setVisibility(@NonNull android.view.SurfaceControl, boolean);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.SurfaceControl.Transaction> CREATOR;
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index b8ce02e7..7756906 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -215,6 +215,10 @@
package android.net {
+ public final class ConnectivityFrameworkInitializerTiramisu {
+ method public static void registerServiceWrappers();
+ }
+
public final class EthernetNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
ctor public EthernetNetworkSpecifier(@NonNull String);
method public int describeContents();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index dd951b4..a33d0a2 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -70,6 +70,7 @@
field public static final String BIND_TRANSLATION_SERVICE = "android.permission.BIND_TRANSLATION_SERVICE";
field public static final String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT";
field public static final String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
+ field public static final String BLUETOOTH_MAP = "android.permission.BLUETOOTH_MAP";
field public static final String BRICK = "android.permission.BRICK";
field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
field public static final String BROADCAST_CLOSE_SYSTEM_DIALOGS = "android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS";
@@ -219,11 +220,13 @@
field public static final String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
field public static final String QUERY_ADMIN_POLICY = "android.permission.QUERY_ADMIN_POLICY";
field public static final String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES";
+ field public static final String QUERY_USERS = "android.permission.QUERY_USERS";
field public static final String RADIO_SCAN_WITHOUT_LOCATION = "android.permission.RADIO_SCAN_WITHOUT_LOCATION";
field public static final String READ_ACTIVE_EMERGENCY_SESSION = "android.permission.READ_ACTIVE_EMERGENCY_SESSION";
field public static final String READ_APP_SPECIFIC_LOCALES = "android.permission.READ_APP_SPECIFIC_LOCALES";
field public static final String READ_CARRIER_APP_INFO = "android.permission.READ_CARRIER_APP_INFO";
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
+ field public static final String READ_COMMUNAL_STATE = "android.permission.READ_COMMUNAL_STATE";
field public static final String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS";
field public static final String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG";
field public static final String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE";
@@ -393,6 +396,7 @@
field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
field public static final int config_systemActivityRecognizer = 17039416; // 0x1040038
field public static final int config_systemAmbientAudioIntelligence = 17039411; // 0x1040033
+ field public static final int config_systemAppProtectionService;
field public static final int config_systemAudioIntelligence = 17039412; // 0x1040034
field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029
@@ -1309,6 +1313,20 @@
}
+package android.app.communal {
+
+ public final class CommunalManager {
+ method @RequiresPermission(android.Manifest.permission.READ_COMMUNAL_STATE) public void addCommunalModeListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.communal.CommunalManager.CommunalModeListener);
+ method @RequiresPermission(android.Manifest.permission.READ_COMMUNAL_STATE) public boolean isCommunalMode();
+ method @RequiresPermission(android.Manifest.permission.READ_COMMUNAL_STATE) public void removeCommunalModeListener(@NonNull android.app.communal.CommunalManager.CommunalModeListener);
+ }
+
+ @java.lang.FunctionalInterface public static interface CommunalManager.CommunalModeListener {
+ method public void onCommunalModeChanged(boolean);
+ }
+
+}
+
package android.app.compat {
public final class CompatChanges {
@@ -2411,6 +2429,8 @@
method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean canPairWithoutPrompt(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @NonNull @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public java.util.List<android.companion.AssociationInfo> getAllAssociations();
method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean isDeviceAssociatedForWifiConnection(@NonNull String, @NonNull android.net.MacAddress, @NonNull android.os.UserHandle);
+ method @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public void notifyDeviceAppeared(int);
+ method @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public void notifyDeviceDisappeared(int);
method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void removeOnAssociationsChangedListener(@NonNull android.companion.CompanionDeviceManager.OnAssociationsChangedListener);
}
@@ -2491,6 +2511,7 @@
field public static final String BATTERY_STATS_SERVICE = "batterystats";
field @Deprecated public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000
field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000
+ field public static final String COMMUNAL_SERVICE = "communal";
field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
field public static final String CONTEXTHUB_SERVICE = "contexthub";
field public static final String ETHERNET_SERVICE = "ethernet";
@@ -2879,13 +2900,16 @@
method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void addOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
method public abstract boolean arePermissionsIndividuallyControlled();
method @NonNull public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(@NonNull String);
- method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.pm.ApplicationInfo getApplicationInfoAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.pm.ApplicationInfo getApplicationInfoAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.pm.ApplicationInfo getApplicationInfoAsUser(@NonNull String, @NonNull android.content.pm.PackageManager.ApplicationInfoFlags, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public android.content.pm.dex.ArtManager getArtManager();
- method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_SHARED_LIBRARIES) public java.util.List<android.content.pm.SharedLibraryInfo> getDeclaredSharedLibraries(@NonNull String, int);
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_SHARED_LIBRARIES) public java.util.List<android.content.pm.SharedLibraryInfo> getDeclaredSharedLibraries(@NonNull String, int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_SHARED_LIBRARIES) public java.util.List<android.content.pm.SharedLibraryInfo> getDeclaredSharedLibraries(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags);
method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract String getDefaultBrowserPackageNameAsUser(int);
method @Nullable @RequiresPermission(android.Manifest.permission.SET_HARMFUL_APP_WARNINGS) public CharSequence getHarmfulAppWarning(@NonNull String);
method @Nullable public String getIncidentReportApproverPackageName();
- method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(@NonNull android.content.pm.PackageManager.PackageInfoFlags, int);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_INSTANT_APPS) public abstract android.graphics.drawable.Drawable getInstantAppIcon(String);
method @Nullable public abstract android.content.ComponentName getInstantAppInstallerComponent();
method @Nullable public abstract android.content.ComponentName getInstantAppResolverSettingsComponent();
@@ -2897,10 +2921,14 @@
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @Deprecated public abstract int installExistingPackage(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated public abstract int installExistingPackage(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, int, android.os.UserHandle);
- method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
- method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProvidersAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
- method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentServicesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, int, android.os.UserHandle);
+ method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags, @NonNull android.os.UserHandle);
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
+ method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags, @NonNull android.os.UserHandle);
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProvidersAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
+ method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProvidersAsUser(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags, @NonNull android.os.UserHandle);
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentServicesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
+ method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentServicesAsUser(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags, @NonNull android.os.UserHandle);
method public abstract void registerDexModule(@NonNull String, @Nullable android.content.pm.PackageManager.DexModuleRegisterCallback);
method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void removeOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
method public void replacePreferredActivity(@NonNull android.content.IntentFilter, int, @NonNull java.util.List<android.content.ComponentName>, @NonNull android.content.ComponentName);
@@ -3872,6 +3900,127 @@
}
+package android.hardware.input {
+
+ public final class VirtualKeyEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getAction();
+ method public int getKeyCode();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int ACTION_DOWN = 0; // 0x0
+ field public static final int ACTION_UP = 1; // 0x1
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualKeyEvent> CREATOR;
+ }
+
+ public static final class VirtualKeyEvent.Builder {
+ ctor public VirtualKeyEvent.Builder();
+ method @NonNull public android.hardware.input.VirtualKeyEvent build();
+ method @NonNull public android.hardware.input.VirtualKeyEvent.Builder setAction(int);
+ method @NonNull public android.hardware.input.VirtualKeyEvent.Builder setKeyCode(int);
+ }
+
+ public class VirtualKeyboard implements java.io.Closeable {
+ method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
+ method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendKeyEvent(@NonNull android.hardware.input.VirtualKeyEvent);
+ }
+
+ public class VirtualMouse implements java.io.Closeable {
+ method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
+ method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendButtonEvent(@NonNull android.hardware.input.VirtualMouseButtonEvent);
+ method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendRelativeEvent(@NonNull android.hardware.input.VirtualMouseRelativeEvent);
+ method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendScrollEvent(@NonNull android.hardware.input.VirtualMouseScrollEvent);
+ }
+
+ public final class VirtualMouseButtonEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getAction();
+ method public int getButtonCode();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int ACTION_BUTTON_PRESS = 11; // 0xb
+ field public static final int ACTION_BUTTON_RELEASE = 12; // 0xc
+ field public static final int BUTTON_BACK = 8; // 0x8
+ field public static final int BUTTON_FORWARD = 16; // 0x10
+ field public static final int BUTTON_PRIMARY = 1; // 0x1
+ field public static final int BUTTON_SECONDARY = 2; // 0x2
+ field public static final int BUTTON_TERTIARY = 4; // 0x4
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualMouseButtonEvent> CREATOR;
+ }
+
+ public static final class VirtualMouseButtonEvent.Builder {
+ ctor public VirtualMouseButtonEvent.Builder();
+ method @NonNull public android.hardware.input.VirtualMouseButtonEvent build();
+ method @NonNull public android.hardware.input.VirtualMouseButtonEvent.Builder setAction(int);
+ method @NonNull public android.hardware.input.VirtualMouseButtonEvent.Builder setButtonCode(int);
+ }
+
+ public final class VirtualMouseRelativeEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method public float getRelativeX();
+ method public float getRelativeY();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualMouseRelativeEvent> CREATOR;
+ }
+
+ public static final class VirtualMouseRelativeEvent.Builder {
+ ctor public VirtualMouseRelativeEvent.Builder();
+ method @NonNull public android.hardware.input.VirtualMouseRelativeEvent build();
+ method @NonNull public android.hardware.input.VirtualMouseRelativeEvent.Builder setRelativeX(float);
+ method @NonNull public android.hardware.input.VirtualMouseRelativeEvent.Builder setRelativeY(float);
+ }
+
+ public final class VirtualMouseScrollEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method public float getXAxisMovement();
+ method public float getYAxisMovement();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualMouseScrollEvent> CREATOR;
+ }
+
+ public static final class VirtualMouseScrollEvent.Builder {
+ ctor public VirtualMouseScrollEvent.Builder();
+ method @NonNull public android.hardware.input.VirtualMouseScrollEvent build();
+ method @NonNull public android.hardware.input.VirtualMouseScrollEvent.Builder setXAxisMovement(@FloatRange(from=-1.0F, to=1.0f) float);
+ method @NonNull public android.hardware.input.VirtualMouseScrollEvent.Builder setYAxisMovement(@FloatRange(from=-1.0F, to=1.0f) float);
+ }
+
+ public final class VirtualTouchEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getAction();
+ method public float getMajorAxisSize();
+ method public int getPointerId();
+ method public float getPressure();
+ method public int getToolType();
+ method public float getX();
+ method public float getY();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int ACTION_CANCEL = 3; // 0x3
+ field public static final int ACTION_DOWN = 0; // 0x0
+ field public static final int ACTION_MOVE = 2; // 0x2
+ field public static final int ACTION_UP = 1; // 0x1
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualTouchEvent> CREATOR;
+ field public static final int TOOL_TYPE_FINGER = 1; // 0x1
+ field public static final int TOOL_TYPE_PALM = 5; // 0x5
+ }
+
+ public static final class VirtualTouchEvent.Builder {
+ ctor public VirtualTouchEvent.Builder();
+ method @NonNull public android.hardware.input.VirtualTouchEvent build();
+ method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setAction(int);
+ method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setMajorAxisSize(@FloatRange(from=0.0f) float);
+ method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setPointerId(int);
+ method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setPressure(@FloatRange(from=0.0f) float);
+ method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setToolType(int);
+ method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setX(float);
+ method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setY(float);
+ }
+
+ public class VirtualTouchscreen implements java.io.Closeable {
+ method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
+ method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendTouchEvent(@NonNull android.hardware.input.VirtualTouchEvent);
+ }
+
+}
+
package android.hardware.lights {
public final class LightState implements android.os.Parcelable {
@@ -3893,6 +4042,7 @@
public class ContextHubClient implements java.io.Closeable {
method public void close();
method @NonNull public android.hardware.location.ContextHubInfo getAttachedHub();
+ method public int getId();
method @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int sendMessageToNanoApp(@NonNull android.hardware.location.NanoAppMessage);
}
@@ -5726,9 +5876,11 @@
method public android.media.AudioTrack createAudioTrackSource(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException;
method public int detachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>);
method public int getFocusDuckingBehavior();
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioFocusInfo> getFocusStack();
method public int getStatus();
method public boolean removeUidDeviceAffinity(int);
method public boolean removeUserIdDeviceAffinity(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean sendFocusLoss(@NonNull android.media.AudioFocusInfo) throws java.lang.IllegalStateException;
method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setRegistration(String);
method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
@@ -6298,6 +6450,7 @@
method public int flush();
method public long read(long);
method public long read(@NonNull byte[], long, long);
+ method public long seek(long);
method public void setFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
method public int start();
method public int stop();
@@ -6433,6 +6586,7 @@
public class DownloadEvent extends android.media.tv.tuner.filter.FilterEvent {
method public int getDataLength();
+ method public int getDownloadId();
method public int getItemFragmentIndex();
method public int getItemId();
method public int getLastItemFragmentIndex();
@@ -6442,11 +6596,13 @@
public class DownloadSettings extends android.media.tv.tuner.filter.Settings {
method @NonNull public static android.media.tv.tuner.filter.DownloadSettings.Builder builder(int);
method public int getDownloadId();
+ method public boolean useDownloadId();
}
public static class DownloadSettings.Builder {
method @NonNull public android.media.tv.tuner.filter.DownloadSettings build();
method @NonNull public android.media.tv.tuner.filter.DownloadSettings.Builder setDownloadId(int);
+ method @NonNull public android.media.tv.tuner.filter.DownloadSettings.Builder setUseDownloadId(boolean);
}
public class Filter implements java.lang.AutoCloseable {
@@ -6545,12 +6701,14 @@
method public long getAudioHandle();
method public long getAvDataId();
method public long getDataLength();
+ method public long getDts();
method @Nullable public android.media.tv.tuner.filter.AudioDescriptor getExtraMetaData();
method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock();
method @IntRange(from=0) public int getMpuSequenceNumber();
method public long getOffset();
method public long getPts();
method public int getStreamId();
+ method public boolean isDtsPresent();
method public boolean isPrivateData();
method public boolean isPtsPresent();
method public boolean isSecureMemory();
@@ -6660,7 +6818,8 @@
}
public class SectionEvent extends android.media.tv.tuner.filter.FilterEvent {
- method public int getDataLength();
+ method @Deprecated public int getDataLength();
+ method public long getDataLengthLong();
method public int getSectionNumber();
method public int getTableId();
method public int getVersion();
@@ -7392,6 +7551,7 @@
method public int getSignalStrength();
method public int getSnr();
method public int getSpectralInversion();
+ method @NonNull public int[] getStreamIdList();
method public int getSymbolRate();
method @IntRange(from=0, to=65535) public int getSystemId();
method public int getTransmissionMode();
@@ -7438,6 +7598,7 @@
field public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH = 6; // 0x6
field public static final int FRONTEND_STATUS_TYPE_SNR = 1; // 0x1
field public static final int FRONTEND_STATUS_TYPE_SPECTRAL = 10; // 0xa
+ field public static final int FRONTEND_STATUS_TYPE_STREAM_ID_LIST = 39; // 0x27
field public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE = 7; // 0x7
field public static final int FRONTEND_STATUS_TYPE_T2_SYSTEM_ID = 29; // 0x1d
field public static final int FRONTEND_STATUS_TYPE_TRANSMISSION_MODE = 27; // 0x1b
@@ -9034,31 +9195,31 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void clearSeedAccountData();
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle createProfile(@NonNull String, @NonNull String, @NonNull java.util.Set<java.lang.String>) throws android.os.UserManager.UserOperationException;
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.NewUserResponse createUser(@NonNull android.os.NewUserRequest);
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getAllProfiles();
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getEnabledProfiles();
+ method @NonNull public java.util.List<android.os.UserHandle> getAllProfiles();
+ method @NonNull public java.util.List<android.os.UserHandle> getEnabledProfiles();
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle);
- method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle getRestrictedProfileParent();
+ method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public android.os.UserHandle getRestrictedProfileParent();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountName();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountType();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public long[] getSerialNumbersOfUsers(boolean);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.os.UserHandle> getUserHandles(boolean);
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public android.graphics.Bitmap getUserIcon();
- method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserRestrictionSource(String, android.os.UserHandle);
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle);
+ method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public int getUserRestrictionSource(String, android.os.UserHandle);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle);
method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public int getUserSwitchability();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean hasRestrictedProfiles();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean hasUserRestrictionForUser(@NonNull String, @NonNull android.os.UserHandle);
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isAdminUser();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isAdminUser();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isCloneProfile();
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isGuestUser();
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isManagedProfile(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isGuestUser();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isManagedProfile(int);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isMediaSharedWithParent();
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isPrimaryUser();
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isProfile();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isPrimaryUser();
+ method public boolean isProfile();
method public boolean isRestrictedProfile();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public boolean isRestrictedProfile(@NonNull android.os.UserHandle);
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isSameProfileGroup(@NonNull android.os.UserHandle, @NonNull android.os.UserHandle);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isSameProfileGroup(@NonNull android.os.UserHandle, @NonNull android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public boolean isUserNameSet();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isUserOfType(@NonNull String);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isUserUnlockingOrUnlocked(@NonNull android.os.UserHandle);
@@ -9551,6 +9712,7 @@
field public static final String NAMESPACE_TELEPHONY = "telephony";
field public static final String NAMESPACE_TETHERING = "tethering";
field public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
+ field public static final String NAMESPACE_UWB = "uwb";
field public static final String NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT = "window_manager_native_boot";
}
@@ -13884,12 +14046,14 @@
}
public final class RcsClientConfiguration implements android.os.Parcelable {
- ctor public RcsClientConfiguration(@NonNull String, @NonNull String, @NonNull String, @NonNull String);
+ ctor @Deprecated public RcsClientConfiguration(@NonNull String, @NonNull String, @NonNull String, @NonNull String);
+ ctor public RcsClientConfiguration(@NonNull String, @NonNull String, @NonNull String, @NonNull String, boolean);
method public int describeContents();
method @NonNull public String getClientVendor();
method @NonNull public String getClientVersion();
method @NonNull public String getRcsProfile();
method @NonNull public String getRcsVersion();
+ method public boolean isRcsEnabledByUser();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsClientConfiguration> CREATOR;
field public static final String RCS_PROFILE_1_0 = "UP_1.0";
@@ -14026,6 +14190,7 @@
field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2
field public static final int PUBLISH_STATE_OK = 1; // 0x1
field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6
+ field public static final int PUBLISH_STATE_PUBLISHING = 7; // 0x7
field public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; // 0x4
field public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; // 0x5
field public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3; // 0x3
@@ -14288,6 +14453,7 @@
package android.telephony.ims.stub {
public interface CapabilityExchangeEventListener {
+ method public default void onPublishUpdated(int, @NonNull String, int, @NonNull String) throws android.telephony.ims.ImsException;
method public void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.Set<java.lang.String>, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener.OptionsRequestCallback) throws android.telephony.ims.ImsException;
method public void onRequestPublishCapabilities(int) throws android.telephony.ims.ImsException;
method public void onUnpublish() throws android.telephony.ims.ImsException;
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index aa791aa..a921500 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -42,6 +42,7 @@
field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
+ field public static final String WRITE_COMMUNAL_STATE = "android.permission.WRITE_COMMUNAL_STATE";
field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
field @Deprecated public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
@@ -608,6 +609,14 @@
}
+package android.app.communal {
+
+ public final class CommunalManager {
+ method @RequiresPermission(android.Manifest.permission.WRITE_COMMUNAL_STATE) public void setCommunalViewShowing(boolean);
+ }
+
+}
+
package android.app.contentsuggestions {
public final class ContentSuggestionsManager {
@@ -676,6 +685,14 @@
}
+package android.companion {
+
+ public abstract class CompanionDeviceService extends android.app.Service {
+ method public void onBindCompanionDeviceService(@NonNull android.content.Intent);
+ }
+
+}
+
package android.content {
public final class AttributionSource implements android.os.Parcelable {
@@ -809,7 +826,8 @@
method @Nullable public String getDefaultTextClassifierPackageName();
method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public android.os.IBinder getHoldLockToken();
method public abstract int getInstallReason(@NonNull String, @NonNull android.os.UserHandle);
- method @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
+ method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
+ method @NonNull public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(@NonNull android.content.pm.PackageManager.ApplicationInfoFlags, int);
method @Nullable public abstract String[] getNamesForUids(int[]);
method @NonNull public String getPermissionControllerPackageName();
method @NonNull public abstract String getServicesSystemSharedLibraryPackageName();
@@ -819,6 +837,7 @@
method public void holdLock(android.os.IBinder, int);
method @RequiresPermission(android.Manifest.permission.KEEP_UNINSTALLED_PACKAGES) public void setKeepUninstalledPackages(@NonNull java.util.List<java.lang.String>);
field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
+ field public static final String FEATURE_COMMUNAL_MODE = "android.software.communal_mode";
field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
field public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec";
field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80
@@ -1807,7 +1826,7 @@
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createRestrictedProfile(@Nullable String);
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createUser(@Nullable String, @NonNull String, int);
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.Set<java.lang.String> getPreInstallableSystemPackages(@NonNull String);
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public String getUserType();
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public String getUserType();
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
method public static boolean isGuestUserEphemeral();
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index ad62872..3573a56 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -521,7 +521,9 @@
public static final int GLOBAL_ACTION_POWER_DIALOG = 6;
/**
- * Action to toggle docking the current app's window
+ * Action to toggle docking the current app's window.
+ * <p>
+ * <strong>Note:</strong> It is effective only if it appears in {@link #getSystemActions()}.
*/
public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7;
@@ -1392,6 +1394,12 @@
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will
* return a default value of {@code 1.0f}.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> This legacy API gets the scale of full-screen
+ * magnification. To get the scale of the current controlling magnifier,
+ * use {@link #getMagnificationConfig} instead.
+ * </p>
*
* @return the current magnification scale
*/
@@ -1420,6 +1428,12 @@
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will
* return a default value of {@code 0.0f}.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> This legacy API gets the center position of full-screen
+ * magnification. To get the magnification center of the current controlling magnifier,
+ * use {@link #getMagnificationConfig} instead.
+ * </p>
*
* @return the unscaled screen-relative X coordinate of the center of
* the magnified region
@@ -1449,6 +1463,12 @@
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will
* return a default value of {@code 0.0f}.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> This legacy API gets the center position of full-screen
+ * magnification. To get the magnification center of the current controlling magnifier,
+ * use {@link #getMagnificationConfig} instead.
+ * </p>
*
* @return the unscaled screen-relative Y coordinate of the center of
* the magnified region
@@ -1569,6 +1589,11 @@
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will have
* no effect and return {@code false}.
+ * <p>
+ * <strong>Note:</strong> This legacy API sets the scale of full-screen
+ * magnification. To set the scale of the specified magnifier,
+ * use {@link #setMagnificationConfig} instead.
+ * </p>
*
* @param scale the magnification scale to set, must be >= 1 and <= 8
* @param animate {@code true} to animate from the current scale or
@@ -1600,6 +1625,12 @@
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will have
* no effect and return {@code false}.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> This legacy API sets the center of full-screen
+ * magnification. To set the center of the specified magnifier,
+ * use {@link #setMagnificationConfig} instead.
+ * </p>
*
* @param centerX the unscaled screen-relative X coordinate on which to
* center the viewport
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 2c41e8d..3cbae99 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -23,6 +23,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Looper;
+import android.os.SystemProperties;
import android.os.Trace;
import android.util.AndroidRuntimeException;
import android.util.Log;
@@ -74,6 +75,8 @@
public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback {
private static final String TAG = "ValueAnimator";
private static final boolean DEBUG = false;
+ private static final boolean TRACE_ANIMATION_FRACTION = SystemProperties.getBoolean(
+ "persist.debug.animator.trace_fraction", false);
/**
* Internal constants
@@ -1554,6 +1557,10 @@
@CallSuper
@UnsupportedAppUsage
void animateValue(float fraction) {
+ if (TRACE_ANIMATION_FRACTION) {
+ Trace.traceCounter(Trace.TRACE_TAG_VIEW, getNameForTrace() + hashCode(),
+ (int) (fraction * 1000));
+ }
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
int numValues = mValues.length;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 15f67d0..c0a8c1e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -38,10 +38,8 @@
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
-import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UptimeMillisLong;
import android.app.RemoteServiceException.BadForegroundServiceNotificationException;
import android.app.RemoteServiceException.CannotDeliverBroadcastException;
import android.app.RemoteServiceException.CannotPostForegroundServiceNotificationException;
@@ -323,7 +321,8 @@
@UnsupportedAppUsage
private ContextImpl mSystemContext;
- private final SparseArray<ContextImpl> mDisplaySystemUiContexts = new SparseArray<>();
+ @GuardedBy("this")
+ private SparseArray<ContextImpl> mDisplaySystemUiContexts;
@UnsupportedAppUsage
static volatile IPackageManager sPackageManager;
@@ -2547,9 +2546,18 @@
if (packageInfo != null) {
if (!isLoadedApkResourceDirsUpToDate(packageInfo, aInfo)) {
- List<String> oldPaths = new ArrayList<>();
- LoadedApk.makePaths(this, aInfo, oldPaths);
- packageInfo.updateApplicationInfo(aInfo, oldPaths);
+ if (packageInfo.getApplicationInfo().createTimestamp > aInfo.createTimestamp) {
+ // The cached loaded apk is newer than the one passed in, we should not
+ // update the cached version
+ Slog.w(TAG, "getPackageInfo() called with an older ApplicationInfo "
+ + "than the cached version for package " + aInfo.packageName);
+ } else {
+ Slog.v(TAG, "getPackageInfo() caused update to cached ApplicationInfo "
+ + "for package " + aInfo.packageName);
+ List<String> oldPaths = new ArrayList<>();
+ LoadedApk.makePaths(this, aInfo, oldPaths);
+ packageInfo.updateApplicationInfo(aInfo, oldPaths);
+ }
}
return packageInfo;
@@ -2658,7 +2666,6 @@
}
}
- @Override
@NonNull
public ContextImpl getSystemUiContext() {
return getSystemUiContext(DEFAULT_DISPLAY);
@@ -2672,6 +2679,9 @@
@NonNull
public ContextImpl getSystemUiContext(int displayId) {
synchronized (this) {
+ if (mDisplaySystemUiContexts == null) {
+ mDisplaySystemUiContexts = new SparseArray<>();
+ }
ContextImpl systemUiContext = mDisplaySystemUiContexts.get(displayId);
if (systemUiContext == null) {
systemUiContext = ContextImpl.createSystemUiContext(getSystemContext(), displayId);
@@ -2681,6 +2691,25 @@
}
}
+ @Nullable
+ @Override
+ public ContextImpl getSystemUiContextNoCreate() {
+ synchronized (this) {
+ if (mDisplaySystemUiContexts == null) return null;
+ return mDisplaySystemUiContexts.get(DEFAULT_DISPLAY);
+ }
+ }
+
+ void onSystemUiContextCleanup(ContextImpl context) {
+ synchronized (this) {
+ if (mDisplaySystemUiContexts == null) return;
+ final int index = mDisplaySystemUiContexts.indexOfValue(context);
+ if (index >= 0) {
+ mDisplaySystemUiContexts.removeAt(index);
+ }
+ }
+ }
+
public void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
synchronized (this) {
getSystemContext().installSystemApplicationInfo(info, classLoader);
@@ -4074,12 +4103,12 @@
}
private void handleStartBinderTracking() {
- Binder.enableTracing();
+ Binder.enableStackTracking();
}
private void handleStopBinderTrackingAndDump(ParcelFileDescriptor fd) {
try {
- Binder.disableTracing();
+ Binder.disableStackTracking();
Binder.getTransactionTracker().writeTracesToFile(fd);
} finally {
IoUtils.closeQuietly(fd);
@@ -6618,7 +6647,7 @@
boolean isAppProfileable = isAppDebuggable || data.appInfo.isProfileable();
Trace.setAppTracingAllowed(isAppProfileable);
if ((isAppProfileable || Build.IS_DEBUGGABLE) && data.enableBinderTracking) {
- Binder.enableTracing();
+ Binder.enableStackTracking();
}
// Initialize heap profiling.
diff --git a/core/java/android/app/ActivityThreadInternal.java b/core/java/android/app/ActivityThreadInternal.java
index bc698f6..b9ad5c3 100644
--- a/core/java/android/app/ActivityThreadInternal.java
+++ b/core/java/android/app/ActivityThreadInternal.java
@@ -28,7 +28,7 @@
interface ActivityThreadInternal {
ContextImpl getSystemContext();
- ContextImpl getSystemUiContext();
+ ContextImpl getSystemUiContextNoCreate();
boolean isInDensityCompatMode();
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index d1b7145..44fb5db 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -210,16 +210,28 @@
@Override
public PackageInfo getPackageInfo(String packageName, int flags)
throws NameNotFoundException {
+ return getPackageInfo(packageName, PackageInfoFlags.of(flags));
+ }
+
+ @Override
+ public PackageInfo getPackageInfo(String packageName, PackageInfoFlags flags)
+ throws NameNotFoundException {
return getPackageInfoAsUser(packageName, flags, getUserId());
}
@Override
public PackageInfo getPackageInfo(VersionedPackage versionedPackage, int flags)
throws NameNotFoundException {
+ return getPackageInfo(versionedPackage, PackageInfoFlags.of(flags));
+ }
+
+ @Override
+ public PackageInfo getPackageInfo(VersionedPackage versionedPackage, PackageInfoFlags flags)
+ throws NameNotFoundException {
final int userId = getUserId();
try {
PackageInfo pi = mPM.getPackageInfoVersioned(versionedPackage,
- updateFlagsForPackage(flags, userId), userId);
+ updateFlagsForPackage(flags.getValue(), userId), userId);
if (pi != null) {
return pi;
}
@@ -232,10 +244,16 @@
@Override
public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
throws NameNotFoundException {
+ return getPackageInfoAsUser(packageName, PackageInfoFlags.of(flags), userId);
+ }
+
+ @Override
+ public PackageInfo getPackageInfoAsUser(String packageName, PackageInfoFlags flags, int userId)
+ throws NameNotFoundException {
PackageInfo pi =
getPackageInfoAsUserCached(
packageName,
- updateFlagsForPackage(flags, userId),
+ updateFlagsForPackage(flags.getValue(), userId),
userId);
if (pi == null) {
throw new NameNotFoundException(packageName);
@@ -334,10 +352,16 @@
@Override
public int[] getPackageGids(String packageName, int flags)
throws NameNotFoundException {
+ return getPackageGids(packageName, PackageInfoFlags.of(flags));
+ }
+
+ @Override
+ public int[] getPackageGids(String packageName, PackageInfoFlags flags)
+ throws NameNotFoundException {
final int userId = getUserId();
try {
int[] gids = mPM.getPackageGids(packageName,
- updateFlagsForPackage(flags, userId), userId);
+ updateFlagsForPackage(flags.getValue(), userId), userId);
if (gids != null) {
return gids;
}
@@ -350,6 +374,12 @@
@Override
public int getPackageUid(String packageName, int flags) throws NameNotFoundException {
+ return getPackageUid(packageName, PackageInfoFlags.of(flags));
+ }
+
+ @Override
+ public int getPackageUid(String packageName, PackageInfoFlags flags)
+ throws NameNotFoundException {
return getPackageUidAsUser(packageName, flags, getUserId());
}
@@ -361,9 +391,15 @@
@Override
public int getPackageUidAsUser(String packageName, int flags, int userId)
throws NameNotFoundException {
+ return getPackageUidAsUser(packageName, PackageInfoFlags.of(flags), userId);
+ }
+
+ @Override
+ public int getPackageUidAsUser(String packageName, PackageInfoFlags flags, int userId)
+ throws NameNotFoundException {
try {
int uid = mPM.getPackageUid(packageName,
- updateFlagsForPackage(flags, userId), userId);
+ updateFlagsForPackage(flags.getValue(), userId), userId);
if (uid >= 0) {
return uid;
}
@@ -448,15 +484,27 @@
@Override
public ApplicationInfo getApplicationInfo(String packageName, int flags)
throws NameNotFoundException {
+ return getApplicationInfo(packageName, ApplicationInfoFlags.of(flags));
+ }
+
+ @Override
+ public ApplicationInfo getApplicationInfo(String packageName, ApplicationInfoFlags flags)
+ throws NameNotFoundException {
return getApplicationInfoAsUser(packageName, flags, getUserId());
}
@Override
public ApplicationInfo getApplicationInfoAsUser(String packageName, int flags, int userId)
throws NameNotFoundException {
+ return getApplicationInfoAsUser(packageName, ApplicationInfoFlags.of(flags), userId);
+ }
+
+ @Override
+ public ApplicationInfo getApplicationInfoAsUser(String packageName, ApplicationInfoFlags flags,
+ int userId) throws NameNotFoundException {
ApplicationInfo ai = getApplicationInfoAsUserCached(
packageName,
- updateFlagsForApplication(flags, userId),
+ updateFlagsForApplication(flags.getValue(), userId),
userId);
if (ai == null) {
throw new NameNotFoundException(packageName);
@@ -505,10 +553,16 @@
@Override
public ActivityInfo getActivityInfo(ComponentName className, int flags)
throws NameNotFoundException {
+ return getActivityInfo(className, ComponentInfoFlags.of(flags));
+ }
+
+ @Override
+ public ActivityInfo getActivityInfo(ComponentName className, ComponentInfoFlags flags)
+ throws NameNotFoundException {
final int userId = getUserId();
try {
ActivityInfo ai = mPM.getActivityInfo(className,
- updateFlagsForComponent(flags, userId, null), userId);
+ updateFlagsForComponent(flags.getValue(), userId, null), userId);
if (ai != null) {
return ai;
}
@@ -522,10 +576,16 @@
@Override
public ActivityInfo getReceiverInfo(ComponentName className, int flags)
throws NameNotFoundException {
+ return getReceiverInfo(className, ComponentInfoFlags.of(flags));
+ }
+
+ @Override
+ public ActivityInfo getReceiverInfo(ComponentName className, ComponentInfoFlags flags)
+ throws NameNotFoundException {
final int userId = getUserId();
try {
ActivityInfo ai = mPM.getReceiverInfo(className,
- updateFlagsForComponent(flags, userId, null), userId);
+ updateFlagsForComponent(flags.getValue(), userId, null), userId);
if (ai != null) {
return ai;
}
@@ -539,10 +599,16 @@
@Override
public ServiceInfo getServiceInfo(ComponentName className, int flags)
throws NameNotFoundException {
+ return getServiceInfo(className, ComponentInfoFlags.of(flags));
+ }
+
+ @Override
+ public ServiceInfo getServiceInfo(ComponentName className, ComponentInfoFlags flags)
+ throws NameNotFoundException {
final int userId = getUserId();
try {
ServiceInfo si = mPM.getServiceInfo(className,
- updateFlagsForComponent(flags, userId, null), userId);
+ updateFlagsForComponent(flags.getValue(), userId, null), userId);
if (si != null) {
return si;
}
@@ -556,10 +622,16 @@
@Override
public ProviderInfo getProviderInfo(ComponentName className, int flags)
throws NameNotFoundException {
+ return getProviderInfo(className, ComponentInfoFlags.of(flags));
+ }
+
+ @Override
+ public ProviderInfo getProviderInfo(ComponentName className, ComponentInfoFlags flags)
+ throws NameNotFoundException {
final int userId = getUserId();
try {
ProviderInfo pi = mPM.getProviderInfo(className,
- updateFlagsForComponent(flags, userId, null), userId);
+ updateFlagsForComponent(flags.getValue(), userId, null), userId);
if (pi != null) {
return pi;
}
@@ -582,16 +654,30 @@
/** @hide */
@Override
public @NonNull List<SharedLibraryInfo> getSharedLibraries(int flags) {
+ return this.getSharedLibraries(PackageInfoFlags.of(flags));
+ }
+
+ /** @hide
+ * @param flags */
+ @Override
+ public @NonNull List<SharedLibraryInfo> getSharedLibraries(PackageInfoFlags flags) {
return getSharedLibrariesAsUser(flags, getUserId());
}
/** @hide */
@Override
- @SuppressWarnings("unchecked")
public @NonNull List<SharedLibraryInfo> getSharedLibrariesAsUser(int flags, int userId) {
+ return getSharedLibrariesAsUser(PackageInfoFlags.of(flags), userId);
+ }
+
+ /** @hide */
+ @Override
+ @SuppressWarnings("unchecked")
+ public @NonNull List<SharedLibraryInfo> getSharedLibrariesAsUser(PackageInfoFlags flags,
+ int userId) {
try {
ParceledListSlice<SharedLibraryInfo> sharedLibs = mPM.getSharedLibraries(
- mContext.getOpPackageName(), flags, userId);
+ mContext.getOpPackageName(), flags.getValue(), userId);
if (sharedLibs == null) {
return Collections.emptyList();
}
@@ -604,10 +690,18 @@
@NonNull
@Override
public List<SharedLibraryInfo> getDeclaredSharedLibraries(@NonNull String packageName,
- @InstallFlags int flags) {
+ int flags) {
+ return getDeclaredSharedLibraries(packageName, PackageInfoFlags.of(flags));
+ }
+
+ @NonNull
+ @Override
+ @SuppressWarnings("unchecked")
+ public List<SharedLibraryInfo> getDeclaredSharedLibraries(@NonNull String packageName,
+ PackageInfoFlags flags) {
try {
ParceledListSlice<SharedLibraryInfo> sharedLibraries = mPM.getDeclaredSharedLibraries(
- packageName, flags, mContext.getUserId());
+ packageName, flags.getValue(), mContext.getUserId());
return sharedLibraries != null ? sharedLibraries.getList() : Collections.emptyList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1092,6 +1186,12 @@
@SuppressWarnings("unchecked")
@Override
public List<PackageInfo> getInstalledPackages(int flags) {
+ return getInstalledPackages(PackageInfoFlags.of(flags));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public List<PackageInfo> getInstalledPackages(PackageInfoFlags flags) {
return getInstalledPackagesAsUser(flags, getUserId());
}
@@ -1099,9 +1199,17 @@
@Override
@SuppressWarnings("unchecked")
public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
+ return getInstalledPackagesAsUser(PackageInfoFlags.of(flags), userId);
+ }
+
+ /** @hide */
+ @Override
+ @SuppressWarnings("unchecked")
+ public List<PackageInfo> getInstalledPackagesAsUser(PackageInfoFlags flags, int userId) {
try {
ParceledListSlice<PackageInfo> parceledList =
- mPM.getInstalledPackages(updateFlagsForPackage(flags, userId), userId);
+ mPM.getInstalledPackages(updateFlagsForPackage(flags.getValue(), userId),
+ userId);
if (parceledList == null) {
return Collections.emptyList();
}
@@ -1113,13 +1221,19 @@
@SuppressWarnings("unchecked")
@Override
- public List<PackageInfo> getPackagesHoldingPermissions(
- String[] permissions, int flags) {
+ public List<PackageInfo> getPackagesHoldingPermissions(String[] permissions, int flags) {
+ return this.getPackagesHoldingPermissions(permissions, PackageInfoFlags.of(flags));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public List<PackageInfo> getPackagesHoldingPermissions(String[] permissions,
+ PackageInfoFlags flags) {
final int userId = getUserId();
try {
ParceledListSlice<PackageInfo> parceledList =
mPM.getPackagesHoldingPermissions(permissions,
- updateFlagsForPackage(flags, userId), userId);
+ updateFlagsForPackage(flags.getValue(), userId), userId);
if (parceledList == null) {
return Collections.emptyList();
}
@@ -1135,13 +1249,27 @@
return getInstalledApplicationsAsUser(flags, getUserId());
}
+ @Override
+ public List<ApplicationInfo> getInstalledApplications(ApplicationInfoFlags flags) {
+ return getInstalledApplicationsAsUser(flags, getUserId());
+ }
+
/** @hide */
@SuppressWarnings("unchecked")
@Override
public List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId) {
+ return getInstalledApplicationsAsUser(ApplicationInfoFlags.of(flags), userId);
+ }
+
+ /** @hide */
+ @SuppressWarnings("unchecked")
+ @Override
+ public List<ApplicationInfo> getInstalledApplicationsAsUser(ApplicationInfoFlags flags,
+ int userId) {
try {
ParceledListSlice<ApplicationInfo> parceledList =
- mPM.getInstalledApplications(updateFlagsForApplication(flags, userId), userId);
+ mPM.getInstalledApplications(updateFlagsForApplication(
+ flags.getValue(), userId), userId);
if (parceledList == null) {
return Collections.emptyList();
}
@@ -1249,16 +1377,26 @@
@Override
public ResolveInfo resolveActivity(Intent intent, int flags) {
+ return resolveActivity(intent, ResolveInfoFlags.of(flags));
+ }
+
+ @Override
+ public ResolveInfo resolveActivity(Intent intent, ResolveInfoFlags flags) {
return resolveActivityAsUser(intent, flags, getUserId());
}
@Override
public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) {
+ return resolveActivityAsUser(intent, ResolveInfoFlags.of(flags), userId);
+ }
+
+ @Override
+ public ResolveInfo resolveActivityAsUser(Intent intent, ResolveInfoFlags flags, int userId) {
try {
return mPM.resolveIntent(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- updateFlagsForComponent(flags, userId, intent),
+ updateFlagsForComponent(flags.getValue(), userId, intent),
userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1266,21 +1404,31 @@
}
@Override
- public List<ResolveInfo> queryIntentActivities(Intent intent,
- int flags) {
+ public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
+ return queryIntentActivities(intent, ResolveInfoFlags.of(flags));
+ }
+
+ @Override
+ public List<ResolveInfo> queryIntentActivities(Intent intent, ResolveInfoFlags flags) {
return queryIntentActivitiesAsUser(intent, flags, getUserId());
}
/** @hide Same as above but for a specific user */
@Override
+ public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId) {
+ return queryIntentActivitiesAsUser(intent, ResolveInfoFlags.of(flags), userId);
+ }
+
+ /** @hide Same as above but for a specific user */
+ @Override
@SuppressWarnings("unchecked")
- public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
- int flags, int userId) {
+ public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, ResolveInfoFlags flags,
+ int userId) {
try {
ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentActivities(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- updateFlagsForComponent(flags, userId, intent),
+ updateFlagsForComponent(flags.getValue(), userId, intent),
userId);
if (parceledList == null) {
return Collections.emptyList();
@@ -1292,22 +1440,30 @@
}
@Override
- @SuppressWarnings("unchecked")
public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller, Intent[] specifics,
Intent intent, int flags) {
+ return queryIntentActivityOptions(caller,
+ specifics == null ? null : new ArrayList<>(Arrays.asList(specifics)),
+ intent, ResolveInfoFlags.of(flags));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
+ List<Intent> specifics, Intent intent, ResolveInfoFlags flags) {
final int userId = getUserId();
final ContentResolver resolver = mContext.getContentResolver();
String[] specificTypes = null;
if (specifics != null) {
- final int N = specifics.length;
- for (int i=0; i<N; i++) {
- Intent sp = specifics[i];
+ final int numSpecifics = specifics.size();
+ for (int i = 0; i < numSpecifics; i++) {
+ Intent sp = specifics.get(i);
if (sp != null) {
String t = sp.resolveTypeIfNeeded(resolver);
if (t != null) {
if (specificTypes == null) {
- specificTypes = new String[N];
+ specificTypes = new String[numSpecifics];
}
specificTypes[i] = t;
}
@@ -1318,11 +1474,11 @@
try {
ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentActivityOptions(
caller,
- specifics,
+ specifics == null ? null : specifics.toArray(new Intent[0]),
specificTypes,
intent,
intent.resolveTypeIfNeeded(resolver),
- updateFlagsForComponent(flags, userId, intent),
+ updateFlagsForComponent(flags.getValue(), userId, intent),
userId);
if (parceledList == null) {
return Collections.emptyList();
@@ -1337,13 +1493,22 @@
* @hide
*/
@Override
- @SuppressWarnings("unchecked")
public List<ResolveInfo> queryBroadcastReceiversAsUser(Intent intent, int flags, int userId) {
+ return queryBroadcastReceiversAsUser(intent, ResolveInfoFlags.of(flags), userId);
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public List<ResolveInfo> queryBroadcastReceiversAsUser(Intent intent, ResolveInfoFlags flags,
+ int userId) {
try {
ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentReceivers(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- updateFlagsForComponent(flags, userId, intent),
+ updateFlagsForComponent(flags.getValue(), userId, intent),
userId);
if (parceledList == null) {
return Collections.emptyList();
@@ -1356,17 +1521,27 @@
@Override
public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
+ return queryBroadcastReceivers(intent, ResolveInfoFlags.of(flags));
+ }
+
+ @Override
+ public List<ResolveInfo> queryBroadcastReceivers(Intent intent, ResolveInfoFlags flags) {
return queryBroadcastReceiversAsUser(intent, flags, getUserId());
}
@Override
- public ResolveInfo resolveServiceAsUser(Intent intent, @ResolveInfoFlags int flags,
+ public ResolveInfo resolveServiceAsUser(Intent intent, int flags, @UserIdInt int userId) {
+ return resolveServiceAsUser(intent, ResolveInfoFlags.of(flags), userId);
+ }
+
+ @Override
+ public ResolveInfo resolveServiceAsUser(Intent intent, ResolveInfoFlags flags,
@UserIdInt int userId) {
try {
return mPM.resolveService(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- updateFlagsForComponent(flags, userId, intent),
+ updateFlagsForComponent(flags.getValue(), userId, intent),
userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1375,17 +1550,28 @@
@Override
public ResolveInfo resolveService(Intent intent, int flags) {
+ return resolveService(intent, ResolveInfoFlags.of(flags));
+ }
+
+ @Override
+ public ResolveInfo resolveService(Intent intent, ResolveInfoFlags flags) {
return resolveServiceAsUser(intent, flags, getUserId());
}
@Override
- @SuppressWarnings("unchecked")
public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
+ return queryIntentServicesAsUser(intent, ResolveInfoFlags.of(flags), userId);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, ResolveInfoFlags flags,
+ int userId) {
try {
ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentServices(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- updateFlagsForComponent(flags, userId, intent),
+ updateFlagsForComponent(flags.getValue(), userId, intent),
userId);
if (parceledList == null) {
return Collections.emptyList();
@@ -1398,18 +1584,29 @@
@Override
public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
+ return queryIntentServices(intent, ResolveInfoFlags.of(flags));
+ }
+
+ @Override
+ public List<ResolveInfo> queryIntentServices(Intent intent, ResolveInfoFlags flags) {
return queryIntentServicesAsUser(intent, flags, getUserId());
}
@Override
+ public List<ResolveInfo> queryIntentContentProvidersAsUser(
+ Intent intent, int flags, int userId) {
+ return queryIntentContentProvidersAsUser(intent, ResolveInfoFlags.of(flags), userId);
+ }
+
+ @Override
@SuppressWarnings("unchecked")
public List<ResolveInfo> queryIntentContentProvidersAsUser(
- Intent intent, int flags, int userId) {
+ Intent intent, ResolveInfoFlags flags, int userId) {
try {
ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentContentProviders(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- updateFlagsForComponent(flags, userId, intent),
+ updateFlagsForComponent(flags.getValue(), userId, intent),
userId);
if (parceledList == null) {
return Collections.emptyList();
@@ -1422,39 +1619,68 @@
@Override
public List<ResolveInfo> queryIntentContentProviders(Intent intent, int flags) {
+ return queryIntentContentProviders(intent, ResolveInfoFlags.of(flags));
+ }
+
+ @Override
+ public List<ResolveInfo> queryIntentContentProviders(Intent intent, ResolveInfoFlags flags) {
return queryIntentContentProvidersAsUser(intent, flags, getUserId());
}
@Override
public ProviderInfo resolveContentProvider(String name, int flags) {
+ return resolveContentProvider(name, ComponentInfoFlags.of(flags));
+ }
+
+ @Override
+ public ProviderInfo resolveContentProvider(String name, ComponentInfoFlags flags) {
return resolveContentProviderAsUser(name, flags, getUserId());
}
/** @hide **/
@Override
public ProviderInfo resolveContentProviderAsUser(String name, int flags, int userId) {
+ return resolveContentProviderAsUser(name, ComponentInfoFlags.of(flags), userId);
+ }
+
+ /** @hide **/
+ @Override
+ public ProviderInfo resolveContentProviderAsUser(String name, ComponentInfoFlags flags,
+ int userId) {
try {
return mPM.resolveContentProvider(name,
- updateFlagsForComponent(flags, userId, null), userId);
+ updateFlagsForComponent(flags.getValue(), userId, null), userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@Override
- public List<ProviderInfo> queryContentProviders(String processName,
- int uid, int flags) {
+ public List<ProviderInfo> queryContentProviders(String processName, int uid, int flags) {
+ return queryContentProviders(processName, uid, ComponentInfoFlags.of(flags));
+ }
+
+ @Override
+ public List<ProviderInfo> queryContentProviders(String processName, int uid,
+ ComponentInfoFlags flags) {
return queryContentProviders(processName, uid, flags, null);
}
@Override
+ public List<ProviderInfo> queryContentProviders(String processName,
+ int uid, int flags, String metaDataKey) {
+ return queryContentProviders(processName, uid, ComponentInfoFlags.of(flags), metaDataKey);
+ }
+
+ @Override
@SuppressWarnings("unchecked")
public List<ProviderInfo> queryContentProviders(String processName,
- int uid, int flags, String metaDataKey) {
+ int uid, ComponentInfoFlags flags, String metaDataKey) {
try {
ParceledListSlice<ProviderInfo> slice = mPM.queryContentProviders(processName, uid,
- updateFlagsForComponent(flags, UserHandle.getUserId(uid), null), metaDataKey);
- return slice != null ? slice.getList() : Collections.<ProviderInfo>emptyList();
+ updateFlagsForComponent(flags.getValue(), UserHandle.getUserId(uid),
+ null), metaDataKey);
+ return slice != null ? slice.getList() : Collections.emptyList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1839,7 +2065,7 @@
/**
* Update given flags when being used to request {@link PackageInfo}.
*/
- private int updateFlagsForPackage(int flags, int userId) {
+ private long updateFlagsForPackage(long flags, int userId) {
if ((flags & (GET_ACTIVITIES | GET_RECEIVERS | GET_SERVICES | GET_PROVIDERS)) != 0) {
// Caller is asking for component details, so they'd better be
// asking for specific Direct Boot matching behavior
@@ -1855,14 +2081,15 @@
/**
* Update given flags when being used to request {@link ApplicationInfo}.
*/
- private int updateFlagsForApplication(int flags, int userId) {
+ private long updateFlagsForApplication(long flags, int userId) {
return updateFlagsForPackage(flags, userId);
}
/**
* Update given flags when being used to request {@link ComponentInfo}.
*/
- private int updateFlagsForComponent(int flags, int userId, Intent intent) {
+ private long updateFlagsForComponent(@ComponentInfoFlagsBits long flags, int userId,
+ Intent intent) {
if (intent != null) {
if ((intent.getFlags() & Intent.FLAG_DIRECT_BOOT_AUTO) != 0) {
flags |= MATCH_DIRECT_BOOT_AUTO;
@@ -2109,18 +2336,24 @@
}
@Nullable
+ public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath, int flags) {
+ return getPackageArchiveInfo(archiveFilePath, PackageInfoFlags.of(flags));
+ }
+
+ @Nullable
public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath,
- @PackageInfoFlags int flags) {
- if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ PackageInfoFlags flags) {
+ long flagsBits = flags.getValue();
+ if ((flagsBits & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_DIRECT_BOOT_AWARE)) == 0) {
// Caller expressed no opinion about what encryption
// aware/unaware components they want to see, so match both
- flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE
+ flagsBits |= PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
}
- boolean collectCertificates = (flags & PackageManager.GET_SIGNATURES) != 0
- || (flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0;
+ boolean collectCertificates = (flagsBits & PackageManager.GET_SIGNATURES) != 0
+ || (flagsBits & PackageManager.GET_SIGNING_CERTIFICATES) != 0;
ParseInput input = ParseTypeImpl.forParsingWithoutPlatformCompat().reset();
ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefault(input,
@@ -2129,8 +2362,8 @@
if (result.isError()) {
return null;
}
- return PackageInfoWithoutStateUtils.generate(result.getResult(), null, flags, 0, 0, null,
- FrameworkPackageUserState.DEFAULT, UserHandle.getCallingUserId());
+ return PackageInfoWithoutStateUtils.generate(result.getResult(), null, flagsBits, 0, 0,
+ null, FrameworkPackageUserState.DEFAULT, UserHandle.getCallingUserId());
}
@Override
diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java
index 8637e31..58f60a6 100644
--- a/core/java/android/app/ConfigurationController.java
+++ b/core/java/android/app/ConfigurationController.java
@@ -154,9 +154,12 @@
int configDiff;
boolean equivalent;
+ // Get theme outside of synchronization to avoid nested lock.
+ final Resources.Theme systemTheme = mActivityThread.getSystemContext().getTheme();
+ final ContextImpl systemUiContext = mActivityThread.getSystemUiContextNoCreate();
+ final Resources.Theme systemUiTheme =
+ systemUiContext != null ? systemUiContext.getTheme() : null;
synchronized (mResourcesManager) {
- final Resources.Theme systemTheme = mActivityThread.getSystemContext().getTheme();
- final Resources.Theme systemUiTheme = mActivityThread.getSystemUiContext().getTheme();
if (mPendingConfiguration != null) {
if (!mPendingConfiguration.isOtherSeqNewer(config)) {
config = mPendingConfiguration;
@@ -207,7 +210,8 @@
systemTheme.rebase();
}
- if ((systemUiTheme.getChangingConfigurations() & configDiff) != 0) {
+ if (systemUiTheme != null
+ && (systemUiTheme.getChangingConfigurations() & configDiff) != 0) {
systemUiTheme.rebase();
}
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4a7361e..885feb1 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3212,6 +3212,10 @@
final void performFinalCleanup(String who, String what) {
//Log.i(TAG, "Cleanup up context: " + this);
mPackageInfo.removeContextRegistrations(getOuterContext(), who, what);
+ if (mContextType == CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
+ && mToken instanceof WindowTokenClient) {
+ mMainThread.onSystemUiContextCleanup(this);
+ }
}
@UnsupportedAppUsage
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 8f904b5..a2578d6 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -520,6 +520,10 @@
// descriptor.
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
boolean stopBinderTrackingAndDump(in ParcelFileDescriptor fd);
+
+ /** Enables server-side binder tracing for the calling uid. */
+ void enableBinderTracing();
+
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
void suppressResizeConfigChanges(boolean suppress);
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index af907af..779552f1 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5808,6 +5808,7 @@
p, result);
buildCustomContentIntoTemplate(mContext, standard, customContent,
p, result);
+ makeHeaderExpanded(standard);
return standard;
}
diff --git a/core/java/android/app/NotificationHistory.java b/core/java/android/app/NotificationHistory.java
index eb9ec86..b40fbee 100644
--- a/core/java/android/app/NotificationHistory.java
+++ b/core/java/android/app/NotificationHistory.java
@@ -497,7 +497,11 @@
p.writeLong(notification.getPostedTimeMs());
p.writeString(notification.getTitle());
p.writeString(notification.getText());
- notification.getIcon().writeToParcel(p, flags);
+ p.writeBoolean(false);
+ // The current design does not display icons, so don't bother adding them to the parcel
+ //if (notification.getIcon() != null) {
+ // notification.getIcon().writeToParcel(p, flags);
+ //}
}
/**
@@ -539,7 +543,9 @@
notificationOut.setPostedTimeMs(p.readLong());
notificationOut.setTitle(p.readString());
notificationOut.setText(p.readString());
- notificationOut.setIcon(Icon.CREATOR.createFromParcel(p));
+ if (p.readBoolean()) {
+ notificationOut.setIcon(Icon.CREATOR.createFromParcel(p));
+ }
return notificationOut.build();
}
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 2c0f983..bc78df5 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -39,7 +39,7 @@
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
-import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.PackageManager.ResolveInfoFlagsBits;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.os.Build;
@@ -1296,7 +1296,7 @@
@RequiresPermission(permission.GET_INTENT_SENDER_INTENT)
@SystemApi(client = Client.MODULE_LIBRARIES)
@TestApi
- public @NonNull List<ResolveInfo> queryIntentComponents(@ResolveInfoFlags int flags) {
+ public @NonNull List<ResolveInfo> queryIntentComponents(@ResolveInfoFlagsBits int flags) {
try {
ParceledListSlice<ResolveInfo> parceledList = ActivityManager.getService()
.queryIntentComponentsForIntentSender(mTarget, flags);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 089c269..5002a59 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -131,6 +131,7 @@
import android.media.tv.tunerresourcemanager.ITunerResourceManager;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
import android.net.ConnectivityFrameworkInitializer;
+import android.net.ConnectivityFrameworkInitializerTiramisu;
import android.net.EthernetManager;
import android.net.IEthernetManager;
import android.net.IIpSecService;
@@ -146,8 +147,6 @@
import android.net.VpnManager;
import android.net.lowpan.ILowpanManager;
import android.net.lowpan.LowpanManager;
-import android.net.nsd.INsdManager;
-import android.net.nsd.NsdManager;
import android.net.vcn.IVcnManagementService;
import android.net.vcn.VcnManager;
import android.net.wifi.WifiFrameworkInitializer;
@@ -576,15 +575,6 @@
ctx.mMainThread.getHandler());
}});
- registerService(Context.NSD_SERVICE, NsdManager.class,
- new CachedServiceFetcher<NsdManager>() {
- @Override
- public NsdManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.NSD_SERVICE);
- INsdManager service = INsdManager.Stub.asInterface(b);
- return new NsdManager(ctx.getOuterContext(), service);
- }});
-
registerService(Context.PEOPLE_SERVICE, PeopleManager.class,
new CachedServiceFetcher<PeopleManager>() {
@Override
@@ -1513,7 +1503,7 @@
}
});
- registerService(Context.COMMUNAL_MANAGER_SERVICE, CommunalManager.class,
+ registerService(Context.COMMUNAL_SERVICE, CommunalManager.class,
new CachedServiceFetcher<CommunalManager>() {
@Override
public CommunalManager createService(ContextImpl ctx) {
@@ -1522,7 +1512,7 @@
return null;
}
IBinder iBinder =
- ServiceManager.getService(Context.COMMUNAL_MANAGER_SERVICE);
+ ServiceManager.getService(Context.COMMUNAL_SERVICE);
return iBinder != null ? new CommunalManager(
ICommunalManager.Stub.asInterface(iBinder)) : null;
}
@@ -1547,6 +1537,7 @@
SupplementalProcessFrameworkInitializer.registerServiceWrappers();
UwbFrameworkInitializer.registerServiceWrappers();
SafetyCenterFrameworkInitializer.registerServiceWrappers();
+ ConnectivityFrameworkInitializerTiramisu.registerServiceWrappers();
} finally {
// If any of the above code throws, we're in a pretty bad shape and the process
// will likely crash, but we'll reset it just in case there's an exception handler...
diff --git a/core/java/android/app/communal/CommunalManager.java b/core/java/android/app/communal/CommunalManager.java
index 60730ad..22f07693 100644
--- a/core/java/android/app/communal/CommunalManager.java
+++ b/core/java/android/app/communal/CommunalManager.java
@@ -17,15 +17,21 @@
package android.app.communal;
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.Overridable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.RemoteException;
+import android.util.ArrayMap;
+
+import java.util.concurrent.Executor;
/**
* System private class for talking with the
@@ -33,10 +39,12 @@
*
* @hide
*/
-@SystemService(Context.COMMUNAL_MANAGER_SERVICE)
+@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+@SystemService(Context.COMMUNAL_SERVICE)
@RequiresFeature(PackageManager.FEATURE_COMMUNAL_MODE)
public final class CommunalManager {
private final ICommunalManager mService;
+ private final ArrayMap<CommunalModeListener, ICommunalModeListener> mCommunalModeListeners;
/**
* This change id is used to annotate packages which can run in communal mode by default,
@@ -59,15 +67,20 @@
@Disabled
public static final long ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT = 200324021L;
+ /** @hide */
public CommunalManager(ICommunalManager service) {
mService = service;
+ mCommunalModeListeners = new ArrayMap<CommunalModeListener, ICommunalModeListener>();
}
/**
* Updates whether or not the communal view is currently showing over the lockscreen.
*
* @param isShowing Whether communal view is showing.
+ *
+ * @hide
*/
+ @TestApi
@RequiresPermission(Manifest.permission.WRITE_COMMUNAL_STATE)
public void setCommunalViewShowing(boolean isShowing) {
try {
@@ -76,4 +89,72 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Checks whether or not the communal view is currently showing over the lockscreen.
+ */
+ @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
+ public boolean isCommunalMode() {
+ try {
+ return mService.isCommunalMode();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Listener for communal state changes.
+ */
+ @FunctionalInterface
+ public interface CommunalModeListener {
+ /**
+ * Callback function that executes when the communal state changes.
+ */
+ void onCommunalModeChanged(boolean isCommunalMode);
+ }
+
+ /**
+ * Registers a callback to execute when the communal state changes.
+ *
+ * @param listener The listener to add to receive communal state changes.
+ * @param executor {@link Executor} to dispatch to. To dispatch the callback to the main
+ * thread of your application, use
+ * {@link android.content.Context#getMainExecutor()}.
+ */
+ @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
+ public void addCommunalModeListener(@NonNull Executor executor,
+ @NonNull CommunalModeListener listener) {
+ synchronized (mCommunalModeListeners) {
+ try {
+ ICommunalModeListener iListener = new ICommunalModeListener.Stub() {
+ @Override
+ public void onCommunalModeChanged(boolean isCommunalMode) {
+ executor.execute(() -> listener.onCommunalModeChanged(isCommunalMode));
+ }
+ };
+ mService.addCommunalModeListener(iListener);
+ mCommunalModeListeners.put(listener, iListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Unregisters a callback that executes when communal state changes.
+ */
+ @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
+ public void removeCommunalModeListener(@NonNull CommunalModeListener listener) {
+ synchronized (mCommunalModeListeners) {
+ ICommunalModeListener iListener = mCommunalModeListeners.get(listener);
+ if (iListener != null) {
+ try {
+ mService.removeCommunalModeListener(iListener);
+ mCommunalModeListeners.remove(listener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
}
diff --git a/core/java/android/app/communal/ICommunalManager.aidl b/core/java/android/app/communal/ICommunalManager.aidl
index 02e8a65..869891e 100644
--- a/core/java/android/app/communal/ICommunalManager.aidl
+++ b/core/java/android/app/communal/ICommunalManager.aidl
@@ -16,12 +16,17 @@
package android.app.communal;
+import android.app.communal.ICommunalModeListener;
+
/**
* System private API for talking with the communal manager service that handles communal mode
* state.
*
* @hide
*/
-oneway interface ICommunalManager {
- void setCommunalViewShowing(boolean isShowing);
+interface ICommunalManager {
+ oneway void setCommunalViewShowing(boolean isShowing);
+ boolean isCommunalMode();
+ void addCommunalModeListener(in ICommunalModeListener listener);
+ void removeCommunalModeListener(in ICommunalModeListener listener);
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/core/java/android/app/communal/ICommunalModeListener.aidl
similarity index 74%
rename from packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
rename to core/java/android/app/communal/ICommunalModeListener.aidl
index fbb78c8..006e782 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/core/java/android/app/communal/ICommunalModeListener.aidl
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.flags;
+package android.app.communal;
/**
- * Class to manage simple DeviceConfig-based feature flags.
+ * System private API to be notified about communal mode changes.
*
- * See {@link Flags} for instructions on defining new flags.
+ * @hide
*/
-public interface FeatureFlags extends FlagReader {
-}
+oneway interface ICommunalModeListener {
+ void onCommunalModeChanged(boolean isCommunalMode);
+}
\ No newline at end of file
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 20122fb..602a186 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2261,21 +2261,21 @@
public @interface LeFeatureReturnValues {}
/**
- * Returns {@link BluetoothStatusCodes#SUCCESS} if LE Connected Isochronous Stream Central
- * feature is supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if
+ * Returns {@link BluetoothStatusCodes#SUCCESS} if the LE audio feature is
+ * supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if
* the feature is not supported or an error code.
*
- * @return whether the chipset supports the LE Connected Isochronous Stream Central feature
+ * @return whether the LE audio is supported
*/
@RequiresNoPermission
- public @LeFeatureReturnValues int isCisCentralSupported() {
+ public @LeFeatureReturnValues int isLeAudioSupported() {
if (!getLeAccess()) {
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
}
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.isCisCentralSupported();
+ return mService.isLeAudioSupported();
}
} catch (RemoteException e) {
e.rethrowFromSystemServer();
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index c000e56..8ee38d3 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -23,7 +23,6 @@
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Attributable;
import android.content.AttributionSource;
@@ -170,7 +169,7 @@
mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
+ UserHandle.CURRENT)) {
Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothProfileConnector.java b/core/java/android/bluetooth/BluetoothProfileConnector.java
index a254291..ecd5e40 100644
--- a/core/java/android/bluetooth/BluetoothProfileConnector.java
+++ b/core/java/android/bluetooth/BluetoothProfileConnector.java
@@ -103,7 +103,7 @@
mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
+ UserHandle.CURRENT)) {
logError("Could not bind to Bluetooth Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/le/TransportBlock.java b/core/java/android/bluetooth/le/TransportBlock.java
index b388bed..18bad9c 100644
--- a/core/java/android/bluetooth/le/TransportBlock.java
+++ b/core/java/android/bluetooth/le/TransportBlock.java
@@ -24,6 +24,7 @@
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
+import java.util.Arrays;
/**
* Wrapper for Transport Discovery Data Transport Blocks.
@@ -59,8 +60,12 @@
mOrgId = in.readInt();
mTdsFlags = in.readInt();
mTransportDataLength = in.readInt();
- mTransportData = new byte[mTransportDataLength];
- in.readByteArray(mTransportData);
+ if (mTransportDataLength > 0) {
+ mTransportData = new byte[mTransportDataLength];
+ in.readByteArray(mTransportData);
+ } else {
+ mTransportData = null;
+ }
}
@Override
@@ -68,7 +73,9 @@
dest.writeInt(mOrgId);
dest.writeInt(mTdsFlags);
dest.writeInt(mTransportDataLength);
- dest.writeByteArray(mTransportData);
+ if (mTransportData != null) {
+ dest.writeByteArray(mTransportData);
+ }
}
/**
@@ -79,6 +86,21 @@
return 0;
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ TransportBlock other = (TransportBlock) obj;
+ return Arrays.equals(toByteArray(), other.toByteArray());
+ }
+
public static final @NonNull Creator<TransportBlock> CREATOR = new Creator<TransportBlock>() {
@Override
public TransportBlock createFromParcel(Parcel in) {
diff --git a/core/java/android/bluetooth/le/TransportDiscoveryData.java b/core/java/android/bluetooth/le/TransportDiscoveryData.java
index c8e97f9..2b52f19 100644
--- a/core/java/android/bluetooth/le/TransportDiscoveryData.java
+++ b/core/java/android/bluetooth/le/TransportDiscoveryData.java
@@ -26,6 +26,7 @@
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -96,6 +97,21 @@
return 0;
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ TransportDiscoveryData other = (TransportDiscoveryData) obj;
+ return Arrays.equals(toByteArray(), other.toByteArray());
+ }
+
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mTransportDataType);
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 2b12f12..ae13425 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -776,6 +776,51 @@
}
}
+ /**
+ * Notify the system that the given self-managed association has just 'appeared'.
+ * This causes the system to bind to the companion app to keep it running until the association
+ * is reported as 'disappeared'
+ *
+ * <p>This API is only available for the companion apps that manage the connectivity by
+ * themselves.</p>
+ *
+ * @param associationId the unique {@link AssociationInfo#getId ID} assigned to the Association
+ * recorded by CompanionDeviceManager
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED)
+ public void notifyDeviceAppeared(int associationId) {
+ try {
+ mService.notifyDeviceAppeared(associationId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Notify the system that the given self-managed association has just 'disappeared'.
+ * This causes the system to unbind to the companion app.
+ *
+ * <p>This API is only available for the companion apps that manage the connectivity by
+ * themselves.</p>
+ *
+ * @param associationId the unique {@link AssociationInfo#getId ID} assigned to the Association
+ * recorded by CompanionDeviceManager
+
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED)
+ public void notifyDeviceDisappeared(int associationId) {
+ try {
+ mService.notifyDeviceDisappeared(associationId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private boolean checkFeaturePresent() {
boolean featurePresent = mService != null;
if (!featurePresent && DEBUG) {
diff --git a/core/java/android/companion/CompanionDeviceService.java b/core/java/android/companion/CompanionDeviceService.java
index 15266d6..3237f7c 100644
--- a/core/java/android/companion/CompanionDeviceService.java
+++ b/core/java/android/companion/CompanionDeviceService.java
@@ -21,12 +21,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.TestApi;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
+
import com.android.internal.util.function.pooled.PooledLambda;
import java.util.Objects;
@@ -34,8 +36,9 @@
/**
* Service to be implemented by apps that manage a companion device.
*
- * System will keep this service bound whenever an associated device is nearby,
- * ensuring app stays alive.
+ * System will keep this service bound whenever an associated device is nearby for Bluetooth
+ * devices or companion app manages the connectivity and reports disappeared, ensuring app stays
+ * alive
*
* An app must be {@link CompanionDeviceManager#associate associated} with at leas one device,
* before it can take advantage of this service.
@@ -43,6 +46,17 @@
* You must declare this service in your manifest with an
* intent-filter action of {@link #SERVICE_INTERFACE} and
* permission of {@link android.Manifest.permission#BIND_COMPANION_DEVICE_SERVICE}
+ *
+ * <p>If you want to declare more than one of these services, you must declare the meta-data in the
+ * service of your manifest with the corresponding name and value to true to indicate the
+ * primary service.
+ * Only the primary one will get the callback from
+ * {@link #onDeviceAppeared(AssociationInfo associationInfo)}.</p>
+ *
+ * Example:
+ * <meta-data
+ * android:name="primary"
+ * android:value="true" />
*/
public abstract class CompanionDeviceService extends Service {
@@ -52,13 +66,14 @@
* An intent action for a service to be bound whenever this app's companion device(s)
* are nearby.
*
- * <p>The app will be kept alive for as long as the device is nearby.
+ * <p>The app will be kept alive for as long as the device is nearby or companion app reports
+ * appeared.
* If the app is not running at the time device gets connected, the app will be woken up.</p>
*
- * <p>Shortly after the device goes out of range, the service will be unbound, and the
- * app will be eligible for cleanup, unless any other user-visible components are running.</p>
+ * <p>Shortly after the device goes out of range or the companion app reports disappeared,
+ * the service will be unbound, and the app will be eligible for cleanup, unless any other
+ * user-visible components are running.</p>
*
- * <p>An app shouldn't declare more than one of these services.
* If running in background is not essential for the devices that this app can manage,
* app should avoid declaring this service.</p>
*
@@ -73,9 +88,13 @@
* Called by system whenever a device associated with this app is available.
*
* @param address the MAC address of the device
+ * @deprecated please override {@link #onDeviceAppeared(AssociationInfo)} instead.
*/
+ @Deprecated
@MainThread
- public abstract void onDeviceAppeared(@NonNull String address);
+ public void onDeviceAppeared(@NonNull String address) {
+ // Do nothing. Companion apps can override this function.
+ }
/**
* Called by system whenever a device associated with this app stops being available.
@@ -83,9 +102,13 @@
* Usually this means the device goes out of range or is turned off.
*
* @param address the MAC address of the device
+ * @deprecated please override {@link #onDeviceDisappeared(AssociationInfo)} instead.
*/
+ @Deprecated
@MainThread
- public abstract void onDeviceDisappeared(@NonNull String address);
+ public void onDeviceDisappeared(@NonNull String address) {
+ // Do nothing. Companion apps can override this function.
+ }
/**
* Called by system whenever the system dispatches a message to the app to send it to
@@ -118,10 +141,35 @@
companionDeviceManager.dispatchMessage(messageId, associationId, message);
}
+ /**
+ * Called by system whenever a device associated with this app is connected.
+ *
+ * @param associationInfo A record for the companion device.
+ */
+ @MainThread
+ public void onDeviceAppeared(@NonNull AssociationInfo associationInfo) {
+ if (!associationInfo.isSelfManaged()) {
+ onDeviceAppeared(associationInfo.getDeviceMacAddressAsString());
+ }
+ }
+
+ /**
+ * Called by system whenever a device associated with this app is disconnected.
+ *
+ * @param associationInfo A record for the companion device.
+ */
+ @MainThread
+ public void onDeviceDisappeared(@NonNull AssociationInfo associationInfo) {
+ if (!associationInfo.isSelfManaged()) {
+ onDeviceDisappeared(associationInfo.getDeviceMacAddressAsString());
+ }
+ }
+
@Nullable
@Override
public final IBinder onBind(@NonNull Intent intent) {
if (Objects.equals(intent.getAction(), SERVICE_INTERFACE)) {
+ onBindCompanionDeviceService(intent);
return mRemote;
}
Log.w(LOG_TAG,
@@ -129,20 +177,26 @@
return null;
}
+ /**
+ * Used to track the state of Binder connection in CTS tests.
+ * @hide
+ */
+ @TestApi
+ public void onBindCompanionDeviceService(@NonNull Intent intent) {
+ }
+
class Stub extends ICompanionDeviceService.Stub {
@Override
- public void onDeviceAppeared(String address) {
- Handler.getMain().sendMessage(PooledLambda.obtainMessage(
- CompanionDeviceService::onDeviceAppeared,
- CompanionDeviceService.this, address));
+ public void onDeviceAppeared(AssociationInfo associationInfo) {
+ Handler.getMain().post(
+ () -> CompanionDeviceService.this.onDeviceAppeared(associationInfo));
}
@Override
- public void onDeviceDisappeared(String address) {
- Handler.getMain().sendMessage(PooledLambda.obtainMessage(
- CompanionDeviceService::onDeviceDisappeared,
- CompanionDeviceService.this, address));
+ public void onDeviceDisappeared(AssociationInfo associationInfo) {
+ Handler.getMain().post(
+ () -> CompanionDeviceService.this.onDeviceDisappeared(associationInfo));
}
public void onDispatchMessage(int messageId, int associationId, @NonNull byte[] message) {
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index 1558db2..68a6031f 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -65,5 +65,10 @@
void dispatchMessage(in int messageId, in int associationId, in byte[] message);
void addOnAssociationsChangedListener(IOnAssociationsChangedListener listener, int userId);
+
void removeOnAssociationsChangedListener(IOnAssociationsChangedListener listener, int userId);
+
+ void notifyDeviceAppeared(int associationId);
+
+ void notifyDeviceDisappeared(int associationId);
}
diff --git a/core/java/android/companion/ICompanionDeviceService.aidl b/core/java/android/companion/ICompanionDeviceService.aidl
index 25212a1..4e453573 100644
--- a/core/java/android/companion/ICompanionDeviceService.aidl
+++ b/core/java/android/companion/ICompanionDeviceService.aidl
@@ -16,9 +16,11 @@
package android.companion;
+import android.companion.AssociationInfo;
+
/** @hide */
oneway interface ICompanionDeviceService {
- void onDeviceAppeared(in String address);
- void onDeviceDisappeared(in String address);
+ void onDeviceAppeared(in AssociationInfo associationInfo);
+ void onDeviceDisappeared(in AssociationInfo associationInfo);
void onDispatchMessage(in int messageId, in int associationId, in byte[] message);
}
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index dabc603..82ad150 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -16,6 +16,13 @@
package android.companion.virtual;
+import android.graphics.Point;
+import android.hardware.input.VirtualKeyEvent;
+import android.hardware.input.VirtualMouseButtonEvent;
+import android.hardware.input.VirtualMouseRelativeEvent;
+import android.hardware.input.VirtualMouseScrollEvent;
+import android.hardware.input.VirtualTouchEvent;
+
/**
* Interface for a virtual device.
*
@@ -34,4 +41,29 @@
* Closes the virtual device and frees all associated resources.
*/
void close();
+ void createVirtualKeyboard(
+ int displayId,
+ String inputDeviceName,
+ int vendorId,
+ int productId,
+ IBinder token);
+ void createVirtualMouse(
+ int displayId,
+ String inputDeviceName,
+ int vendorId,
+ int productId,
+ IBinder token);
+ void createVirtualTouchscreen(
+ int displayId,
+ String inputDeviceName,
+ int vendorId,
+ int productId,
+ IBinder token,
+ in Point screenSize);
+ void unregisterInputDevice(IBinder token);
+ boolean sendKeyEvent(IBinder token, in VirtualKeyEvent event);
+ boolean sendButtonEvent(IBinder token, in VirtualMouseButtonEvent event);
+ boolean sendRelativeEvent(IBinder token, in VirtualMouseRelativeEvent event);
+ boolean sendScrollEvent(IBinder token, in VirtualMouseScrollEvent event);
+ boolean sendTouchEvent(IBinder token, in VirtualTouchEvent event);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 590b108..0d024b1 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -23,7 +23,13 @@
import android.annotation.SystemService;
import android.companion.AssociationInfo;
import android.content.Context;
+import android.graphics.Point;
+import android.hardware.display.VirtualDisplay;
+import android.hardware.input.VirtualKeyboard;
+import android.hardware.input.VirtualMouse;
+import android.hardware.input.VirtualTouchscreen;
import android.os.Binder;
+import android.os.IBinder;
import android.os.RemoteException;
/**
@@ -73,6 +79,8 @@
* A virtual device has its own virtual display, audio output, microphone, and camera etc. The
* creator of a virtual device can take the output from the virtual display and stream it over
* to another device, and inject input events that are received from the remote device.
+ *
+ * TODO(b/204081582): Consider using a builder pattern for the input APIs.
*/
public static class VirtualDevice implements AutoCloseable {
@@ -95,5 +103,88 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Creates a virtual keyboard.
+ *
+ * @param display the display that the events inputted through this device should target
+ * @param inputDeviceName the name to call this input device
+ * @param vendorId the vendor id
+ * @param productId the product id
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ @NonNull
+ public VirtualKeyboard createVirtualKeyboard(
+ @NonNull VirtualDisplay display,
+ @NonNull String inputDeviceName,
+ int vendorId,
+ int productId) {
+ try {
+ final IBinder token = new Binder(
+ "android.hardware.input.VirtualKeyboard:" + inputDeviceName);
+ mVirtualDevice.createVirtualKeyboard(display.getDisplay().getDisplayId(),
+ inputDeviceName, vendorId, productId, token);
+ return new VirtualKeyboard(mVirtualDevice, token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Creates a virtual mouse.
+ *
+ * @param display the display that the events inputted through this device should target
+ * @param inputDeviceName the name to call this input device
+ * @param vendorId the vendor id
+ * @param productId the product id
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ @NonNull
+ public VirtualMouse createVirtualMouse(
+ @NonNull VirtualDisplay display,
+ @NonNull String inputDeviceName,
+ int vendorId,
+ int productId) {
+ try {
+ final IBinder token = new Binder(
+ "android.hardware.input.VirtualMouse:" + inputDeviceName);
+ mVirtualDevice.createVirtualMouse(display.getDisplay().getDisplayId(),
+ inputDeviceName, vendorId, productId, token);
+ return new VirtualMouse(mVirtualDevice, token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Creates a virtual touchscreen.
+ *
+ * @param display the display that the events inputted through this device should target
+ * @param inputDeviceName the name to call this input device
+ * @param vendorId the vendor id
+ * @param productId the product id
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ @NonNull
+ public VirtualTouchscreen createVirtualTouchscreen(
+ @NonNull VirtualDisplay display,
+ @NonNull String inputDeviceName,
+ int vendorId,
+ int productId) {
+ try {
+ final IBinder token = new Binder(
+ "android.hardware.input.VirtualTouchscreen:" + inputDeviceName);
+ final Point size = new Point();
+ display.getDisplay().getSize(size);
+ mVirtualDevice.createVirtualTouchscreen(display.getDisplay().getDisplayId(),
+ inputDeviceName, vendorId, productId, token, size);
+ return new VirtualTouchscreen(mVirtualDevice, token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 73740d2c..543239b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5862,13 +5862,14 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
- * {@link android.app.CommunalManager} for interacting with the global system state.
+ * {@link android.app.communal.CommunalManager} for interacting with the global system state.
*
* @see #getSystemService(String)
- * @see android.app.CommunalManager
+ * @see android.app.communal.CommunalManager
* @hide
*/
- public static final String COMMUNAL_MANAGER_SERVICE = "communal_manager";
+ @SystemApi
+ public static final String COMMUNAL_SERVICE = "communal";
/**
* Use with {@link #getSystemService(String)} to retrieve a
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 18237a2..983d0cc 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -9236,7 +9236,7 @@
* @see #resolveActivity
*/
public ActivityInfo resolveActivityInfo(@NonNull PackageManager pm,
- @PackageManager.ComponentInfoFlags int flags) {
+ @PackageManager.ComponentInfoFlagsBits int flags) {
ActivityInfo ai = null;
if (mComponent != null) {
try {
@@ -9264,7 +9264,7 @@
*/
@UnsupportedAppUsage
public @Nullable ComponentName resolveSystemService(@NonNull PackageManager pm,
- @PackageManager.ComponentInfoFlags int flags) {
+ @PackageManager.ComponentInfoFlagsBits int flags) {
if (mComponent != null) {
return mComponent;
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 45d6ddd..8bea006 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -304,12 +304,23 @@
* @see android.R.attr#colorMode
*/
public static final int COLOR_MODE_HDR = 2;
+ // 3 Corresponds to android::uirenderer::ColorMode::Hdr10.
+ /**
+ * Value of {@link #colorMode} indicating that the activity should use an
+ * 8 bit alpha buffer if the presentation display supports it.
+ *
+ * @see android.R.attr#colorMode
+ * @hide
+ */
+ public static final int COLOR_MODE_A8 = 4;
+
/** @hide */
@IntDef(prefix = { "COLOR_MODE_" }, value = {
COLOR_MODE_DEFAULT,
COLOR_MODE_WIDE_COLOR_GAMUT,
COLOR_MODE_HDR,
+ COLOR_MODE_A8,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ColorMode {}
@@ -1374,18 +1385,18 @@
* Returns if the activity should never be sandboxed to the activity window bounds.
* @hide
*/
- public boolean neverSandboxDisplayApis() {
+ public boolean neverSandboxDisplayApis(ConstrainDisplayApisConfig constrainDisplayApisConfig) {
return isChangeEnabled(NEVER_SANDBOX_DISPLAY_APIS)
- || ConstrainDisplayApisConfig.neverConstrainDisplayApis(applicationInfo);
+ || constrainDisplayApisConfig.getNeverConstrainDisplayApis(applicationInfo);
}
/**
* Returns if the activity should always be sandboxed to the activity window bounds.
* @hide
*/
- public boolean alwaysSandboxDisplayApis() {
+ public boolean alwaysSandboxDisplayApis(ConstrainDisplayApisConfig constrainDisplayApisConfig) {
return isChangeEnabled(ALWAYS_SANDBOX_DISPLAY_APIS)
- || ConstrainDisplayApisConfig.alwaysConstrainDisplayApis(applicationInfo);
+ || constrainDisplayApisConfig.getAlwaysConstrainDisplayApis(applicationInfo);
}
/** @hide */
@@ -1682,6 +1693,8 @@
return "COLOR_MODE_WIDE_COLOR_GAMUT";
case COLOR_MODE_HDR:
return "COLOR_MODE_HDR";
+ case COLOR_MODE_A8:
+ return "COLOR_MODE_A8";
default:
return Integer.toString(colorMode);
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 84c9fa9..2998f76 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1148,6 +1148,12 @@
public int versionCode;
/**
+ * The timestamp of when this ApplicationInfo was created.
+ * @hide
+ */
+ public long createTimestamp;
+
+ /**
* The user-visible SDK version (ex. 26) of the framework against which the application claims
* to have been compiled, or {@code 0} if not specified.
* <p>
@@ -1639,6 +1645,7 @@
+ requestRawExternalStorageAccess);
}
}
+ pw.println(prefix + "createTimestamp=" + createTimestamp);
super.dumpBack(pw, prefix);
}
@@ -1796,6 +1803,7 @@
}
public ApplicationInfo() {
+ createTimestamp = System.currentTimeMillis();
}
public ApplicationInfo(ApplicationInfo orig) {
@@ -1867,6 +1875,7 @@
memtagMode = orig.memtagMode;
nativeHeapZeroInitialized = orig.nativeHeapZeroInitialized;
requestRawExternalStorageAccess = orig.requestRawExternalStorageAccess;
+ createTimestamp = System.currentTimeMillis();
}
public String toString() {
@@ -1957,6 +1966,7 @@
dest.writeInt(memtagMode);
dest.writeInt(nativeHeapZeroInitialized);
sForBoolean.parcel(requestRawExternalStorageAccess, dest, parcelableFlags);
+ dest.writeLong(createTimestamp);
}
public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
@@ -2044,6 +2054,7 @@
memtagMode = source.readInt();
nativeHeapZeroInitialized = source.readInt();
requestRawExternalStorageAccess = sForBoolean.unparcel(source);
+ createTimestamp = source.readLong();
}
/**
diff --git a/core/java/android/content/pm/ConstrainDisplayApisConfig.java b/core/java/android/content/pm/ConstrainDisplayApisConfig.java
index 11ba3d4..98b73aa 100644
--- a/core/java/android/content/pm/ConstrainDisplayApisConfig.java
+++ b/core/java/android/content/pm/ConstrainDisplayApisConfig.java
@@ -19,10 +19,15 @@
import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
import android.provider.DeviceConfig;
+import android.util.ArrayMap;
+import android.util.Pair;
import android.util.Slog;
+import com.android.internal.os.BackgroundThread;
+
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
/**
* Class for processing flags in the Device Config namespace 'constrain_display_apis'.
@@ -55,19 +60,45 @@
"always_constrain_display_apis";
/**
+ * Indicates that display APIs should never be constrained to the activity window bounds for all
+ * packages.
+ */
+ private boolean mNeverConstrainDisplayApisAllPackages;
+
+ /**
+ * Indicates that display APIs should never be constrained to the activity window bounds for
+ * a set of defined packages. Map keys are package names, and entries are a
+ * 'Pair(<min-version-code>, <max-version-code>)'.
+ */
+ private ArrayMap<String, Pair<Long, Long>> mNeverConstrainConfigMap;
+
+ /**
+ * Indicates that display APIs should always be constrained to the activity window bounds for
+ * a set of defined packages. Map keys are package names, and entries are a
+ * 'Pair(<min-version-code>, <max-version-code>)'.
+ */
+ private ArrayMap<String, Pair<Long, Long>> mAlwaysConstrainConfigMap;
+
+ public ConstrainDisplayApisConfig() {
+ updateCache();
+
+ DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
+ BackgroundThread.getExecutor(), properties -> updateCache());
+ }
+
+ /**
* Returns true if either the flag 'never_constrain_display_apis_all_packages' is true or the
* flag 'never_constrain_display_apis' contains a package entry that matches the given {@code
* applicationInfo}.
*
* @param applicationInfo Information about the application/package.
*/
- public static boolean neverConstrainDisplayApis(ApplicationInfo applicationInfo) {
- if (DeviceConfig.getBoolean(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
- FLAG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES, /* defaultValue= */ false)) {
+ public boolean getNeverConstrainDisplayApis(ApplicationInfo applicationInfo) {
+ if (mNeverConstrainDisplayApisAllPackages) {
return true;
}
- return flagHasMatchingPackageEntry(FLAG_NEVER_CONSTRAIN_DISPLAY_APIS, applicationInfo);
+ return flagHasMatchingPackageEntry(mNeverConstrainConfigMap, applicationInfo);
}
/**
@@ -76,73 +107,106 @@
*
* @param applicationInfo Information about the application/package.
*/
- public static boolean alwaysConstrainDisplayApis(ApplicationInfo applicationInfo) {
- return flagHasMatchingPackageEntry(FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS, applicationInfo);
+ public boolean getAlwaysConstrainDisplayApis(ApplicationInfo applicationInfo) {
+ return flagHasMatchingPackageEntry(mAlwaysConstrainConfigMap, applicationInfo);
+ }
+
+
+ /**
+ * Updates {@link #mNeverConstrainDisplayApisAllPackages}, {@link #mNeverConstrainConfigMap},
+ * and {@link #mAlwaysConstrainConfigMap} from the {@link DeviceConfig}.
+ */
+ private void updateCache() {
+ mNeverConstrainDisplayApisAllPackages = DeviceConfig.getBoolean(
+ NAMESPACE_CONSTRAIN_DISPLAY_APIS,
+ FLAG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES, /* defaultValue= */ false);
+
+ final String neverConstrainConfigStr = DeviceConfig.getString(
+ NAMESPACE_CONSTRAIN_DISPLAY_APIS,
+ FLAG_NEVER_CONSTRAIN_DISPLAY_APIS, /* defaultValue= */ "");
+ mNeverConstrainConfigMap = buildConfigMap(neverConstrainConfigStr);
+
+ final String alwaysConstrainConfigStr = DeviceConfig.getString(
+ NAMESPACE_CONSTRAIN_DISPLAY_APIS,
+ FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS, /* defaultValue= */ "");
+ mAlwaysConstrainConfigMap = buildConfigMap(alwaysConstrainConfigStr);
+ }
+
+ /**
+ * Processes the configuration string into a map of version codes, for the given
+ * configuration to be applied to the specified packages. If the given package
+ * entry string is invalid, then the map will not contain an entry for the package.
+ *
+ * @param configStr A configuration string expected to be in the format of a list of package
+ * entries separated by ','. A package entry expected to be in the format
+ * '<package-name>:<min-version-code>?:<max-version-code>?'.
+ * @return a map of configuration entries, where each key is a package name. Each value is
+ * a pair of version codes, in the format 'Pair(<min-version-code>, <max-version-code>)'.
+ */
+ private static ArrayMap<String, Pair<Long, Long>> buildConfigMap(String configStr) {
+ ArrayMap<String, Pair<Long, Long>> configMap = new ArrayMap<>();
+ // String#split returns a non-empty array given an empty string.
+ if (configStr.isEmpty()) {
+ return configMap;
+ }
+ for (String packageEntryString : configStr.split(",")) {
+ List<String> packageAndVersions = Arrays.asList(packageEntryString.split(":", 3));
+ if (packageAndVersions.size() != 3) {
+ Slog.w(TAG, "Invalid package entry in flag 'never/always_constrain_display_apis': "
+ + packageEntryString);
+ // Skip this entry.
+ continue;
+ }
+ String packageName = packageAndVersions.get(0);
+ String minVersionCodeStr = packageAndVersions.get(1);
+ String maxVersionCodeStr = packageAndVersions.get(2);
+ try {
+ final long minVersion =
+ minVersionCodeStr.isEmpty() ? Long.MIN_VALUE : Long.parseLong(
+ minVersionCodeStr);
+ final long maxVersion =
+ maxVersionCodeStr.isEmpty() ? Long.MAX_VALUE : Long.parseLong(
+ maxVersionCodeStr);
+ Pair<Long, Long> minMaxVersionCodes = new Pair<>(minVersion, maxVersion);
+ configMap.put(packageName, minMaxVersionCodes);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Invalid APK version code in package entry: " + packageEntryString);
+ // Skip this entry.
+ }
+ }
+ return configMap;
}
/**
* Returns true if the flag with the given {@code flagName} contains a package entry that
* matches the given {@code applicationInfo}.
*
+ * @param configMap the map representing the current configuration value to examine
* @param applicationInfo Information about the application/package.
*/
- private static boolean flagHasMatchingPackageEntry(String flagName,
+ private static boolean flagHasMatchingPackageEntry(Map<String, Pair<Long, Long>> configMap,
ApplicationInfo applicationInfo) {
- String configStr = DeviceConfig.getString(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
- flagName, /* defaultValue= */ "");
-
- // String#split returns a non-empty array given an empty string.
- if (configStr.isEmpty()) {
+ if (configMap.isEmpty()) {
return false;
}
-
- for (String packageEntryString : configStr.split(",")) {
- if (matchesApplicationInfo(packageEntryString, applicationInfo)) {
- return true;
- }
+ if (!configMap.containsKey(applicationInfo.packageName)) {
+ return false;
}
-
- return false;
+ return matchesApplicationInfo(configMap.get(applicationInfo.packageName), applicationInfo);
}
/**
- * Parses the given {@code packageEntryString} and returns true if {@code
- * applicationInfo.packageName} matches the package name in the config and {@code
- * applicationInfo.longVersionCode} is within the version range in the config.
+ * Parses the given {@code minMaxVersionCodes} and returns true if {@code
+ * applicationInfo.longVersionCode} is within the version range in the pair.
+ * Returns false otherwise.
*
- * <p>Logs a warning and returns false in case the given {@code packageEntryString} is invalid.
- *
- * @param packageEntryStr A package entry expected to be in the format
- * '<package-name>:<min-version-code>?:<max-version-code>?'.
+ * @param minMaxVersionCodes A pair expected to be in the format
+ * 'Pair(<min-version-code>, <max-version-code>)'.
* @param applicationInfo Information about the application/package.
*/
- private static boolean matchesApplicationInfo(String packageEntryStr,
+ private static boolean matchesApplicationInfo(Pair<Long, Long> minMaxVersionCodes,
ApplicationInfo applicationInfo) {
- List<String> packageAndVersions = Arrays.asList(packageEntryStr.split(":", 3));
- if (packageAndVersions.size() != 3) {
- Slog.w(TAG, "Invalid package entry in flag 'never_constrain_display_apis': "
- + packageEntryStr);
- return false;
- }
- String packageName = packageAndVersions.get(0);
- String minVersionCodeStr = packageAndVersions.get(1);
- String maxVersionCodeStr = packageAndVersions.get(2);
-
- if (!packageName.equals(applicationInfo.packageName)) {
- return false;
- }
- long version = applicationInfo.longVersionCode;
- try {
- if (!minVersionCodeStr.isEmpty() && version < Long.parseLong(minVersionCodeStr)) {
- return false;
- }
- if (!maxVersionCodeStr.isEmpty() && version > Long.parseLong(maxVersionCodeStr)) {
- return false;
- }
- } catch (NumberFormatException e) {
- Slog.w(TAG, "Invalid APK version code in package entry: " + packageEntryStr);
- return false;
- }
- return true;
+ return applicationInfo.longVersionCode >= minMaxVersionCodes.first
+ && applicationInfo.longVersionCode <= minMaxVersionCodes.second;
}
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index ae5f71bc..617d3ab 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -41,7 +41,7 @@
import android.content.pm.PackageInstaller.SessionCallback;
import android.content.pm.PackageInstaller.SessionCallbackDelegate;
import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.PackageManager.ApplicationInfoFlagsBits;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -1012,7 +1012,7 @@
* isn't enabled.
*/
public ApplicationInfo getApplicationInfo(@NonNull String packageName,
- @ApplicationInfoFlags int flags, @NonNull UserHandle user)
+ @ApplicationInfoFlagsBits int flags, @NonNull UserHandle user)
throws PackageManager.NameNotFoundException {
Objects.requireNonNull(packageName, "packageName");
Objects.requireNonNull(user, "user");
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3bf5f31..f984f43 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -22,6 +22,7 @@
import android.annotation.DrawableRes;
import android.annotation.IntDef;
import android.annotation.IntRange;
+import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -655,7 +656,7 @@
*/
/** @hide */
- @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+ @LongDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
GET_ACTIVITIES,
GET_CONFIGURATIONS,
GET_GIDS,
@@ -685,10 +686,10 @@
GET_ATTRIBUTIONS,
})
@Retention(RetentionPolicy.SOURCE)
- public @interface PackageInfoFlags {}
+ public @interface PackageInfoFlagsBits {}
/** @hide */
- @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+ @LongDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
GET_META_DATA,
GET_SHARED_LIBRARY_FILES,
MATCH_UNINSTALLED_PACKAGES,
@@ -704,10 +705,10 @@
MATCH_APEX,
})
@Retention(RetentionPolicy.SOURCE)
- public @interface ApplicationInfoFlags {}
+ public @interface ApplicationInfoFlagsBits {}
/** @hide */
- @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+ @LongDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
GET_META_DATA,
GET_SHARED_LIBRARY_FILES,
MATCH_ALL,
@@ -727,10 +728,10 @@
GET_UNINSTALLED_PACKAGES,
})
@Retention(RetentionPolicy.SOURCE)
- public @interface ComponentInfoFlags {}
+ public @interface ComponentInfoFlagsBits {}
/** @hide */
- @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+ @LongDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
GET_META_DATA,
GET_RESOLVED_FILTER,
GET_SHARED_LIBRARY_FILES,
@@ -750,7 +751,7 @@
GET_UNINSTALLED_PACKAGES,
})
@Retention(RetentionPolicy.SOURCE)
- public @interface ResolveInfoFlags {}
+ public @interface ResolveInfoFlagsBits {}
/** @hide */
@IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
@@ -2762,6 +2763,16 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports FeliCa communication, which is based on
+ * ISO/IEC 18092 and JIS X 6319-4.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_FELICA = "android.hardware.felica";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device's
* {@link ActivityManager#isLowRamDevice() ActivityManager.isLowRamDevice()} method returns
* true.
@@ -3971,6 +3982,7 @@
* @hide
*/
@SdkConstant(SdkConstantType.FEATURE)
+ @TestApi
public static final String FEATURE_COMMUNAL_MODE = "android.software.communal_mode";
/** @hide */
@@ -4650,6 +4662,74 @@
*/
public static final String PROPERTY_ALLOW_ADB_BACKUP = "android.backup.ALLOW_ADB_BACKUP";
+ /**
+ * Flags class that wraps around the bitmask flags used in methods that retrieve package or
+ * application info.
+ * @hide
+ */
+ public static class Flags {
+ final long mValue;
+ protected Flags(long value) {
+ mValue = value;
+ }
+ public long getValue() {
+ return mValue;
+ }
+ }
+
+ /**
+ * Specific flags used for retrieving package info. Example:
+ * {@code PackageManager.getPackageInfo(packageName, PackageInfoFlags.of(0)}
+ */
+ public final static class PackageInfoFlags extends Flags {
+ private PackageInfoFlags(@PackageInfoFlagsBits long value) {
+ super(value);
+ }
+ @NonNull
+ public static PackageInfoFlags of(@PackageInfoFlagsBits long value) {
+ return new PackageInfoFlags(value);
+ }
+ }
+
+ /**
+ * Specific flags used for retrieving application info.
+ */
+ public final static class ApplicationInfoFlags extends Flags {
+ private ApplicationInfoFlags(@ApplicationInfoFlagsBits long value) {
+ super(value);
+ }
+ @NonNull
+ public static ApplicationInfoFlags of(@ApplicationInfoFlagsBits long value) {
+ return new ApplicationInfoFlags(value);
+ }
+ }
+
+ /**
+ * Specific flags used for retrieving component info.
+ */
+ public final static class ComponentInfoFlags extends Flags {
+ private ComponentInfoFlags(@ComponentInfoFlagsBits long value) {
+ super(value);
+ }
+ @NonNull
+ public static ComponentInfoFlags of(@ComponentInfoFlagsBits long value) {
+ return new ComponentInfoFlags(value);
+ }
+ }
+
+ /**
+ * Specific flags used for retrieving resolve info.
+ */
+ public final static class ResolveInfoFlags extends Flags {
+ private ResolveInfoFlags(@ResolveInfoFlagsBits long value) {
+ super(value);
+ }
+ @NonNull
+ public static ResolveInfoFlags of(@ResolveInfoFlagsBits long value) {
+ return new ResolveInfoFlags(value);
+ }
+ }
+
/** {@hide} */
public int getUserId() {
return UserHandle.myUserId();
@@ -4678,12 +4758,23 @@
* deleted with {@code DELETE_KEEP_DATA} flag set).
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
+ * @deprecated Use {@link #getPackageInfo(String, PackageInfoFlags)} instead.
*/
- public abstract PackageInfo getPackageInfo(@NonNull String packageName,
- @PackageInfoFlags int flags)
+ @Deprecated
+ public abstract PackageInfo getPackageInfo(@NonNull String packageName, int flags)
throws NameNotFoundException;
/**
+ * See {@link #getPackageInfo(String, int)}
+ */
+ @NonNull
+ public PackageInfo getPackageInfo(@NonNull String packageName, @NonNull PackageInfoFlags flags)
+ throws NameNotFoundException {
+ throw new UnsupportedOperationException(
+ "getPackageInfo not implemented in subclass");
+ }
+
+ /**
* Retrieve overall information about an application package that is
* installed on the system. This method can be used for retrieving
* information about packages for which multiple versions can be installed
@@ -4704,9 +4795,21 @@
* deleted with {@code DELETE_KEEP_DATA} flag set).
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
+ * @deprecated Use {@link #getPackageInfo(VersionedPackage, PackageInfoFlags)} instead.
*/
+ @Deprecated
public abstract PackageInfo getPackageInfo(@NonNull VersionedPackage versionedPackage,
- @PackageInfoFlags int flags) throws NameNotFoundException;
+ int flags) throws NameNotFoundException;
+
+ /**
+ * See {@link #getPackageInfo(VersionedPackage, int)}
+ */
+ @NonNull
+ public PackageInfo getPackageInfo(@NonNull VersionedPackage versionedPackage,
+ @NonNull PackageInfoFlags flags) throws NameNotFoundException {
+ throw new UnsupportedOperationException(
+ "getPackageInfo not implemented in subclass");
+ }
/**
* Retrieve overall information about an application package that is
@@ -4725,13 +4828,27 @@
* deleted with {@code DELETE_KEEP_DATA} flag set).
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
+ * @deprecated Use {@link #getPackageInfoAsUser(String, PackageInfoFlags, int)} instead.
* @hide
*/
+ @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
@UnsupportedAppUsage
public abstract PackageInfo getPackageInfoAsUser(@NonNull String packageName,
- @PackageInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException;
+ int flags, @UserIdInt int userId) throws NameNotFoundException;
+
+ /**
+ * See {@link #getPackageInfoAsUser(String, int, int)}
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ @NonNull
+ public PackageInfo getPackageInfoAsUser(@NonNull String packageName,
+ @NonNull PackageInfoFlags flags, @UserIdInt int userId) throws NameNotFoundException {
+ throw new UnsupportedOperationException(
+ "getPackageInfoAsUser not implemented in subclass");
+ }
/**
* Map from the current package names in use on the device to whatever
@@ -4854,11 +4971,23 @@
* none.
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
+ * @deprecated Use {@link #getPackageGids(String, PackageInfoFlags)} instead.
*/
- public abstract int[] getPackageGids(@NonNull String packageName, @PackageInfoFlags int flags)
+ @Deprecated
+ public abstract int[] getPackageGids(@NonNull String packageName, int flags)
throws NameNotFoundException;
/**
+ * See {@link #getPackageGids(String, int)}.
+ */
+ @Nullable
+ public int[] getPackageGids(@NonNull String packageName, @NonNull PackageInfoFlags flags)
+ throws NameNotFoundException {
+ throw new UnsupportedOperationException(
+ "getPackageGids not implemented in subclass");
+ }
+
+ /**
* Return the UID associated with the given package name.
* <p>
* Note that the same package will have different UIDs under different
@@ -4869,11 +4998,22 @@
* @return Returns an integer UID who owns the given package name.
* @throws NameNotFoundException if a package with the given name can not be
* found on the system.
+ * @deprecated Use {@link #getPackageUid(String, PackageInfoFlags)} instead.
*/
- public abstract int getPackageUid(@NonNull String packageName, @PackageInfoFlags int flags)
+ @Deprecated
+ public abstract int getPackageUid(@NonNull String packageName, int flags)
throws NameNotFoundException;
/**
+ * See {@link #getPackageUid(String, int)}.
+ */
+ public int getPackageUid(@NonNull String packageName, @NonNull PackageInfoFlags flags)
+ throws NameNotFoundException {
+ throw new UnsupportedOperationException(
+ "getPackageUid not implemented in subclass");
+ }
+
+ /**
* Return the UID associated with the given package name.
* <p>
* Note that the same package will have different UIDs under different
@@ -4904,12 +5044,24 @@
* @return Returns an integer UID who owns the given package name.
* @throws NameNotFoundException if a package with the given name can not be
* found on the system.
+ * @deprecated Use {@link #getPackageUidAsUser(String, PackageInfoFlags, int)} instead.
* @hide
*/
+ @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract int getPackageUidAsUser(@NonNull String packageName,
- @PackageInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException;
+ int flags, @UserIdInt int userId) throws NameNotFoundException;
+
+ /**
+ * See {@link #getPackageUidAsUser(String, int, int)}.
+ * @hide
+ */
+ public int getPackageUidAsUser(@NonNull String packageName, @NonNull PackageInfoFlags flags,
+ @UserIdInt int userId) throws NameNotFoundException {
+ throw new UnsupportedOperationException(
+ "getPackageUidAsUser not implemented in subclass");
+ }
/**
* Retrieve all of the information we know about a particular permission.
@@ -5035,17 +5187,42 @@
* which had been deleted with {@code DELETE_KEEP_DATA} flag set).
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
+ * @deprecated Use {@link #getApplicationInfo(String, ApplicationInfoFlags)} instead.
*/
@NonNull
+ @Deprecated
public abstract ApplicationInfo getApplicationInfo(@NonNull String packageName,
- @ApplicationInfoFlags int flags) throws NameNotFoundException;
+ int flags) throws NameNotFoundException;
- /** {@hide} */
+ /**
+ * See {@link #getApplicationInfo(String, int)}.
+ */
+ @NonNull
+ public ApplicationInfo getApplicationInfo(@NonNull String packageName,
+ @NonNull ApplicationInfoFlags flags) throws NameNotFoundException {
+ throw new UnsupportedOperationException(
+ "getApplicationInfo not implemented in subclass");
+ }
+
+ /**
+ * @deprecated Use {@link #getApplicationInfoAsUser(String, ApplicationInfoFlags, int)} instead.
+ * {@hide}
+ */
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage
+ @Deprecated
public abstract ApplicationInfo getApplicationInfoAsUser(@NonNull String packageName,
- @ApplicationInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException;
+ int flags, @UserIdInt int userId) throws NameNotFoundException;
+
+ /** {@hide} */
+ @NonNull
+ public ApplicationInfo getApplicationInfoAsUser(@NonNull String packageName,
+ @NonNull ApplicationInfoFlags flags, @UserIdInt int userId)
+ throws NameNotFoundException {
+ throw new UnsupportedOperationException(
+ "getApplicationInfoAsUser not implemented in subclass");
+ }
/**
* Retrieve all of the information we know about a particular
@@ -5063,13 +5240,29 @@
* which had been deleted with {@code DELETE_KEEP_DATA} flag set).
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
+ * @deprecated Use {@link #getApplicationInfoAsUser(String, ApplicationInfoFlags, UserHandle)}
+ * instead.
+ * @hide
+ */
+ @NonNull
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ @SystemApi
+ @Deprecated
+ public ApplicationInfo getApplicationInfoAsUser(@NonNull String packageName,
+ int flags, @NonNull UserHandle user)
+ throws NameNotFoundException {
+ return getApplicationInfoAsUser(packageName, flags, user.getIdentifier());
+ }
+
+ /**
+ * See {@link #getApplicationInfoAsUser(String, int, UserHandle)}.
* @hide
*/
@NonNull
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
@SystemApi
public ApplicationInfo getApplicationInfoAsUser(@NonNull String packageName,
- @ApplicationInfoFlags int flags, @NonNull UserHandle user)
+ @NonNull ApplicationInfoFlags flags, @NonNull UserHandle user)
throws NameNotFoundException {
return getApplicationInfoAsUser(packageName, flags, user.getIdentifier());
}
@@ -5095,10 +5288,22 @@
* activity.
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
+ * @deprecated Use {@link #getActivityInfo(ComponentName, ComponentInfoFlags)} instead.
*/
+ @Deprecated
@NonNull
public abstract ActivityInfo getActivityInfo(@NonNull ComponentName component,
- @ComponentInfoFlags int flags) throws NameNotFoundException;
+ int flags) throws NameNotFoundException;
+
+ /**
+ * See {@link #getActivityInfo(ComponentName, int)}.
+ */
+ @NonNull
+ public ActivityInfo getActivityInfo(@NonNull ComponentName component,
+ @NonNull ComponentInfoFlags flags) throws NameNotFoundException {
+ throw new UnsupportedOperationException(
+ "getActivityInfo not implemented in subclass");
+ }
/**
* Retrieve all of the information we know about a particular receiver
@@ -5112,10 +5317,22 @@
* receiver.
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
+ * @deprecated Use {@link #getReceiverInfo(ComponentName, ComponentInfoFlags)} instead.
*/
+ @Deprecated
@NonNull
public abstract ActivityInfo getReceiverInfo(@NonNull ComponentName component,
- @ComponentInfoFlags int flags) throws NameNotFoundException;
+ int flags) throws NameNotFoundException;
+
+ /**
+ * See {@link #getReceiverInfo(ComponentName, int)}.
+ */
+ @NonNull
+ public ActivityInfo getReceiverInfo(@NonNull ComponentName component,
+ @NonNull ComponentInfoFlags flags) throws NameNotFoundException {
+ throw new UnsupportedOperationException(
+ "getReceiverInfo not implemented in subclass");
+ }
/**
* Retrieve all of the information we know about a particular service class.
@@ -5127,10 +5344,22 @@
* @return A {@link ServiceInfo} object containing information about the
* service.
* @throws NameNotFoundException if the component cannot be found on the system.
+ * @deprecated Use {@link #getServiceInfo(ComponentName, ComponentInfoFlags)} instead.
*/
+ @Deprecated
@NonNull
public abstract ServiceInfo getServiceInfo(@NonNull ComponentName component,
- @ComponentInfoFlags int flags) throws NameNotFoundException;
+ int flags) throws NameNotFoundException;
+
+ /**
+ * See {@link #getServiceInfo(ComponentName, int)}.
+ */
+ @NonNull
+ public ServiceInfo getServiceInfo(@NonNull ComponentName component,
+ @NonNull ComponentInfoFlags flags) throws NameNotFoundException {
+ throw new UnsupportedOperationException(
+ "getServiceInfo not implemented in subclass");
+ }
/**
* Retrieve all of the information we know about a particular content
@@ -5144,10 +5373,22 @@
* provider.
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
+ * @deprecated Use {@link #getProviderInfo(ComponentName, ComponentInfoFlags)} instead.
*/
+ @Deprecated
@NonNull
public abstract ProviderInfo getProviderInfo(@NonNull ComponentName component,
- @ComponentInfoFlags int flags) throws NameNotFoundException;
+ int flags) throws NameNotFoundException;
+
+ /**
+ * See {@link #getProviderInfo(ComponentName, int)}.
+ */
+ @NonNull
+ public ProviderInfo getProviderInfo(@NonNull ComponentName component,
+ @NonNull ComponentInfoFlags flags) throws NameNotFoundException {
+ throw new UnsupportedOperationException(
+ "getProviderInfo not implemented in subclass");
+ }
/**
* Retrieve information for a particular module.
@@ -5192,9 +5433,21 @@
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
* deleted with {@code DELETE_KEEP_DATA} flag set).
+ * @deprecated Use {@link #getInstalledPackages(PackageInfoFlags)} instead.
+ */
+ @Deprecated
+ @NonNull
+ public abstract List<PackageInfo> getInstalledPackages(int flags);
+
+ /**
+ * See {@link #getInstalledPackages(int)}.
+ * @param flags
*/
@NonNull
- public abstract List<PackageInfo> getInstalledPackages(@PackageInfoFlags int flags);
+ public List<PackageInfo> getInstalledPackages(@NonNull PackageInfoFlags flags) {
+ throw new UnsupportedOperationException(
+ "getInstalledPackages not implemented in subclass");
+ }
/**
* Return a List of all installed packages that are currently holding any of
@@ -5210,10 +5463,22 @@
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
* deleted with {@code DELETE_KEEP_DATA} flag set).
+ * @deprecated Use {@link #getPackagesHoldingPermissions(String[], PackageInfoFlags)} instead.
*/
+ @Deprecated
@NonNull
public abstract List<PackageInfo> getPackagesHoldingPermissions(
- @NonNull String[] permissions, @PackageInfoFlags int flags);
+ @NonNull String[] permissions, int flags);
+
+ /**
+ * See {@link #getPackagesHoldingPermissions(String[], int)}.
+ */
+ @NonNull
+ public List<PackageInfo> getPackagesHoldingPermissions(
+ @NonNull String[] permissions, @NonNull PackageInfoFlags flags) {
+ throw new UnsupportedOperationException(
+ "getPackagesHoldingPermissions not implemented in subclass");
+ }
/**
* Return a List of all packages that are installed on the device, for a
@@ -5229,16 +5494,31 @@
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
* deleted with {@code DELETE_KEEP_DATA} flag set).
+ * @deprecated Use {@link #getInstalledPackagesAsUser(PackageInfoFlags, int)} instead.
* @hide
*/
+ @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@SystemApi
@RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
- public abstract List<PackageInfo> getInstalledPackagesAsUser(@PackageInfoFlags int flags,
+ public abstract List<PackageInfo> getInstalledPackagesAsUser(int flags,
@UserIdInt int userId);
/**
+ * See {@link #getInstalledPackagesAsUser(int, int)}.
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ public List<PackageInfo> getInstalledPackagesAsUser(@NonNull PackageInfoFlags flags,
+ @UserIdInt int userId) {
+ throw new UnsupportedOperationException(
+ "getApplicationInfoAsUser not implemented in subclass");
+ }
+
+ /**
* Check whether a particular package has been granted a particular
* permission.
*
@@ -5924,11 +6204,22 @@
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
* deleted with {@code DELETE_KEEP_DATA} flag set).
+ * @deprecated Use {@link #getInstalledApplications(ApplicationInfoFlags)} instead.
*/
@NonNull
- public abstract List<ApplicationInfo> getInstalledApplications(@ApplicationInfoFlags int flags);
+ @Deprecated
+ public abstract List<ApplicationInfo> getInstalledApplications(int flags);
/**
+ * See {@link #getInstalledApplications(int)}
+ * @param flags
+ */
+ @NonNull
+ public List<ApplicationInfo> getInstalledApplications(@NonNull ApplicationInfoFlags flags) {
+ throw new UnsupportedOperationException(
+ "getInstalledApplications not implemented in subclass");
+ }
+ /**
* Return a List of all application packages that are installed on the
* device, for a specific user. If flag GET_UNINSTALLED_PACKAGES has been
* set, a list of all applications including those deleted with
@@ -5946,13 +6237,27 @@
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
* deleted with {@code DELETE_KEEP_DATA} flag set).
+ * @deprecated Use {@link #getInstalledApplicationsAsUser(ApplicationInfoFlags, int)} instead.
* @hide
*/
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@TestApi
+ @Deprecated
public abstract List<ApplicationInfo> getInstalledApplicationsAsUser(
- @ApplicationInfoFlags int flags, @UserIdInt int userId);
+ int flags, @UserIdInt int userId);
+
+ /**
+ * See {@link #getInstalledApplicationsAsUser(int, int}.
+ * @hide
+ */
+ @NonNull
+ @TestApi
+ public List<ApplicationInfo> getInstalledApplicationsAsUser(
+ @NonNull ApplicationInfoFlags flags, @UserIdInt int userId) {
+ throw new UnsupportedOperationException(
+ "getInstalledApplicationsAsUser not implemented in subclass");
+ }
/**
* Gets the instant applications the user recently used.
@@ -6101,9 +6406,19 @@
* @return The shared library list.
*
* @see #MATCH_UNINSTALLED_PACKAGES
+ * @deprecated Use {@link #getSharedLibraries(PackageInfoFlags)} instead.
*/
- public abstract @NonNull List<SharedLibraryInfo> getSharedLibraries(
- @InstallFlags int flags);
+ @Deprecated
+ public abstract @NonNull List<SharedLibraryInfo> getSharedLibraries(int flags);
+
+ /**
+ * See {@link #getSharedLibraries(int)}.
+ * @param flags
+ */
+ public @NonNull List<SharedLibraryInfo> getSharedLibraries(@NonNull PackageInfoFlags flags) {
+ throw new UnsupportedOperationException(
+ "getSharedLibraries() not implemented in subclass");
+ }
/**
* Get a list of shared libraries on the device.
@@ -6118,10 +6433,22 @@
* @see #MATCH_UNINSTALLED_PACKAGES
*
* @hide
+ * @deprecated Use {@link #getSharedLibrariesAsUser(PackageInfoFlags, int)} instead.
*/
+ @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
- public abstract @NonNull List<SharedLibraryInfo> getSharedLibrariesAsUser(
- @InstallFlags int flags, @UserIdInt int userId);
+ public abstract @NonNull List<SharedLibraryInfo> getSharedLibrariesAsUser(int flags,
+ @UserIdInt int userId);
+
+ /**
+ * See {@link #getSharedLibrariesAsUser(int, int)}.
+ * @hide
+ */
+ public @NonNull List<SharedLibraryInfo> getSharedLibrariesAsUser(
+ @NonNull PackageInfoFlags flags, @UserIdInt int userId) {
+ throw new UnsupportedOperationException(
+ "getSharedLibrariesAsUser() not implemented in subclass");
+ }
/**
* Get the list of shared libraries declared by a package.
@@ -6131,13 +6458,28 @@
* @return the shared library list
*
* @hide
+ * @deprecated Use {@link #getDeclaredSharedLibraries(String, PackageInfoFlags)} instead.
*/
+ @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@RequiresPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES)
@SystemApi
public List<SharedLibraryInfo> getDeclaredSharedLibraries(@NonNull String packageName,
- @InstallFlags int flags) {
+ int flags) {
+ throw new UnsupportedOperationException(
+ "getDeclaredSharedLibraries() not implemented in subclass");
+ }
+
+ /**
+ * See {@link #getDeclaredSharedLibraries(String, int)}.
+ * @hide
+ */
+ @NonNull
+ @RequiresPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES)
+ @SystemApi
+ public List<SharedLibraryInfo> getDeclaredSharedLibraries(@NonNull String packageName,
+ @NonNull PackageInfoFlags flags) {
throw new UnsupportedOperationException(
"getDeclaredSharedLibraries() not implemented in subclass");
}
@@ -6238,10 +6580,20 @@
* matching activity was found. If multiple matching activities are
* found and there is no default set, returns a ResolveInfo object
* containing something else, such as the activity resolver.
+ * @deprecated Use {@link #resolveActivity(Intent, ResolveInfoFlags)} instead.
+ */
+ @Deprecated
+ @Nullable
+ public abstract ResolveInfo resolveActivity(@NonNull Intent intent, int flags);
+
+ /**
+ * See {@link #resolveActivity(Intent, int)}.
*/
@Nullable
- public abstract ResolveInfo resolveActivity(@NonNull Intent intent,
- @ResolveInfoFlags int flags);
+ public ResolveInfo resolveActivity(@NonNull Intent intent, @NonNull ResolveInfoFlags flags) {
+ throw new UnsupportedOperationException(
+ "resolveActivity not implemented in subclass");
+ }
/**
* Determine the best action to perform for a given Intent for a given user.
@@ -6270,12 +6622,25 @@
* found and there is no default set, returns a ResolveInfo object
* containing something else, such as the activity resolver.
* @hide
+ * @deprecated Use {@link #resolveActivityAsUser(Intent, ResolveInfoFlags, int)} instead.
*/
+ @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@Nullable
@UnsupportedAppUsage
public abstract ResolveInfo resolveActivityAsUser(@NonNull Intent intent,
- @ResolveInfoFlags int flags, @UserIdInt int userId);
+ int flags, @UserIdInt int userId);
+
+ /**
+ * See {@link #resolveActivityAsUser(Intent, int, int)}.
+ * @hide
+ */
+ @Nullable
+ public ResolveInfo resolveActivityAsUser(@NonNull Intent intent,
+ @NonNull ResolveInfoFlags flags, @UserIdInt int userId) {
+ throw new UnsupportedOperationException(
+ "resolveActivityAsUser not implemented in subclass");
+ }
/**
* Retrieve all activities that can be performed for the given intent.
@@ -6291,10 +6656,21 @@
* words, the first item is what would be returned by
* {@link #resolveActivity}. If there are no matching activities, an
* empty list is returned.
+ * @deprecated Use {@link #queryIntentActivities(Intent, ResolveInfoFlags)} instead.
+ */
+ @Deprecated
+ @NonNull
+ public abstract List<ResolveInfo> queryIntentActivities(@NonNull Intent intent, int flags);
+
+ /**
+ * See {@link #queryIntentActivities(Intent, int)}.
*/
@NonNull
- public abstract List<ResolveInfo> queryIntentActivities(@NonNull Intent intent,
- @ResolveInfoFlags int flags);
+ public List<ResolveInfo> queryIntentActivities(@NonNull Intent intent,
+ @NonNull ResolveInfoFlags flags) {
+ throw new UnsupportedOperationException(
+ "queryIntentActivities not implemented in subclass");
+ }
/**
* Retrieve all activities that can be performed for the given intent, for a
@@ -6312,12 +6688,25 @@
* {@link #resolveActivity}. If there are no matching activities, an
* empty list is returned.
* @hide
+ * @deprecated Use {@link #queryIntentActivitiesAsUser(Intent, ResolveInfoFlags, int)} instead.
*/
+ @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage
public abstract List<ResolveInfo> queryIntentActivitiesAsUser(@NonNull Intent intent,
- @ResolveInfoFlags int flags, @UserIdInt int userId);
+ int flags, @UserIdInt int userId);
+
+ /**
+ * See {@link #queryIntentActivitiesAsUser(Intent, int, int)}.
+ * @hide
+ */
+ @NonNull
+ public List<ResolveInfo> queryIntentActivitiesAsUser(@NonNull Intent intent,
+ @NonNull ResolveInfoFlags flags, @UserIdInt int userId) {
+ throw new UnsupportedOperationException(
+ "queryIntentActivitiesAsUser not implemented in subclass");
+ }
/**
* Retrieve all activities that can be performed for the given intent, for a
@@ -6336,13 +6725,28 @@
* {@link #resolveActivity}. If there are no matching activities, an
* empty list is returned.
* @hide
+ * @deprecated Use {@link #queryIntentActivitiesAsUser(Intent, ResolveInfoFlags, UserHandle)}
+ * instead.
*/
+ @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
@SystemApi
public List<ResolveInfo> queryIntentActivitiesAsUser(@NonNull Intent intent,
- @ResolveInfoFlags int flags, @NonNull UserHandle user) {
+ int flags, @NonNull UserHandle user) {
+ return queryIntentActivitiesAsUser(intent, flags, user.getIdentifier());
+ }
+
+ /**
+ * See {@link #queryIntentActivitiesAsUser(Intent, int, UserHandle)}.
+ * @hide
+ */
+ @NonNull
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ @SystemApi
+ public List<ResolveInfo> queryIntentActivitiesAsUser(@NonNull Intent intent,
+ @NonNull ResolveInfoFlags flags, @NonNull UserHandle user) {
return queryIntentActivitiesAsUser(intent, flags, user.getIdentifier());
}
@@ -6370,10 +6774,24 @@
* activities that can handle <var>intent</var> but did not get
* included by one of the <var>specifics</var> intents. If there are
* no matching activities, an empty list is returned.
+ * @deprecated Use {@link #queryIntentActivityOptions(ComponentName, List, Intent,
+ * ResolveInfoFlags)} instead.
*/
+ @Deprecated
@NonNull
public abstract List<ResolveInfo> queryIntentActivityOptions(@Nullable ComponentName caller,
- @Nullable Intent[] specifics, @NonNull Intent intent, @ResolveInfoFlags int flags);
+ @Nullable Intent[] specifics, @NonNull Intent intent, int flags);
+
+ /**
+ * See {@link #queryIntentActivityOptions(ComponentName, Intent[], Intent, int)}.
+ */
+ @NonNull
+ public List<ResolveInfo> queryIntentActivityOptions(@Nullable ComponentName caller,
+ @Nullable List<Intent> specifics, @NonNull Intent intent,
+ @NonNull ResolveInfoFlags flags) {
+ throw new UnsupportedOperationException(
+ "queryIntentActivityOptions not implemented in subclass");
+ }
/**
* Retrieve all receivers that can handle a broadcast of the given intent.
@@ -6383,10 +6801,21 @@
* @return Returns a List of ResolveInfo objects containing one entry for
* each matching receiver, ordered from best to worst. If there are
* no matching receivers, an empty list or null is returned.
+ * @deprecated Use {@link #queryBroadcastReceivers(Intent, ResolveInfoFlags)} instead.
+ */
+ @Deprecated
+ @NonNull
+ public abstract List<ResolveInfo> queryBroadcastReceivers(@NonNull Intent intent, int flags);
+
+ /**
+ * See {@link #queryBroadcastReceivers(Intent, int)}.
*/
@NonNull
- public abstract List<ResolveInfo> queryBroadcastReceivers(@NonNull Intent intent,
- @ResolveInfoFlags int flags);
+ public List<ResolveInfo> queryBroadcastReceivers(@NonNull Intent intent,
+ @NonNull ResolveInfoFlags flags) {
+ throw new UnsupportedOperationException(
+ "queryBroadcastReceivers not implemented in subclass");
+ }
/**
* Retrieve all receivers that can handle a broadcast of the given intent,
@@ -6399,24 +6828,53 @@
* each matching receiver, ordered from best to worst. If there are
* no matching receivers, an empty list or null is returned.
* @hide
+ * @deprecated Use {@link #queryBroadcastReceiversAsUser(Intent, ResolveInfoFlags, UserHandle)}
+ * instead.
*/
+ @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@SystemApi
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
public List<ResolveInfo> queryBroadcastReceiversAsUser(@NonNull Intent intent,
- @ResolveInfoFlags int flags, UserHandle userHandle) {
+ int flags, UserHandle userHandle) {
+ return queryBroadcastReceiversAsUser(intent, flags, userHandle.getIdentifier());
+ }
+
+ /**
+ * See {@link #queryBroadcastReceiversAsUser(Intent, int, UserHandle)}.
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ public List<ResolveInfo> queryBroadcastReceiversAsUser(@NonNull Intent intent,
+ @NonNull ResolveInfoFlags flags, @NonNull UserHandle userHandle) {
return queryBroadcastReceiversAsUser(intent, flags, userHandle.getIdentifier());
}
/**
* @hide
+ * @deprecated Use {@link #queryBroadcastReceiversAsUser(Intent, ResolveInfoFlags, int)}
+ * instead.
*/
+ @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage
public abstract List<ResolveInfo> queryBroadcastReceiversAsUser(@NonNull Intent intent,
- @ResolveInfoFlags int flags, @UserIdInt int userId);
+ int flags, @UserIdInt int userId);
+
+ /**
+ * See {@link #queryBroadcastReceiversAsUser(Intent, int, int)}.
+ * @hide
+ */
+ @NonNull
+ public List<ResolveInfo> queryBroadcastReceiversAsUser(@NonNull Intent intent,
+ @NonNull ResolveInfoFlags flags, @UserIdInt int userId) {
+ throw new UnsupportedOperationException(
+ "queryBroadcastReceiversAsUser not implemented in subclass");
+ }
/** @deprecated @hide */
@@ -6424,7 +6882,7 @@
@Deprecated
@UnsupportedAppUsage
public List<ResolveInfo> queryBroadcastReceivers(@NonNull Intent intent,
- @ResolveInfoFlags int flags, @UserIdInt int userId) {
+ int flags, @UserIdInt int userId) {
final String msg = "Shame on you for calling the hidden API "
+ "queryBroadcastReceivers(). Shame!";
if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.O) {
@@ -6444,17 +6902,41 @@
* @return Returns a ResolveInfo object containing the final service intent
* that was determined to be the best action. Returns null if no
* matching service was found.
+ * @deprecated Use {@link #resolveService(Intent, ResolveInfoFlags)} instead.
+ */
+ @Deprecated
+ @Nullable
+ public abstract ResolveInfo resolveService(@NonNull Intent intent, int flags);
+
+ /**
+ * See {@link #resolveService(Intent, int)}.
*/
@Nullable
- public abstract ResolveInfo resolveService(@NonNull Intent intent, @ResolveInfoFlags int flags);
+ public ResolveInfo resolveService(@NonNull Intent intent, @NonNull ResolveInfoFlags flags) {
+ throw new UnsupportedOperationException(
+ "resolveService not implemented in subclass");
+ }
/**
* @hide
+ * @deprecated Use {@link #resolveServiceAsUser(Intent, ResolveInfoFlags, int)} instead.
*/
+ @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@Nullable
public abstract ResolveInfo resolveServiceAsUser(@NonNull Intent intent,
- @ResolveInfoFlags int flags, @UserIdInt int userId);
+ int flags, @UserIdInt int userId);
+
+ /**
+ * See {@link #resolveServiceAsUser(Intent, int, int)}.
+ * @hide
+ */
+ @Nullable
+ public ResolveInfo resolveServiceAsUser(@NonNull Intent intent,
+ @NonNull ResolveInfoFlags flags, @UserIdInt int userId) {
+ throw new UnsupportedOperationException(
+ "resolveServiceAsUser not implemented in subclass");
+ }
/**
* Retrieve all services that can match the given intent.
@@ -6466,10 +6948,22 @@
* words, the first item is what would be returned by
* {@link #resolveService}. If there are no matching services, an
* empty list or null is returned.
+ * @deprecated Use {@link #queryIntentServices(Intent, ResolveInfoFlags)} instead.
*/
+ @Deprecated
@NonNull
public abstract List<ResolveInfo> queryIntentServices(@NonNull Intent intent,
- @ResolveInfoFlags int flags);
+ int flags);
+
+ /**
+ * See {@link #queryIntentServices(Intent, int)}.
+ */
+ @NonNull
+ public List<ResolveInfo> queryIntentServices(@NonNull Intent intent,
+ @NonNull ResolveInfoFlags flags) {
+ throw new UnsupportedOperationException(
+ "queryIntentServices not implemented in subclass");
+ }
/**
* Retrieve all services that can match the given intent for a given user.
@@ -6483,12 +6977,25 @@
* {@link #resolveService}. If there are no matching services, an
* empty list or null is returned.
* @hide
+ * @deprecated Use {@link #queryIntentServicesAsUser(Intent, ResolveInfoFlags, int)} instead.
*/
+ @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage
public abstract List<ResolveInfo> queryIntentServicesAsUser(@NonNull Intent intent,
- @ResolveInfoFlags int flags, @UserIdInt int userId);
+ int flags, @UserIdInt int userId);
+
+ /**
+ * See {@link #queryIntentServicesAsUser(Intent, int, int)}.
+ * @hide
+ */
+ @NonNull
+ public List<ResolveInfo> queryIntentServicesAsUser(@NonNull Intent intent,
+ @NonNull ResolveInfoFlags flags, @UserIdInt int userId) {
+ throw new UnsupportedOperationException(
+ "queryIntentServicesAsUser not implemented in subclass");
+ }
/**
* Retrieve all services that can match the given intent for a given user.
@@ -6502,14 +7009,60 @@
* {@link #resolveService}. If there are no matching services, an
* empty list or null is returned.
* @hide
+ * @deprecated Use {@link #queryIntentServicesAsUser(Intent, ResolveInfoFlags, UserHandle)}
+ * instead.
+ */
+ @Deprecated
+ @NonNull
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ @SystemApi
+ public List<ResolveInfo> queryIntentServicesAsUser(@NonNull Intent intent,
+ int flags, @NonNull UserHandle user) {
+ return queryIntentServicesAsUser(intent, flags, user.getIdentifier());
+ }
+
+ /**
+ * See {@link #queryIntentServicesAsUser(Intent, int, UserHandle)}.
+ * @hide
*/
@NonNull
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
@SystemApi
public List<ResolveInfo> queryIntentServicesAsUser(@NonNull Intent intent,
- @ResolveInfoFlags int flags, @NonNull UserHandle user) {
+ @NonNull ResolveInfoFlags flags, @NonNull UserHandle user) {
return queryIntentServicesAsUser(intent, flags, user.getIdentifier());
}
+ /**
+ * Retrieve all providers that can match the given intent.
+ *
+ * @param intent An intent containing all of the desired specification
+ * (action, data, type, category, and/or component).
+ * @param flags Additional option flags to modify the data returned.
+ * @param userId The user id.
+ * @return Returns a List of ResolveInfo objects containing one entry for
+ * each matching provider, ordered from best to worst. If there are
+ * no matching services, an empty list or null is returned.
+ * @hide
+ * @deprecated Use {@link #queryIntentContentProvidersAsUser(Intent, ResolveInfoFlags, int)}
+ * instead.
+ */
+ @Deprecated
+ @SuppressWarnings("HiddenAbstractMethod")
+ @NonNull
+ @UnsupportedAppUsage
+ public abstract List<ResolveInfo> queryIntentContentProvidersAsUser(
+ @NonNull Intent intent, int flags, @UserIdInt int userId);
+
+ /**
+ * See {@link #queryIntentContentProvidersAsUser(Intent, int, int)}.
+ * @hide
+ */
+ @NonNull
+ protected List<ResolveInfo> queryIntentContentProvidersAsUser(
+ @NonNull Intent intent, @NonNull ResolveInfoFlags flags, @UserIdInt int userId) {
+ throw new UnsupportedOperationException(
+ "queryIntentContentProvidersAsUser not implemented in subclass");
+ }
/**
* Retrieve all providers that can match the given intent.
@@ -6517,35 +7070,32 @@
* @param intent An intent containing all of the desired specification
* (action, data, type, category, and/or component).
* @param flags Additional option flags to modify the data returned.
- * @param userId The user id.
- * @return Returns a List of ResolveInfo objects containing one entry for
- * each matching provider, ordered from best to worst. If there are
- * no matching services, an empty list or null is returned.
- * @hide
- */
- @SuppressWarnings("HiddenAbstractMethod")
- @NonNull
- @UnsupportedAppUsage
- public abstract List<ResolveInfo> queryIntentContentProvidersAsUser(
- @NonNull Intent intent, @ResolveInfoFlags int flags, @UserIdInt int userId);
-
- /**
- * Retrieve all providers that can match the given intent.
- *
- * @param intent An intent containing all of the desired specification
- * (action, data, type, category, and/or component).
- * @param flags Additional option flags to modify the data returned.
* @param user The user being queried.
* @return Returns a List of ResolveInfo objects containing one entry for
* each matching provider, ordered from best to worst. If there are
* no matching services, an empty list or null is returned.
* @hide
+ * @deprecated Use {@link #queryIntentContentProvidersAsUser(Intent, ResolveInfoFlags,
+ * UserHandle)} instead.
+ */
+ @Deprecated
+ @NonNull
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ @SystemApi
+ public List<ResolveInfo> queryIntentContentProvidersAsUser(@NonNull Intent intent,
+ int flags, @NonNull UserHandle user) {
+ return queryIntentContentProvidersAsUser(intent, flags, user.getIdentifier());
+ }
+
+ /**
+ * See {@link #queryIntentContentProvidersAsUser(Intent, int, UserHandle)}.
+ * @hide
*/
@NonNull
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
@SystemApi
public List<ResolveInfo> queryIntentContentProvidersAsUser(@NonNull Intent intent,
- @ResolveInfoFlags int flags, @NonNull UserHandle user) {
+ @NonNull ResolveInfoFlags flags, @NonNull UserHandle user) {
return queryIntentContentProvidersAsUser(intent, flags, user.getIdentifier());
}
@@ -6558,10 +7108,22 @@
* @return Returns a List of ResolveInfo objects containing one entry for
* each matching provider, ordered from best to worst. If there are
* no matching services, an empty list or null is returned.
+ * @deprecated Use {@link #queryIntentContentProviders(Intent, ResolveInfoFlags)} instead.
*/
+ @Deprecated
@NonNull
public abstract List<ResolveInfo> queryIntentContentProviders(@NonNull Intent intent,
- @ResolveInfoFlags int flags);
+ int flags);
+
+ /**
+ * See {@link #queryIntentContentProviders(Intent, int)}.
+ */
+ @NonNull
+ public List<ResolveInfo> queryIntentContentProviders(@NonNull Intent intent,
+ @NonNull ResolveInfoFlags flags) {
+ throw new UnsupportedOperationException(
+ "queryIntentContentProviders not implemented in subclass");
+ }
/**
* Find a single content provider by its authority.
@@ -6576,10 +7138,22 @@
* @param flags Additional option flags to modify the data returned.
* @return A {@link ProviderInfo} object containing information about the
* provider. If a provider was not found, returns null.
+ * @deprecated Use {@link #resolveContentProvider(String, ComponentInfoFlags)} instead.
*/
+ @Deprecated
@Nullable
public abstract ProviderInfo resolveContentProvider(@NonNull String authority,
- @ComponentInfoFlags int flags);
+ int flags);
+
+ /**
+ * See {@link #resolveContentProvider(String, int)}.
+ */
+ @Nullable
+ public ProviderInfo resolveContentProvider(@NonNull String authority,
+ @NonNull ComponentInfoFlags flags) {
+ throw new UnsupportedOperationException(
+ "resolveContentProvider not implemented in subclass");
+ }
/**
* Find a single content provider by its base path name.
@@ -6590,12 +7164,25 @@
* @return A {@link ProviderInfo} object containing information about the
* provider. If a provider was not found, returns null.
* @hide
+ * @deprecated Use {@link #resolveContentProviderAsUser(String, ComponentInfoFlags, int)}
+ * instead.
*/
@SuppressWarnings("HiddenAbstractMethod")
@Nullable
@UnsupportedAppUsage
public abstract ProviderInfo resolveContentProviderAsUser(@NonNull String providerName,
- @ComponentInfoFlags int flags, @UserIdInt int userId);
+ int flags, @UserIdInt int userId);
+
+ /**
+ * See {@link #resolveContentProviderAsUser(String, int, int)}.
+ * @hide
+ */
+ @Nullable
+ public ProviderInfo resolveContentProviderAsUser(@NonNull String providerName,
+ @NonNull ComponentInfoFlags flags, @UserIdInt int userId) {
+ throw new UnsupportedOperationException(
+ "resolveContentProviderAsUser not implemented in subclass");
+ }
/**
* Retrieve content provider information.
@@ -6613,10 +7200,22 @@
* each provider either matching <var>processName</var> or, if
* <var>processName</var> is null, all known content providers.
* <em>If there are no matching providers, null is returned.</em>
+ * @deprecated Use {@link #queryContentProviders(String, int, ComponentInfoFlags)} instead.
*/
+ @Deprecated
@NonNull
public abstract List<ProviderInfo> queryContentProviders(
- @Nullable String processName, int uid, @ComponentInfoFlags int flags);
+ @Nullable String processName, int uid, int flags);
+
+ /**
+ * See {@link #queryContentProviders(String, int, int)}.
+ */
+ @NonNull
+ public List<ProviderInfo> queryContentProviders(
+ @Nullable String processName, int uid, @NonNull ComponentInfoFlags flags) {
+ throw new UnsupportedOperationException(
+ "queryContentProviders not implemented in subclass");
+ }
/**
* Same as {@link #queryContentProviders}, except when {@code metaDataKey} is not null,
@@ -6632,10 +7231,24 @@
* {@link #queryIntentContentProviders} for that.
*
* @hide
+ * @deprecated Use {@link #queryContentProviders(String, int, ComponentInfoFlags, String)}
+ * instead.
+ */
+ @Deprecated
+ @NonNull
+ public List<ProviderInfo> queryContentProviders(@Nullable String processName,
+ int uid, int flags, String metaDataKey) {
+ // Provide the default implementation for mocks.
+ return queryContentProviders(processName, uid, flags);
+ }
+
+ /**
+ * See {@link #queryContentProviders(String, int, int, String)}.
+ * @hide
*/
@NonNull
public List<ProviderInfo> queryContentProviders(@Nullable String processName,
- int uid, @ComponentInfoFlags int flags, String metaDataKey) {
+ int uid, @NonNull ComponentInfoFlags flags, @Nullable String metaDataKey) {
// Provide the default implementation for mocks.
return queryContentProviders(processName, uid, flags);
}
@@ -7171,10 +7784,21 @@
* @param flags Additional option flags to modify the data returned.
* @return A PackageInfo object containing information about the package
* archive. If the package could not be parsed, returns null.
+ * @deprecated Use {@link #getPackageArchiveInfo(String, PackageInfoFlags)} instead.
+ */
+ @Deprecated
+ @Nullable
+ public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath, int flags) {
+ throw new UnsupportedOperationException(
+ "getPackageArchiveInfo() not implemented in subclass");
+ }
+
+ /**
+ * See {@link #getPackageArchiveInfo(String, int)}.
*/
@Nullable
public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath,
- @PackageInfoFlags int flags) {
+ @NonNull PackageInfoFlags flags) {
throw new UnsupportedOperationException(
"getPackageArchiveInfo() not implemented in subclass");
}
@@ -7750,7 +8374,7 @@
*/
@NonNull
@Deprecated
- public abstract List<PackageInfo> getPreferredPackages(@PackageInfoFlags int flags);
+ public abstract List<PackageInfo> getPreferredPackages(int flags);
/**
* Add a new preferred activity mapping to the system. This will be used
@@ -9426,10 +10050,11 @@
private static final class ApplicationInfoQuery {
final String packageName;
- final int flags;
+ final long flags;
final int userId;
- ApplicationInfoQuery(@Nullable String packageName, int flags, int userId) {
+ ApplicationInfoQuery(@Nullable String packageName, @ApplicationInfoFlagsBits long flags,
+ int userId) {
this.packageName = packageName;
this.flags = flags;
this.userId = userId;
@@ -9468,7 +10093,7 @@
}
private static ApplicationInfo getApplicationInfoAsUserUncached(
- String packageName, int flags, int userId) {
+ String packageName, @ApplicationInfoFlagsBits long flags, int userId) {
try {
return ActivityThread.getPackageManager()
.getApplicationInfo(packageName, flags, userId);
@@ -9498,7 +10123,7 @@
/** @hide */
public static ApplicationInfo getApplicationInfoAsUserCached(
- String packageName, int flags, int userId) {
+ String packageName, @ApplicationInfoFlagsBits long flags, int userId) {
return sApplicationInfoCache.query(
new ApplicationInfoQuery(packageName, flags, userId));
}
@@ -9529,10 +10154,10 @@
private static final class PackageInfoQuery {
final String packageName;
- final int flags;
+ final long flags;
final int userId;
- PackageInfoQuery(@Nullable String packageName, int flags, int userId) {
+ PackageInfoQuery(@Nullable String packageName, @PackageInfoFlagsBits long flags, int userId) {
this.packageName = packageName;
this.flags = flags;
this.userId = userId;
@@ -9571,7 +10196,7 @@
}
private static PackageInfo getPackageInfoAsUserUncached(
- String packageName, int flags, int userId) {
+ String packageName, @PackageInfoFlagsBits long flags, int userId) {
try {
return ActivityThread.getPackageManager().getPackageInfo(packageName, flags, userId);
} catch (RemoteException e) {
@@ -9600,7 +10225,7 @@
/** @hide */
public static PackageInfo getPackageInfoAsUserCached(
- String packageName, int flags, int userId) {
+ String packageName, @PackageInfoFlagsBits long flags, int userId) {
return sPackageInfoCache.query(new PackageInfoQuery(packageName, flags, userId));
}
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index b11b38a..28290d7 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -76,8 +76,9 @@
@Nullable
public static PackageInfo generate(ParsingPackageRead pkg, int[] gids,
- @PackageManager.PackageInfoFlags long flags, long firstInstallTime, long lastUpdateTime,
- Set<String> grantedPermissions, FrameworkPackageUserState state, int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
+ long lastUpdateTime, Set<String> grantedPermissions, FrameworkPackageUserState state,
+ int userId) {
return generateWithComponents(pkg, gids, flags, firstInstallTime, lastUpdateTime, grantedPermissions,
state, userId, null);
}
@@ -90,9 +91,9 @@
@Nullable
private static PackageInfo generateWithComponents(ParsingPackageRead pkg, int[] gids,
- @PackageManager.PackageInfoFlags long flags, long firstInstallTime, long lastUpdateTime,
- Set<String> grantedPermissions, FrameworkPackageUserState state, int userId,
- @Nullable ApexInfo apexInfo) {
+ @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
+ long lastUpdateTime, Set<String> grantedPermissions, FrameworkPackageUserState state,
+ int userId, @Nullable ApexInfo apexInfo) {
ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
if (applicationInfo == null) {
return null;
@@ -190,9 +191,9 @@
@Nullable
public static PackageInfo generateWithoutComponents(ParsingPackageRead pkg, int[] gids,
- @PackageManager.PackageInfoFlags long flags, long firstInstallTime, long lastUpdateTime,
- Set<String> grantedPermissions, FrameworkPackageUserState state, int userId,
- @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
+ @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
+ long lastUpdateTime, Set<String> grantedPermissions, FrameworkPackageUserState state,
+ int userId, @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
if (!checkUseInstalled(pkg, state, flags)) {
return null;
}
@@ -210,7 +211,7 @@
*/
@NonNull
public static PackageInfo generateWithoutComponentsUnchecked(ParsingPackageRead pkg, int[] gids,
- @PackageManager.PackageInfoFlags long flags, long firstInstallTime,
+ @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
long lastUpdateTime, Set<String> grantedPermissions, FrameworkPackageUserState state,
int userId, @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
PackageInfo pi = new PackageInfo();
@@ -365,7 +366,7 @@
@Nullable
public static ApplicationInfo generateApplicationInfo(ParsingPackageRead pkg,
- @PackageManager.ApplicationInfoFlags long flags, FrameworkPackageUserState state,
+ @PackageManager.ApplicationInfoFlagsBits long flags, FrameworkPackageUserState state,
int userId) {
if (pkg == null) {
return null;
@@ -393,7 +394,7 @@
*/
@NonNull
public static ApplicationInfo generateApplicationInfoUnchecked(@NonNull ParsingPackageRead pkg,
- @PackageManager.ApplicationInfoFlags long flags,
+ @PackageManager.ApplicationInfoFlagsBits long flags,
@NonNull FrameworkPackageUserState state, int userId, boolean assignUserFields) {
// Make shallow copy so we can store the metadata/libraries safely
ApplicationInfo ai = ((ParsingPackageHidden) pkg).toAppInfoWithoutState();
@@ -453,7 +454,7 @@
@Nullable
public static ApplicationInfo generateDelegateApplicationInfo(@Nullable ApplicationInfo ai,
- @PackageManager.ApplicationInfoFlags long flags,
+ @PackageManager.ApplicationInfoFlagsBits long flags,
@NonNull FrameworkPackageUserState state, int userId) {
if (ai == null || !checkUseInstalledOrHidden(flags, state, ai)) {
return null;
@@ -470,8 +471,8 @@
@Nullable
public static ActivityInfo generateDelegateActivityInfo(@Nullable ActivityInfo a,
- @PackageManager.ComponentInfoFlags long flags, @NonNull FrameworkPackageUserState state,
- int userId) {
+ @PackageManager.ComponentInfoFlagsBits long flags,
+ @NonNull FrameworkPackageUserState state, int userId) {
if (a == null || !checkUseInstalledOrHidden(flags, state, a.applicationInfo)) {
return null;
}
@@ -485,7 +486,7 @@
@Nullable
public static ActivityInfo generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a,
- @PackageManager.ComponentInfoFlags long flags, FrameworkPackageUserState state,
+ @PackageManager.ComponentInfoFlagsBits long flags, FrameworkPackageUserState state,
@Nullable ApplicationInfo applicationInfo, int userId) {
if (a == null) return null;
if (!checkUseInstalled(pkg, state, flags)) {
@@ -510,7 +511,7 @@
*/
@NonNull
public static ActivityInfo generateActivityInfoUnchecked(@NonNull ParsedActivity a,
- @PackageManager.ComponentInfoFlags long flags,
+ @PackageManager.ComponentInfoFlagsBits long flags,
@NonNull ApplicationInfo applicationInfo) {
// Make shallow copies so we can store the metadata safely
ActivityInfo ai = new ActivityInfo();
@@ -551,14 +552,14 @@
@Nullable
public static ActivityInfo generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a,
- @PackageManager.ComponentInfoFlags long flags, FrameworkPackageUserState state,
+ @PackageManager.ComponentInfoFlagsBits long flags, FrameworkPackageUserState state,
int userId) {
return generateActivityInfo(pkg, a, flags, state, null, userId);
}
@Nullable
public static ServiceInfo generateServiceInfo(ParsingPackageRead pkg, ParsedService s,
- @PackageManager.ComponentInfoFlags long flags, FrameworkPackageUserState state,
+ @PackageManager.ComponentInfoFlagsBits long flags, FrameworkPackageUserState state,
@Nullable ApplicationInfo applicationInfo, int userId) {
if (s == null) return null;
if (!checkUseInstalled(pkg, state, flags)) {
@@ -583,7 +584,7 @@
*/
@NonNull
public static ServiceInfo generateServiceInfoUnchecked(@NonNull ParsedService s,
- @PackageManager.ComponentInfoFlags long flags,
+ @PackageManager.ComponentInfoFlagsBits long flags,
@NonNull ApplicationInfo applicationInfo) {
// Make shallow copies so we can store the metadata safely
ServiceInfo si = new ServiceInfo();
@@ -602,14 +603,14 @@
@Nullable
public static ServiceInfo generateServiceInfo(ParsingPackageRead pkg, ParsedService s,
- @PackageManager.ComponentInfoFlags long flags, FrameworkPackageUserState state,
+ @PackageManager.ComponentInfoFlagsBits long flags, FrameworkPackageUserState state,
int userId) {
return generateServiceInfo(pkg, s, flags, state, null, userId);
}
@Nullable
public static ProviderInfo generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p,
- @PackageManager.ComponentInfoFlags long flags, FrameworkPackageUserState state,
+ @PackageManager.ComponentInfoFlagsBits long flags, FrameworkPackageUserState state,
@Nullable ApplicationInfo applicationInfo, int userId) {
if (p == null) return null;
if (!checkUseInstalled(pkg, state, flags)) {
@@ -634,7 +635,7 @@
*/
@NonNull
public static ProviderInfo generateProviderInfoUnchecked(@NonNull ParsedProvider p,
- @PackageManager.ComponentInfoFlags long flags,
+ @PackageManager.ComponentInfoFlagsBits long flags,
@NonNull ApplicationInfo applicationInfo) {
// Make shallow copies so we can store the metadata safely
ProviderInfo pi = new ProviderInfo();
@@ -664,7 +665,7 @@
@Nullable
public static ProviderInfo generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p,
- @PackageManager.ComponentInfoFlags long flags, FrameworkPackageUserState state,
+ @PackageManager.ComponentInfoFlagsBits long flags, FrameworkPackageUserState state,
int userId) {
return generateProviderInfo(pkg, p, flags, state, null, userId);
}
@@ -675,7 +676,7 @@
*/
@Nullable
public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
- ParsingPackageRead pkg, @PackageManager.ComponentInfoFlags long flags, int userId,
+ ParsingPackageRead pkg, @PackageManager.ComponentInfoFlagsBits long flags, int userId,
boolean assignUserFields) {
if (i == null) return null;
@@ -706,7 +707,7 @@
@Nullable
public static PermissionInfo generatePermissionInfo(ParsedPermission p,
- @PackageManager.ComponentInfoFlags long flags) {
+ @PackageManager.ComponentInfoFlagsBits long flags) {
if (p == null) return null;
PermissionInfo pi = new PermissionInfo(p.getBackgroundPermission());
@@ -729,7 +730,7 @@
@Nullable
public static PermissionGroupInfo generatePermissionGroupInfo(ParsedPermissionGroup pg,
- @PackageManager.ComponentInfoFlags long flags) {
+ @PackageManager.ComponentInfoFlagsBits long flags) {
if (pg == null) return null;
PermissionGroupInfo pgi = new PermissionGroupInfo(
@@ -887,7 +888,7 @@
}
private static boolean checkUseInstalled(ParsingPackageRead pkg,
- FrameworkPackageUserState state, @PackageManager.PackageInfoFlags long flags) {
+ FrameworkPackageUserState state, @PackageManager.PackageInfoFlagsBits long flags) {
// If available for the target user
return PackageUserStateUtils.isAvailable(state, flags);
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 63332e7..2fa5df7 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -27,6 +27,7 @@
import android.content.pm.PackageManager.Property;
import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedApexSystemService;
import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedIntentInfo;
@@ -56,6 +57,8 @@
ParsingPackage addAdoptPermission(String adoptPermission);
+ ParsingPackage addApexSystemService(ParsedApexSystemService parsedApexSystemService);
+
ParsingPackage addConfigPreference(ConfigurationInfo configPreference);
ParsingPackage addFeatureGroup(FeatureGroupInfo featureGroup);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 19a8ce9..f03ab6a 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -35,6 +35,8 @@
import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedActivityImpl;
+import android.content.pm.parsing.component.ParsedApexSystemService;
+import android.content.pm.parsing.component.ParsedApexSystemServiceImpl;
import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedAttributionImpl;
import android.content.pm.parsing.component.ParsedComponent;
@@ -268,6 +270,9 @@
protected List<ParsedActivity> activities = emptyList();
@NonNull
+ protected List<ParsedApexSystemService> apexSystemServices = emptyList();
+
+ @NonNull
protected List<ParsedActivity> receivers = emptyList();
@NonNull
@@ -764,6 +769,14 @@
}
@Override
+ public final ParsingPackageImpl addApexSystemService(
+ ParsedApexSystemService parsedApexSystemService) {
+ this.apexSystemServices = CollectionUtils.add(
+ this.apexSystemServices, parsedApexSystemService);
+ return this;
+ }
+
+ @Override
public ParsingPackageImpl addReceiver(ParsedActivity parsedReceiver) {
this.receivers = CollectionUtils.add(this.receivers, parsedReceiver);
addMimeGroupsFromComponent(parsedReceiver);
@@ -832,7 +845,6 @@
return this;
}
-
@Override public ParsingPackageImpl removeUsesOptionalNativeLibrary(String libraryName) {
this.usesOptionalNativeLibraries = CollectionUtils.remove(this.usesOptionalNativeLibraries,
libraryName);
@@ -1198,6 +1210,7 @@
ParsingPackageUtils.writeKeySetMapping(dest, this.keySetMapping);
sForInternedStringList.parcel(this.protectedBroadcasts, dest, flags);
dest.writeTypedList(this.activities);
+ dest.writeTypedList(this.apexSystemServices);
dest.writeTypedList(this.receivers);
dest.writeTypedList(this.services);
dest.writeTypedList(this.providers);
@@ -1339,6 +1352,8 @@
this.protectedBroadcasts = sForInternedStringList.unparcel(in);
this.activities = ParsingUtils.createTypedInterfaceList(in, ParsedActivityImpl.CREATOR);
+ this.apexSystemServices = ParsingUtils.createTypedInterfaceList(in,
+ ParsedApexSystemServiceImpl.CREATOR);
this.receivers = ParsingUtils.createTypedInterfaceList(in, ParsedActivityImpl.CREATOR);
this.services = ParsingUtils.createTypedInterfaceList(in, ParsedServiceImpl.CREATOR);
this.providers = ParsingUtils.createTypedInterfaceList(in, ParsedProviderImpl.CREATOR);
@@ -1706,6 +1721,12 @@
@NonNull
@Override
+ public List<ParsedApexSystemService> getApexSystemServices() {
+ return apexSystemServices;
+ }
+
+ @NonNull
+ @Override
public List<ParsedActivity> getReceivers() {
return receivers;
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 49b3b08..c8113ef 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -23,6 +23,7 @@
import android.content.pm.PackageManager.Property;
import android.content.pm.PackageParser;
import android.content.pm.SigningDetails;
+import android.content.pm.parsing.component.ParsedApexSystemService;
import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedPermissionGroup;
@@ -55,6 +56,12 @@
@NonNull
List<String> getAdoptPermissions();
+ /**
+ * @see R.styleable#AndroidManifestApexSystemService
+ */
+ @NonNull
+ List<ParsedApexSystemService> getApexSystemServices();
+
@NonNull
List<ParsedAttribution> getAttributions();
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 3e537c8..fb24cb2 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -53,6 +53,8 @@
import android.content.pm.parsing.component.ComponentParseUtils;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedActivityUtils;
+import android.content.pm.parsing.component.ParsedApexSystemService;
+import android.content.pm.parsing.component.ParsedApexSystemServiceUtils;
import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedAttributionUtils;
import android.content.pm.parsing.component.ParsedComponent;
@@ -2205,6 +2207,18 @@
result = activityResult;
break;
+ case "apex-system-service":
+ ParseResult<ParsedApexSystemService> systemServiceResult =
+ ParsedApexSystemServiceUtils.parseApexSystemService(res,
+ parser, input);
+ if (systemServiceResult.isSuccess()) {
+ ParsedApexSystemService systemService =
+ systemServiceResult.getResult();
+ pkg.addApexSystemService(systemService);
+ }
+
+ result = systemServiceResult;
+ break;
default:
result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
break;
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/core/java/android/content/pm/parsing/component/ParsedApexSystemService.java
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
copy to core/java/android/content/pm/parsing/component/ParsedApexSystemService.java
index fbb78c8..fe821e0 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/core/java/android/content/pm/parsing/component/ParsedApexSystemService.java
@@ -14,12 +14,25 @@
* limitations under the License.
*/
-package com.android.systemui.flags;
+package android.content.pm.parsing.component;
-/**
- * Class to manage simple DeviceConfig-based feature flags.
- *
- * See {@link Flags} for instructions on defining new flags.
- */
-public interface FeatureFlags extends FlagReader {
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+
+/** @hide */
+public interface ParsedApexSystemService extends Parcelable {
+
+ @NonNull
+ String getName();
+
+ @Nullable
+ String getJarPath();
+
+ @Nullable
+ String getMinSdkVersion();
+
+ @Nullable
+ String getMaxSdkVersion();
+
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java b/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java
new file mode 100644
index 0000000..54196fd
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java
@@ -0,0 +1,241 @@
+/*
+ * 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.
+ */
+
+package android.content.pm.parsing.component;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+/** @hide **/
+@DataClass(genGetters = true, genAidl = false, genSetters = true, genParcelable = true)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedApexSystemServiceImpl implements ParsedApexSystemService {
+
+ @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
+ @NonNull
+ private String name;
+
+ @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
+ @Nullable
+ private String jarPath;
+
+ @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
+ @Nullable
+ private String minSdkVersion;
+
+ @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
+ @Nullable
+ private String maxSdkVersion;
+
+ public ParsedApexSystemServiceImpl() {
+ }
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ public ParsedApexSystemServiceImpl(
+ @NonNull String name,
+ @Nullable String jarPath,
+ @Nullable String minSdkVersion,
+ @Nullable String maxSdkVersion) {
+ this.name = name;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, name);
+ this.jarPath = jarPath;
+ this.minSdkVersion = minSdkVersion;
+ this.maxSdkVersion = maxSdkVersion;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull String getName() {
+ return name;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable String getJarPath() {
+ return jarPath;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable String getMinSdkVersion() {
+ return minSdkVersion;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable String getMaxSdkVersion() {
+ return maxSdkVersion;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull ParsedApexSystemServiceImpl setName(@NonNull String value) {
+ name = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, name);
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull ParsedApexSystemServiceImpl setJarPath(@NonNull String value) {
+ jarPath = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull ParsedApexSystemServiceImpl setMinSdkVersion(@NonNull String value) {
+ minSdkVersion = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull ParsedApexSystemServiceImpl setMaxSdkVersion(@NonNull String value) {
+ maxSdkVersion = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ static Parcelling<String> sParcellingForName =
+ Parcelling.Cache.get(
+ Parcelling.BuiltIn.ForInternedString.class);
+ static {
+ if (sParcellingForName == null) {
+ sParcellingForName = Parcelling.Cache.put(
+ new Parcelling.BuiltIn.ForInternedString());
+ }
+ }
+
+ @DataClass.Generated.Member
+ static Parcelling<String> sParcellingForJarPath =
+ Parcelling.Cache.get(
+ Parcelling.BuiltIn.ForInternedString.class);
+ static {
+ if (sParcellingForJarPath == null) {
+ sParcellingForJarPath = Parcelling.Cache.put(
+ new Parcelling.BuiltIn.ForInternedString());
+ }
+ }
+
+ @DataClass.Generated.Member
+ static Parcelling<String> sParcellingForMinSdkVersion =
+ Parcelling.Cache.get(
+ Parcelling.BuiltIn.ForInternedString.class);
+ static {
+ if (sParcellingForMinSdkVersion == null) {
+ sParcellingForMinSdkVersion = Parcelling.Cache.put(
+ new Parcelling.BuiltIn.ForInternedString());
+ }
+ }
+
+ @DataClass.Generated.Member
+ static Parcelling<String> sParcellingForMaxSdkVersion =
+ Parcelling.Cache.get(
+ Parcelling.BuiltIn.ForInternedString.class);
+ static {
+ if (sParcellingForMaxSdkVersion == null) {
+ sParcellingForMaxSdkVersion = Parcelling.Cache.put(
+ new Parcelling.BuiltIn.ForInternedString());
+ }
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (jarPath != null) flg |= 0x2;
+ if (minSdkVersion != null) flg |= 0x4;
+ if (maxSdkVersion != null) flg |= 0x8;
+ dest.writeByte(flg);
+ sParcellingForName.parcel(name, dest, flags);
+ sParcellingForJarPath.parcel(jarPath, dest, flags);
+ sParcellingForMinSdkVersion.parcel(minSdkVersion, dest, flags);
+ sParcellingForMaxSdkVersion.parcel(maxSdkVersion, dest, flags);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected ParsedApexSystemServiceImpl(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ String _name = sParcellingForName.unparcel(in);
+ String _jarPath = sParcellingForJarPath.unparcel(in);
+ String _minSdkVersion = sParcellingForMinSdkVersion.unparcel(in);
+ String _maxSdkVersion = sParcellingForMaxSdkVersion.unparcel(in);
+
+ this.name = _name;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, name);
+ this.jarPath = _jarPath;
+ this.minSdkVersion = _minSdkVersion;
+ this.maxSdkVersion = _maxSdkVersion;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull android.os.Parcelable.Creator<ParsedApexSystemServiceImpl> CREATOR
+ = new android.os.Parcelable.Creator<ParsedApexSystemServiceImpl>() {
+ @Override
+ public ParsedApexSystemServiceImpl[] newArray(int size) {
+ return new ParsedApexSystemServiceImpl[size];
+ }
+
+ @Override
+ public ParsedApexSystemServiceImpl createFromParcel(@NonNull android.os.Parcel in) {
+ return new ParsedApexSystemServiceImpl(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1638903241144L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java",
+ inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedApexSystemService]\n@com.android.internal.util.DataClass(genGetters=true, genAidl=false, genSetters=true, genParcelable=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceUtils.java b/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceUtils.java
new file mode 100644
index 0000000..26abf48
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceUtils.java
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+package android.content.pm.parsing.component;
+
+import android.R;
+import android.annotation.NonNull;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.text.TextUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/** @hide */
+public class ParsedApexSystemServiceUtils {
+
+ @NonNull
+ public static ParseResult<ParsedApexSystemService> parseApexSystemService(
+ Resources res, XmlResourceParser parser, ParseInput input)
+ throws XmlPullParserException, IOException {
+ final ParsedApexSystemServiceImpl systemService =
+ new ParsedApexSystemServiceImpl();
+ TypedArray sa = res.obtainAttributes(parser,
+ R.styleable.AndroidManifestApexSystemService);
+ try {
+ String className = sa.getString(
+ R.styleable.AndroidManifestApexSystemService_name);
+ if (TextUtils.isEmpty(className)) {
+ return input.error("<apex-system-service> does not have name attribute");
+ }
+
+ String jarPath = sa.getString(
+ R.styleable.AndroidManifestApexSystemService_path);
+ String minSdkVersion = sa.getString(
+ R.styleable.AndroidManifestApexSystemService_minSdkVersion);
+ String maxSdkVersion = sa.getString(
+ R.styleable.AndroidManifestApexSystemService_maxSdkVersion);
+
+ systemService.setName(className)
+ .setMinSdkVersion(minSdkVersion)
+ .setMaxSdkVersion(maxSdkVersion);
+ if (!TextUtils.isEmpty(jarPath)) {
+ systemService.setJarPath(jarPath);
+ }
+
+ return input.success(systemService);
+ } finally {
+ sa.recycle();
+ }
+ }
+}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 24c6a5a..bfd9fd0 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -792,6 +792,21 @@
}
/**
+ * To get the parent theme resource id according to the parameter theme resource id.
+ * @param resId theme resource id.
+ * @return the parent theme resource id.
+ * @hide
+ */
+ @StyleRes
+ int getParentThemeIdentifier(@StyleRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ // name is checked in JNI.
+ return nativeGetParentThemeIdentifier(mObject, resId);
+ }
+ }
+
+ /**
* Enable resource resolution logging to track the steps taken to resolve the last resource
* entry retrieved. Stores the configuration and package names for each step.
*
@@ -1600,6 +1615,8 @@
private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag,
String prefix);
static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr);
+ @StyleRes
+ private static native int nativeGetParentThemeIdentifier(long ptr, @StyleRes int styleId);
// AssetInputStream related native methods.
private static native void nativeAssetDestroy(long assetPtr);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 632d8e5..cb53a2a 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -80,6 +80,7 @@
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -1507,6 +1508,12 @@
* retrieve XML attributes with style and theme information applied.
*/
public final class Theme {
+ /**
+ * To trace parent themes needs to prevent a cycle situation.
+ * e.x. A's parent is B, B's parent is C, and C's parent is A.
+ */
+ private static final int MAX_NUMBER_OF_TRACING_PARENT_THEME = 100;
+
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -1800,6 +1807,13 @@
}
}
+ @StyleRes
+ /*package*/ int getParentThemeIdentifier(@StyleRes int resId) {
+ synchronized (mLock) {
+ return mThemeImpl.getParentThemeIdentifier(resId);
+ }
+ }
+
/**
* @hide
*/
@@ -1942,6 +1956,35 @@
final Theme other = (Theme) o;
return getKey().equals(other.getKey());
}
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append('{');
+ int themeResId = getAppliedStyleResId();
+ int i = 0;
+ sb.append("InheritanceMap=[");
+ while (themeResId > 0) {
+ if (i > MAX_NUMBER_OF_TRACING_PARENT_THEME) {
+ sb.append(",...");
+ break;
+ }
+
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append("id=0x").append(Integer.toHexString(themeResId));
+ sb.append(getResourcePackageName(themeResId))
+ .append(":").append(getResourceTypeName(themeResId))
+ .append("/").append(getResourceEntryName(themeResId));
+
+ i++;
+ themeResId = getParentThemeIdentifier(themeResId);
+ }
+ sb.append("], Themes=").append(Arrays.deepToString(getTheme()));
+ sb.append('}');
+ return sb.toString();
+ }
}
static class ThemeKey implements Cloneable {
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index b9f93b8..4d850b0c 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -1310,6 +1310,14 @@
return mThemeResId;
}
+ @StyleRes
+ /*package*/ int getParentThemeIdentifier(@StyleRes int resId) {
+ if (resId > 0) {
+ return mAssets.getParentThemeIdentifier(resId);
+ }
+ return 0;
+ }
+
void applyStyle(int resId, boolean force) {
mAssets.applyStyleToTheme(mTheme, resId, force);
mThemeResId = resId;
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index d6f55d6..9eb9cd5 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -527,11 +527,12 @@
final TypedValue value = mValue;
getValueAt(index, value);
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + attrIndex + ": " + value);
+ "Failed to resolve attribute at index " + attrIndex + ": " + value
+ + ", theme=" + mTheme);
}
throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
- + " to color: type=0x" + Integer.toHexString(type));
+ + " to color: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
}
/**
@@ -561,7 +562,8 @@
if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + index + ": " + value);
+ "Failed to resolve attribute at index " + index + ": " + value
+ + ", theme=" + mTheme);
}
return mResources.loadComplexColor(value, value.resourceId, mTheme);
}
@@ -596,7 +598,8 @@
if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + index + ": " + value);
+ "Failed to resolve attribute at index " + index + ": " + value
+ + ", theme=" + mTheme);
}
return mResources.loadColorStateList(value, value.resourceId, mTheme);
}
@@ -637,11 +640,12 @@
final TypedValue value = mValue;
getValueAt(index, value);
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + attrIndex + ": " + value);
+ "Failed to resolve attribute at index " + attrIndex + ": " + value
+ + ", theme=" + mTheme);
}
throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
- + " to integer: type=0x" + Integer.toHexString(type));
+ + " to integer: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
}
/**
@@ -684,11 +688,12 @@
final TypedValue value = mValue;
getValueAt(index, value);
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + attrIndex + ": " + value);
+ "Failed to resolve attribute at index " + attrIndex + ": " + value
+ + ", theme=" + mTheme);
}
throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
- + " to dimension: type=0x" + Integer.toHexString(type));
+ + " to dimension: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
}
/**
@@ -732,11 +737,12 @@
final TypedValue value = mValue;
getValueAt(index, value);
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + attrIndex + ": " + value);
+ "Failed to resolve attribute at index " + attrIndex + ": " + value
+ + ", theme=" + mTheme);
}
throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
- + " to dimension: type=0x" + Integer.toHexString(type));
+ + " to dimension: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
}
/**
@@ -781,11 +787,12 @@
final TypedValue value = mValue;
getValueAt(index, value);
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + attrIndex + ": " + value);
+ "Failed to resolve attribute at index " + attrIndex + ": " + value
+ + ", theme=" + mTheme);
}
throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
- + " to dimension: type=0x" + Integer.toHexString(type));
+ + " to dimension: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
}
/**
@@ -825,11 +832,12 @@
final TypedValue value = mValue;
getValueAt(index, value);
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + attrIndex + ": " + value);
+ "Failed to resolve attribute at index " + attrIndex + ": " + value
+ + ", theme=" + mTheme);
}
throw new UnsupportedOperationException(getPositionDescription()
- + ": You must supply a " + name + " attribute.");
+ + ": You must supply a " + name + " attribute." + ", theme=" + mTheme);
}
/**
@@ -900,11 +908,12 @@
final TypedValue value = mValue;
getValueAt(index, value);
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + attrIndex + ": " + value);
+ "Failed to resolve attribute at index " + attrIndex + ": " + value
+ + ", theme=" + mTheme);
}
throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
- + " to fraction: type=0x" + Integer.toHexString(type));
+ + " to fraction: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
}
/**
@@ -996,7 +1005,8 @@
if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + index + ": " + value);
+ "Failed to resolve attribute at index " + index + ": " + value
+ + ", theme=" + mTheme);
}
if (density > 0) {
@@ -1032,7 +1042,8 @@
if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + index + ": " + value);
+ "Failed to resolve attribute at index " + index + ": " + value
+ + ", theme=" + mTheme);
}
return mResources.getFont(value, value.resourceId);
}
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index cad30dd..a4a8f31 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -25,6 +25,7 @@
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.SurfaceControl;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -129,6 +130,15 @@
public static final long USAGE_GPU_SAMPLED_IMAGE = 1 << 8;
/** Usage: The buffer will be written to by the GPU */
public static final long USAGE_GPU_COLOR_OUTPUT = 1 << 9;
+ /**
+ * The buffer will be used as a composer HAL overlay layer.
+ *
+ * This flag is currently only needed when using
+ * {@link android.view.SurfaceControl.Transaction#setBuffer(SurfaceControl, HardwareBuffer)}
+ * to set a buffer. In all other cases, the framework adds this flag
+ * internally to buffers that could be presented in a composer overlay.
+ */
+ public static final long USAGE_COMPOSER_OVERLAY = 1 << 11;
/** Usage: The buffer must not be used outside of a protected hardware path */
public static final long USAGE_PROTECTED_CONTENT = 1 << 14;
/** Usage: The buffer will be read by a hardware video encoder */
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 3c8b6e9..48a9121 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2669,7 +2669,8 @@
* </tbody>
* </table>
* <p>For applications targeting SDK version 31 or newer, if the mobile device declares to be
- * {@link android.os.Build.VERSION_CDOES.MEDIA_PERFORMANCE_CLASS media performance class} S,
+ * media performance class 12 or higher by setting
+ * {@link android.os.Build.VERSION_CDOES.MEDIA_PERFORMANCE_CLASS } to be 31 or larger,
* the primary camera devices (first rear/front camera in the camera ID list) will not
* support JPEG sizes smaller than 1080p. If the application configures a JPEG stream
* smaller than 1080p, the camera device will round up the JPEG image size to at least
@@ -2742,9 +2743,11 @@
* </tbody>
* </table>
* <p>For applications targeting SDK version 31 or newer, if the mobile device doesn't declare
- * to be media performance class S, or if the camera device isn't a primary rear/front
- * camera, the minimum required output stream configurations are the same as for applications
- * targeting SDK version older than 31.</p>
+ * to be media performance class 12 or better by setting
+ * {@link android.os.Build.VERSION_CDOES.MEDIA_PERFORMANCE_CLASS } to be 31 or larger,
+ * or if the camera device isn't a primary rear/front camera, the minimum required output
+ * stream configurations are the same as for applications targeting SDK version older than
+ * 31.</p>
* <p>Refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} for additional
* mandatory stream configurations on a per-capability basis.</p>
* <p>Exception on 176x144 (QCIF) resolution: camera devices usually have a fixed capability for
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 4c81f9c..0037464 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -30,6 +30,7 @@
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.app.KeyguardManager;
+import android.companion.virtual.IVirtualDevice;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
@@ -902,8 +903,16 @@
@NonNull VirtualDisplayConfig virtualDisplayConfig,
@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler,
@Nullable Context windowContext) {
- return mGlobal.createVirtualDisplay(mContext, projection, virtualDisplayConfig, callback,
- handler, windowContext);
+ return mGlobal.createVirtualDisplay(mContext, projection, null /* virtualDevice */,
+ virtualDisplayConfig, callback, handler, windowContext);
+ }
+
+ /** @hide */
+ public VirtualDisplay createVirtualDisplay(@Nullable IVirtualDevice virtualDevice,
+ @NonNull VirtualDisplayConfig virtualDisplayConfig,
+ @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
+ return mGlobal.createVirtualDisplay(mContext, null /* projection */, virtualDevice,
+ virtualDisplayConfig, callback, handler, null);
}
/**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 75155bb..01833fd 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -24,6 +24,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PropertyInvalidatedCache;
+import android.companion.virtual.IVirtualDevice;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.ParceledListSlice;
@@ -582,14 +583,14 @@
}
public VirtualDisplay createVirtualDisplay(@NonNull Context context, MediaProjection projection,
- @NonNull VirtualDisplayConfig virtualDisplayConfig, VirtualDisplay.Callback callback,
- Handler handler, @Nullable Context windowContext) {
+ IVirtualDevice virtualDevice, @NonNull VirtualDisplayConfig virtualDisplayConfig,
+ VirtualDisplay.Callback callback, Handler handler, @Nullable Context windowContext) {
VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler);
IMediaProjection projectionToken = projection != null ? projection.getProjection() : null;
int displayId;
try {
displayId = mDm.createVirtualDisplay(virtualDisplayConfig, callbackWrapper,
- projectionToken, context.getPackageName());
+ projectionToken, virtualDevice, context.getPackageName());
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 2985c75..83e1061 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -20,7 +20,6 @@
import android.annotation.Nullable;
import android.graphics.Point;
import android.hardware.SensorManager;
-import android.media.projection.IMediaProjection;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
@@ -381,31 +380,6 @@
public abstract void onEarlyInteractivityChange(boolean interactive);
/**
- * A special API for creates a virtual display with a DisplayPolicyController in system_server.
- * <p>
- * If this method is called without original calling uid, the caller must enforce the
- * corresponding permissions according to the flags.
- * {@link android.Manifest.permission#CAPTURE_VIDEO_OUTPUT}
- * {@link android.Manifest.permission#CAPTURE_SECURE_VIDEO_OUTPUT}
- * {@link android.Manifest.permission#ADD_TRUSTED_DISPLAY}
- * {@link android.Manifest.permission#INTERNAL_SYSTEM_WINDOW}
- * </p>
- *
- * @param virtualDisplayConfig The arguments for the virtual display configuration. See
- * {@link VirtualDisplayConfig} for using it.
- * @param callback Callback to call when the virtual display's state changes, or null if none.
- * @param projection MediaProjection token.
- * @param packageName The package name of the app.
- * @param controller The DisplayWindowPolicyControl that can control what contents are
- * allowed to be displayed.
- * @return The newly created virtual display id , or {@link Display#INVALID_DISPLAY} if the
- * virtual display cannot be created.
- */
- public abstract int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
- IVirtualDisplayCallback callback, IMediaProjection projection, String packageName,
- DisplayWindowPolicyController controller);
-
- /**
* Get {@link DisplayWindowPolicyController} associated to the {@link DisplayInfo#displayId}
*
* @param displayId The id of the display.
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 0d5f1af..82b31d4 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -16,6 +16,7 @@
package android.hardware.display;
+import android.companion.virtual.IVirtualDevice;
import android.content.pm.ParceledListSlice;
import android.graphics.Point;
import android.hardware.display.BrightnessConfiguration;
@@ -86,10 +87,10 @@
void requestColorMode(int displayId, int colorMode);
// Requires CAPTURE_VIDEO_OUTPUT, CAPTURE_SECURE_VIDEO_OUTPUT, or an appropriate
- // MediaProjection token for certain combinations of flags.
+ // MediaProjection token or VirtualDevice for certain combinations of flags.
int createVirtualDisplay(in VirtualDisplayConfig virtualDisplayConfig,
in IVirtualDisplayCallback callback, in IMediaProjection projectionToken,
- String packageName);
+ in IVirtualDevice virtualDevice, String packageName);
// No permissions required, but must be same Uid as the creator.
void resizeVirtualDisplay(in IVirtualDisplayCallback token,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/core/java/android/hardware/input/VirtualKeyEvent.aidl
similarity index 73%
copy from packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
copy to core/java/android/hardware/input/VirtualKeyEvent.aidl
index fbb78c8..5b3ee0c 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/core/java/android/hardware/input/VirtualKeyEvent.aidl
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-package com.android.systemui.flags;
+package android.hardware.input;
-/**
- * Class to manage simple DeviceConfig-based feature flags.
- *
- * See {@link Flags} for instructions on defining new flags.
- */
-public interface FeatureFlags extends FlagReader {
-}
+parcelable VirtualKeyEvent;
\ No newline at end of file
diff --git a/core/java/android/hardware/input/VirtualKeyEvent.java b/core/java/android/hardware/input/VirtualKeyEvent.java
new file mode 100644
index 0000000..d875156
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualKeyEvent.java
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+package android.hardware.input;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.KeyEvent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An event describing a keyboard interaction originating from a remote device.
+ *
+ * When the user presses a key, an {@code ACTION_DOWN} event should be reported. When the user
+ * releases the key, an {@code ACTION_UP} event should be reported.
+ *
+ * See {@link android.view.KeyEvent}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class VirtualKeyEvent implements Parcelable {
+
+ /** @hide */
+ public static final int ACTION_UNKNOWN = -1;
+ /** Action indicating the given key has been pressed. */
+ public static final int ACTION_DOWN = KeyEvent.ACTION_DOWN;
+ /** Action indicating the previously pressed key has been lifted. */
+ public static final int ACTION_UP = KeyEvent.ACTION_UP;
+
+ /** @hide */
+ @IntDef(prefix = { "ACTION_" }, value = {
+ ACTION_UNKNOWN,
+ ACTION_DOWN,
+ ACTION_UP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Action {
+ }
+
+ private final @Action int mAction;
+ private final int mKeyCode;
+
+ private VirtualKeyEvent(@Action int action, int keyCode) {
+ mAction = action;
+ mKeyCode = keyCode;
+ }
+
+ private VirtualKeyEvent(@NonNull Parcel parcel) {
+ mAction = parcel.readInt();
+ mKeyCode = parcel.readInt();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) {
+ parcel.writeInt(mAction);
+ parcel.writeInt(mKeyCode);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Returns the key code associated with this event.
+ */
+ public int getKeyCode() {
+ return mKeyCode;
+ }
+
+ /**
+ * Returns the action associated with this event.
+ */
+ public @Action int getAction() {
+ return mAction;
+ }
+
+ /**
+ * Builder for {@link VirtualKeyEvent}.
+ */
+ public static final class Builder {
+
+ private @Action int mAction = ACTION_UNKNOWN;
+ private int mKeyCode = -1;
+
+ /**
+ * Creates a {@link VirtualKeyEvent} object with the current builder configuration.
+ */
+ public @NonNull VirtualKeyEvent build() {
+ if (mAction == ACTION_UNKNOWN || mKeyCode == -1) {
+ throw new IllegalArgumentException(
+ "Cannot build virtual key event with unset fields");
+ }
+ return new VirtualKeyEvent(mAction, mKeyCode);
+ }
+
+ /**
+ * Sets the Android key code of the event. The set of allowed characters include digits 0-9,
+ * characters A-Z, and standard punctuation, as well as numpad keys, function keys F1-F12,
+ * and meta keys (caps lock, shift, etc.).
+ *
+ * @return this builder, to allow for chaining of calls
+ */
+ public @NonNull Builder setKeyCode(int keyCode) {
+ mKeyCode = keyCode;
+ return this;
+ }
+
+ /**
+ * Sets the action of the event.
+ *
+ * @return this builder, to allow for chaining of calls
+ */
+ public @NonNull Builder setAction(@Action int action) {
+ if (action != ACTION_DOWN && action != ACTION_UP) {
+ throw new IllegalArgumentException("Unsupported action type");
+ }
+ mAction = action;
+ return this;
+ }
+ }
+
+ public static final @NonNull Parcelable.Creator<VirtualKeyEvent> CREATOR =
+ new Parcelable.Creator<VirtualKeyEvent>() {
+ public VirtualKeyEvent createFromParcel(Parcel source) {
+ return new VirtualKeyEvent(source);
+ }
+
+ public VirtualKeyEvent[] newArray(int size) {
+ return new VirtualKeyEvent[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/input/VirtualKeyboard.java b/core/java/android/hardware/input/VirtualKeyboard.java
new file mode 100644
index 0000000..ee9b659
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualKeyboard.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package android.hardware.input;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.companion.virtual.IVirtualDevice;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import java.io.Closeable;
+
+/**
+ * A virtual keyboard representing a key input mechanism on a remote device, such as a built-in
+ * keyboard on a laptop, a software keyboard on a tablet, or a keypad on a TV remote control.
+ *
+ * This registers an InputDevice that is interpreted like a physically-connected device and
+ * dispatches received events to it.
+ *
+ * @hide
+ */
+@SystemApi
+public class VirtualKeyboard implements Closeable {
+
+ private final IVirtualDevice mVirtualDevice;
+ private final IBinder mToken;
+
+ /** @hide */
+ public VirtualKeyboard(IVirtualDevice virtualDevice, IBinder token) {
+ mVirtualDevice = virtualDevice;
+ mToken = token;
+ }
+
+ @Override
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void close() {
+ try {
+ mVirtualDevice.unregisterInputDevice(mToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sends a key event to the system.
+ *
+ * @param event the event to send
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void sendKeyEvent(@NonNull VirtualKeyEvent event) {
+ try {
+ mVirtualDevice.sendKeyEvent(mToken, event);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/hardware/input/VirtualMouse.java b/core/java/android/hardware/input/VirtualMouse.java
new file mode 100644
index 0000000..6599dd2
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualMouse.java
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+package android.hardware.input;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.companion.virtual.IVirtualDevice;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.view.MotionEvent;
+
+import java.io.Closeable;
+
+/**
+ * A virtual mouse representing a relative input mechanism on a remote device, such as a mouse or
+ * trackpad.
+ *
+ * This registers an InputDevice that is interpreted like a physically-connected device and
+ * dispatches received events to it.
+ *
+ * @hide
+ */
+@SystemApi
+public class VirtualMouse implements Closeable {
+
+ private final IVirtualDevice mVirtualDevice;
+ private final IBinder mToken;
+
+ /** @hide */
+ public VirtualMouse(IVirtualDevice virtualDevice, IBinder token) {
+ mVirtualDevice = virtualDevice;
+ mToken = token;
+ }
+
+ @Override
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void close() {
+ try {
+ mVirtualDevice.unregisterInputDevice(mToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Send a mouse button event to the system.
+ *
+ * @param event the event
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void sendButtonEvent(@NonNull VirtualMouseButtonEvent event) {
+ try {
+ mVirtualDevice.sendButtonEvent(mToken, event);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sends a scrolling event to the system. See {@link MotionEvent#AXIS_VSCROLL} and
+ * {@link MotionEvent#AXIS_SCROLL}.
+ *
+ * @param event the event
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void sendScrollEvent(@NonNull VirtualMouseScrollEvent event) {
+ try {
+ mVirtualDevice.sendScrollEvent(mToken, event);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sends a relative movement event to the system.
+ *
+ * @param event the event
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void sendRelativeEvent(@NonNull VirtualMouseRelativeEvent event) {
+ try {
+ mVirtualDevice.sendRelativeEvent(mToken, event);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/core/java/android/hardware/input/VirtualMouseButtonEvent.aidl
similarity index 73%
copy from packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
copy to core/java/android/hardware/input/VirtualMouseButtonEvent.aidl
index fbb78c8..ebcf5aa 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/core/java/android/hardware/input/VirtualMouseButtonEvent.aidl
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-package com.android.systemui.flags;
+package android.hardware.input;
-/**
- * Class to manage simple DeviceConfig-based feature flags.
- *
- * See {@link Flags} for instructions on defining new flags.
- */
-public interface FeatureFlags extends FlagReader {
-}
+parcelable VirtualMouseButtonEvent;
\ No newline at end of file
diff --git a/core/java/android/hardware/input/VirtualMouseButtonEvent.java b/core/java/android/hardware/input/VirtualMouseButtonEvent.java
new file mode 100644
index 0000000..2e094cf
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualMouseButtonEvent.java
@@ -0,0 +1,180 @@
+/*
+ * 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.
+ */
+
+package android.hardware.input;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.MotionEvent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An event describing a mouse button click interaction originating from a remote device.
+ *
+ * @hide
+ */
+@SystemApi
+public final class VirtualMouseButtonEvent implements Parcelable {
+
+ /** @hide */
+ public static final int ACTION_UNKNOWN = -1;
+ /** Action indicating the mouse button has been pressed. */
+ public static final int ACTION_BUTTON_PRESS = MotionEvent.ACTION_BUTTON_PRESS;
+ /** Action indicating the mouse button has been released. */
+ public static final int ACTION_BUTTON_RELEASE = MotionEvent.ACTION_BUTTON_RELEASE;
+ /** @hide */
+ @IntDef(prefix = {"ACTION_"}, value = {
+ ACTION_UNKNOWN,
+ ACTION_BUTTON_PRESS,
+ ACTION_BUTTON_RELEASE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Action {}
+
+ /** @hide */
+ public static final int BUTTON_UNKNOWN = -1;
+ /** Action indicating the mouse button involved in this event is in the left position. */
+ public static final int BUTTON_PRIMARY = MotionEvent.BUTTON_PRIMARY;
+ /** Action indicating the mouse button involved in this event is in the middle position. */
+ public static final int BUTTON_TERTIARY = MotionEvent.BUTTON_TERTIARY;
+ /** Action indicating the mouse button involved in this event is in the right position. */
+ public static final int BUTTON_SECONDARY = MotionEvent.BUTTON_SECONDARY;
+ /**
+ * Action indicating the mouse button involved in this event is intended to go back to the
+ * previous.
+ */
+ public static final int BUTTON_BACK = MotionEvent.BUTTON_BACK;
+ /**
+ * Action indicating the mouse button involved in this event is intended to move forward to the
+ * next.
+ */
+ public static final int BUTTON_FORWARD = MotionEvent.BUTTON_FORWARD;
+ /** @hide */
+ @IntDef(prefix = {"BUTTON_"}, value = {
+ BUTTON_UNKNOWN,
+ BUTTON_PRIMARY,
+ BUTTON_TERTIARY,
+ BUTTON_SECONDARY,
+ BUTTON_BACK,
+ BUTTON_FORWARD,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Button {}
+
+ private final @Action int mAction;
+ private final @Button int mButtonCode;
+
+ private VirtualMouseButtonEvent(@Action int action, @Button int buttonCode) {
+ mAction = action;
+ mButtonCode = buttonCode;
+ }
+
+ private VirtualMouseButtonEvent(@NonNull Parcel parcel) {
+ mAction = parcel.readInt();
+ mButtonCode = parcel.readInt();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) {
+ parcel.writeInt(mAction);
+ parcel.writeInt(mButtonCode);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Returns the button code associated with this event.
+ */
+ public @Button int getButtonCode() {
+ return mButtonCode;
+ }
+
+ /**
+ * Returns the action associated with this event.
+ */
+ public @Action int getAction() {
+ return mAction;
+ }
+
+ /**
+ * Builder for {@link VirtualMouseButtonEvent}.
+ */
+ public static final class Builder {
+
+ private @Action int mAction = ACTION_UNKNOWN;
+ private @Button int mButtonCode = -1;
+
+ /**
+ * Creates a {@link VirtualMouseButtonEvent} object with the current builder configuration.
+ */
+ public @NonNull VirtualMouseButtonEvent build() {
+ if (mAction == ACTION_UNKNOWN || mButtonCode == -1) {
+ throw new IllegalArgumentException(
+ "Cannot build virtual mouse button event with unset fields");
+ }
+ return new VirtualMouseButtonEvent(mAction, mButtonCode);
+ }
+
+ /**
+ * Sets the button code of the event.
+ *
+ * @return this builder, to allow for chaining of calls
+ */
+ public @NonNull Builder setButtonCode(int buttonCode) {
+ if (buttonCode != BUTTON_PRIMARY
+ && buttonCode != BUTTON_TERTIARY
+ && buttonCode != BUTTON_SECONDARY
+ && buttonCode != BUTTON_BACK
+ && buttonCode != BUTTON_FORWARD) {
+ throw new IllegalArgumentException("Unsupported mouse button code");
+ }
+ mButtonCode = buttonCode;
+ return this;
+ }
+
+ /**
+ * Sets the action of the event.
+ *
+ * @return this builder, to allow for chaining of calls
+ */
+ public @NonNull Builder setAction(@Action int action) {
+ if (action != ACTION_BUTTON_PRESS && action != ACTION_BUTTON_RELEASE) {
+ throw new IllegalArgumentException("Unsupported mouse button action type");
+ }
+ mAction = action;
+ return this;
+ }
+ }
+
+ public static final @NonNull Parcelable.Creator<VirtualMouseButtonEvent> CREATOR =
+ new Parcelable.Creator<VirtualMouseButtonEvent>() {
+ public VirtualMouseButtonEvent createFromParcel(Parcel source) {
+ return new VirtualMouseButtonEvent(source);
+ }
+
+ public VirtualMouseButtonEvent[] newArray(int size) {
+ return new VirtualMouseButtonEvent[size];
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/core/java/android/hardware/input/VirtualMouseRelativeEvent.aidl
similarity index 73%
copy from packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
copy to core/java/android/hardware/input/VirtualMouseRelativeEvent.aidl
index fbb78c8..1095858 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/core/java/android/hardware/input/VirtualMouseRelativeEvent.aidl
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-package com.android.systemui.flags;
+package android.hardware.input;
-/**
- * Class to manage simple DeviceConfig-based feature flags.
- *
- * See {@link Flags} for instructions on defining new flags.
- */
-public interface FeatureFlags extends FlagReader {
-}
+parcelable VirtualMouseRelativeEvent;
\ No newline at end of file
diff --git a/core/java/android/hardware/input/VirtualMouseRelativeEvent.java b/core/java/android/hardware/input/VirtualMouseRelativeEvent.java
new file mode 100644
index 0000000..65ed1f2
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualMouseRelativeEvent.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+package android.hardware.input;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * An event describing a mouse movement interaction originating from a remote device.
+ *
+ * See {@link android.view.MotionEvent}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class VirtualMouseRelativeEvent implements Parcelable {
+
+ private final float mRelativeX;
+ private final float mRelativeY;
+
+ private VirtualMouseRelativeEvent(float relativeX, float relativeY) {
+ mRelativeX = relativeX;
+ mRelativeY = relativeY;
+ }
+
+ private VirtualMouseRelativeEvent(@NonNull Parcel parcel) {
+ mRelativeX = parcel.readFloat();
+ mRelativeY = parcel.readFloat();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) {
+ parcel.writeFloat(mRelativeX);
+ parcel.writeFloat(mRelativeY);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Returns the relative x-axis movement, in pixels.
+ */
+ public float getRelativeX() {
+ return mRelativeX;
+ }
+
+ /**
+ * Returns the relative x-axis movement, in pixels.
+ */
+ public float getRelativeY() {
+ return mRelativeY;
+ }
+
+ /**
+ * Builder for {@link VirtualMouseRelativeEvent}.
+ */
+ public static final class Builder {
+
+ private float mRelativeX;
+ private float mRelativeY;
+
+ /**
+ * Creates a {@link VirtualMouseRelativeEvent} object with the current builder
+ * configuration.
+ */
+ public @NonNull VirtualMouseRelativeEvent build() {
+ return new VirtualMouseRelativeEvent(mRelativeX, mRelativeY);
+ }
+
+ /**
+ * Sets the relative x-axis movement, in pixels.
+ *
+ * @return this builder, to allow for chaining of calls
+ */
+ public @NonNull Builder setRelativeX(float relativeX) {
+ mRelativeX = relativeX;
+ return this;
+ }
+
+ /**
+ * Sets the relative y-axis movement, in pixels.
+ *
+ * @return this builder, to allow for chaining of calls
+ */
+ public @NonNull Builder setRelativeY(float relativeY) {
+ mRelativeY = relativeY;
+ return this;
+ }
+ }
+
+ public static final @NonNull Parcelable.Creator<VirtualMouseRelativeEvent> CREATOR =
+ new Parcelable.Creator<VirtualMouseRelativeEvent>() {
+ public VirtualMouseRelativeEvent createFromParcel(Parcel source) {
+ return new VirtualMouseRelativeEvent(source);
+ }
+
+ public VirtualMouseRelativeEvent[] newArray(int size) {
+ return new VirtualMouseRelativeEvent[size];
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/core/java/android/hardware/input/VirtualMouseScrollEvent.aidl
similarity index 73%
copy from packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
copy to core/java/android/hardware/input/VirtualMouseScrollEvent.aidl
index fbb78c8..13177ef 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/core/java/android/hardware/input/VirtualMouseScrollEvent.aidl
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-package com.android.systemui.flags;
+package android.hardware.input;
-/**
- * Class to manage simple DeviceConfig-based feature flags.
- *
- * See {@link Flags} for instructions on defining new flags.
- */
-public interface FeatureFlags extends FlagReader {
-}
+parcelable VirtualMouseScrollEvent;
\ No newline at end of file
diff --git a/core/java/android/hardware/input/VirtualMouseScrollEvent.java b/core/java/android/hardware/input/VirtualMouseScrollEvent.java
new file mode 100644
index 0000000..1723259
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualMouseScrollEvent.java
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+package android.hardware.input;
+
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * An event describing a mouse scroll interaction originating from a remote device.
+ *
+ * See {@link android.view.MotionEvent}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class VirtualMouseScrollEvent implements Parcelable {
+
+ private final float mXAxisMovement;
+ private final float mYAxisMovement;
+
+ private VirtualMouseScrollEvent(float xAxisMovement, float yAxisMovement) {
+ mXAxisMovement = xAxisMovement;
+ mYAxisMovement = yAxisMovement;
+ }
+
+ private VirtualMouseScrollEvent(@NonNull Parcel parcel) {
+ mXAxisMovement = parcel.readFloat();
+ mYAxisMovement = parcel.readFloat();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) {
+ parcel.writeFloat(mXAxisMovement);
+ parcel.writeFloat(mYAxisMovement);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Returns the x-axis scroll movement, normalized from -1.0 to 1.0, inclusive. Positive values
+ * indicate scrolling upward; negative values, downward.
+ */
+ public float getXAxisMovement() {
+ return mXAxisMovement;
+ }
+
+ /**
+ * Returns the y-axis scroll movement, normalized from -1.0 to 1.0, inclusive. Positive values
+ * indicate scrolling towards the right; negative values, to the left.
+ */
+ public float getYAxisMovement() {
+ return mYAxisMovement;
+ }
+
+ /**
+ * Builder for {@link VirtualMouseScrollEvent}.
+ */
+ public static final class Builder {
+
+ private float mXAxisMovement;
+ private float mYAxisMovement;
+
+ /**
+ * Creates a {@link VirtualMouseScrollEvent} object with the current builder configuration.
+ */
+ public @NonNull VirtualMouseScrollEvent build() {
+ return new VirtualMouseScrollEvent(mXAxisMovement, mYAxisMovement);
+ }
+
+ /**
+ * Sets the x-axis scroll movement, normalized from -1.0 to 1.0, inclusive. Positive values
+ * indicate scrolling upward; negative values, downward.
+ *
+ * @return this builder, to allow for chaining of calls
+ */
+ public @NonNull Builder setXAxisMovement(
+ @FloatRange(from = -1.0f, to = 1.0f) float xAxisMovement) {
+ Preconditions.checkArgumentInRange(xAxisMovement, -1f, 1f, "xAxisMovement");
+ mXAxisMovement = xAxisMovement;
+ return this;
+ }
+
+ /**
+ * Sets the y-axis scroll movement, normalized from -1.0 to 1.0, inclusive. Positive values
+ * indicate scrolling towards the right; negative values, to the left.
+ *
+ * @return this builder, to allow for chaining of calls
+ */
+ public @NonNull Builder setYAxisMovement(
+ @FloatRange(from = -1.0f, to = 1.0f) float yAxisMovement) {
+ Preconditions.checkArgumentInRange(yAxisMovement, -1f, 1f, "yAxisMovement");
+ mYAxisMovement = yAxisMovement;
+ return this;
+ }
+ }
+
+ public static final @NonNull Parcelable.Creator<VirtualMouseScrollEvent> CREATOR =
+ new Parcelable.Creator<VirtualMouseScrollEvent>() {
+ public VirtualMouseScrollEvent createFromParcel(Parcel source) {
+ return new VirtualMouseScrollEvent(source);
+ }
+
+ public VirtualMouseScrollEvent[] newArray(int size) {
+ return new VirtualMouseScrollEvent[size];
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/core/java/android/hardware/input/VirtualTouchEvent.aidl
similarity index 73%
copy from packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
copy to core/java/android/hardware/input/VirtualTouchEvent.aidl
index fbb78c8..03c82e3 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/core/java/android/hardware/input/VirtualTouchEvent.aidl
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-package com.android.systemui.flags;
+package android.hardware.input;
-/**
- * Class to manage simple DeviceConfig-based feature flags.
- *
- * See {@link Flags} for instructions on defining new flags.
- */
-public interface FeatureFlags extends FlagReader {
-}
+parcelable VirtualTouchEvent;
\ No newline at end of file
diff --git a/core/java/android/hardware/input/VirtualTouchEvent.java b/core/java/android/hardware/input/VirtualTouchEvent.java
new file mode 100644
index 0000000..c7450d8
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualTouchEvent.java
@@ -0,0 +1,299 @@
+/*
+ * 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.
+ */
+
+package android.hardware.input;
+
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.MotionEvent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An event describing a touchscreen interaction originating from a remote device.
+ *
+ * The pointer id, tool type, action, and location are required; pressure and main axis size are
+ * optional.
+ *
+ * @hide
+ */
+@SystemApi
+public final class VirtualTouchEvent implements Parcelable {
+
+ /** @hide */
+ public static final int TOOL_TYPE_UNKNOWN = MotionEvent.TOOL_TYPE_UNKNOWN;
+ /** Tool type indicating that the user's finger is the origin of the event. */
+ public static final int TOOL_TYPE_FINGER = MotionEvent.TOOL_TYPE_FINGER;
+ /**
+ * Tool type indicating that a user's palm (or other input mechanism to be rejected) is the
+ * origin of the event.
+ */
+ public static final int TOOL_TYPE_PALM = MotionEvent.TOOL_TYPE_PALM;
+ /** @hide */
+ @IntDef(prefix = { "TOOL_TYPE_" }, value = {
+ TOOL_TYPE_UNKNOWN,
+ TOOL_TYPE_FINGER,
+ TOOL_TYPE_PALM,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ToolType {}
+
+ /** @hide */
+ public static final int ACTION_UNKNOWN = -1;
+ /** Action indicating the tool has been pressed down to the touchscreen. */
+ public static final int ACTION_DOWN = MotionEvent.ACTION_DOWN;
+ /** Action indicating the tool has been lifted from the touchscreen. */
+ public static final int ACTION_UP = MotionEvent.ACTION_UP;
+ /** Action indicating the tool has been moved along the face of the touchscreen. */
+ public static final int ACTION_MOVE = MotionEvent.ACTION_MOVE;
+ /** Action indicating the tool cancelled the current movement. */
+ public static final int ACTION_CANCEL = MotionEvent.ACTION_CANCEL;
+ /** @hide */
+ @IntDef(prefix = { "ACTION_" }, value = {
+ ACTION_UNKNOWN,
+ ACTION_DOWN,
+ ACTION_UP,
+ ACTION_MOVE,
+ ACTION_CANCEL,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Action {}
+
+ private final int mPointerId;
+ private final @ToolType int mToolType;
+ private final @Action int mAction;
+ private final float mX;
+ private final float mY;
+ private final float mPressure;
+ private final float mMajorAxisSize;
+
+ private VirtualTouchEvent(int pointerId, @ToolType int toolType, @Action int action,
+ float x, float y, float pressure, float majorAxisSize) {
+ mPointerId = pointerId;
+ mToolType = toolType;
+ mAction = action;
+ mX = x;
+ mY = y;
+ mPressure = pressure;
+ mMajorAxisSize = majorAxisSize;
+ }
+
+ private VirtualTouchEvent(@NonNull Parcel parcel) {
+ mPointerId = parcel.readInt();
+ mToolType = parcel.readInt();
+ mAction = parcel.readInt();
+ mX = parcel.readFloat();
+ mY = parcel.readFloat();
+ mPressure = parcel.readFloat();
+ mMajorAxisSize = parcel.readFloat();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mPointerId);
+ dest.writeInt(mToolType);
+ dest.writeInt(mAction);
+ dest.writeFloat(mX);
+ dest.writeFloat(mY);
+ dest.writeFloat(mPressure);
+ dest.writeFloat(mMajorAxisSize);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Returns the pointer id associated with this event.
+ */
+ public int getPointerId() {
+ return mPointerId;
+ }
+
+ /**
+ * Returns the tool type associated with this event.
+ */
+ public @ToolType int getToolType() {
+ return mToolType;
+ }
+
+ /**
+ * Returns the action associated with this event.
+ */
+ public @Action int getAction() {
+ return mAction;
+ }
+
+ /**
+ * Returns the x-axis location associated with this event.
+ */
+ public float getX() {
+ return mX;
+ }
+
+ /**
+ * Returns the y-axis location associated with this event.
+ */
+ public float getY() {
+ return mY;
+ }
+
+ /**
+ * Returns the pressure associated with this event. Returns {@link Float#NaN} if omitted.
+ */
+ public float getPressure() {
+ return mPressure;
+ }
+
+ /**
+ * Returns the major axis size associated with this event. Returns {@link Float#NaN} if omitted.
+ */
+ public float getMajorAxisSize() {
+ return mMajorAxisSize;
+ }
+
+ /**
+ * Builder for {@link VirtualTouchEvent}.
+ */
+ public static final class Builder {
+
+ private @ToolType int mToolType = TOOL_TYPE_UNKNOWN;
+ private int mPointerId = MotionEvent.INVALID_POINTER_ID;
+ private @Action int mAction = ACTION_UNKNOWN;
+ private float mX = Float.NaN;
+ private float mY = Float.NaN;
+ private float mPressure = Float.NaN;
+ private float mMajorAxisSize = Float.NaN;
+
+ /**
+ * Creates a {@link VirtualTouchEvent} object with the current builder configuration.
+ */
+ public @NonNull VirtualTouchEvent build() {
+ if (mToolType == TOOL_TYPE_UNKNOWN || mPointerId == MotionEvent.INVALID_POINTER_ID
+ || mAction == ACTION_UNKNOWN || Float.isNaN(mX) || Float.isNaN(mY)) {
+ throw new IllegalArgumentException(
+ "Cannot build virtual touch event with unset required fields");
+ }
+ if ((mToolType == TOOL_TYPE_PALM && mAction != ACTION_CANCEL)
+ || (mAction == ACTION_CANCEL && mToolType != TOOL_TYPE_PALM)) {
+ throw new IllegalArgumentException(
+ "ACTION_CANCEL and TOOL_TYPE_PALM must always appear together");
+ }
+ return new VirtualTouchEvent(mPointerId, mToolType, mAction, mX, mY, mPressure,
+ mMajorAxisSize);
+ }
+
+ /**
+ * Sets the pointer id of the event.
+ *
+ * @return this builder, to allow for chaining of calls
+ */
+ public @NonNull Builder setPointerId(int pointerId) {
+ mPointerId = pointerId;
+ return this;
+ }
+
+ /**
+ * Sets the tool type of the event.
+ *
+ * @return this builder, to allow for chaining of calls
+ */
+ public @NonNull Builder setToolType(@ToolType int toolType) {
+ if (toolType != TOOL_TYPE_FINGER && toolType != TOOL_TYPE_PALM) {
+ throw new IllegalArgumentException("Unsupported touch event tool type");
+ }
+ mToolType = toolType;
+ return this;
+ }
+
+ /**
+ * Sets the action of the event.
+ *
+ * @return this builder, to allow for chaining of calls
+ */
+ public @NonNull Builder setAction(@Action int action) {
+ if (action != ACTION_DOWN && action != ACTION_UP && action != ACTION_MOVE
+ && action != ACTION_CANCEL) {
+ throw new IllegalArgumentException("Unsupported touch event action type");
+ }
+ mAction = action;
+ return this;
+ }
+
+ /**
+ * Sets the x-axis location of the event.
+ *
+ * @return this builder, to allow for chaining of calls
+ */
+ public @NonNull Builder setX(float absX) {
+ mX = absX;
+ return this;
+ }
+
+ /**
+ * Sets the y-axis location of the event.
+ *
+ * @return this builder, to allow for chaining of calls
+ */
+ public @NonNull Builder setY(float absY) {
+ mY = absY;
+ return this;
+ }
+
+ /**
+ * Sets the pressure of the event. This field is optional and can be omitted.
+ *
+ * @return this builder, to allow for chaining of calls
+ */
+ public @NonNull Builder setPressure(@FloatRange(from = 0f) float pressure) {
+ if (pressure < 0f) {
+ throw new IllegalArgumentException("Touch event pressure cannot be negative");
+ }
+ mPressure = pressure;
+ return this;
+ }
+
+ /**
+ * Sets the major axis size of the event. This field is optional and can be omitted.
+ *
+ * @return this builder, to allow for chaining of calls
+ */
+ public @NonNull Builder setMajorAxisSize(@FloatRange(from = 0f) float majorAxisSize) {
+ if (majorAxisSize < 0f) {
+ throw new IllegalArgumentException(
+ "Touch event major axis size cannot be negative");
+ }
+ mMajorAxisSize = majorAxisSize;
+ return this;
+ }
+ }
+
+ public static final @NonNull Parcelable.Creator<VirtualTouchEvent> CREATOR =
+ new Parcelable.Creator<VirtualTouchEvent>() {
+ public VirtualTouchEvent createFromParcel(Parcel source) {
+ return new VirtualTouchEvent(source);
+ }
+ public VirtualTouchEvent[] newArray(int size) {
+ return new VirtualTouchEvent[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/input/VirtualTouchscreen.java b/core/java/android/hardware/input/VirtualTouchscreen.java
new file mode 100644
index 0000000..c8d602a
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualTouchscreen.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+package android.hardware.input;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.companion.virtual.IVirtualDevice;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import java.io.Closeable;
+
+/**
+ * A virtual touchscreen representing a touch-based display input mechanism on a remote device.
+ *
+ * This registers an InputDevice that is interpreted like a physically-connected device and
+ * dispatches received events to it.
+ *
+ * @hide
+ */
+@SystemApi
+public class VirtualTouchscreen implements Closeable {
+
+ private final IVirtualDevice mVirtualDevice;
+ private final IBinder mToken;
+
+ /** @hide */
+ public VirtualTouchscreen(IVirtualDevice virtualDevice, IBinder token) {
+ mVirtualDevice = virtualDevice;
+ mToken = token;
+ }
+
+ @Override
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void close() {
+ try {
+ mVirtualDevice.unregisterInputDevice(mToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sends a touch event to the system.
+ *
+ * @param event the event to send
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void sendTouchEvent(@NonNull VirtualTouchEvent event) {
+ try {
+ mVirtualDevice.sendTouchEvent(mToken, event);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index bcdd519..a525f58 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -60,6 +60,8 @@
*/
private final boolean mPersistent;
+ private Integer mId = null;
+
/* package */ ContextHubClient(ContextHubInfo hubInfo, boolean persistent) {
mAttachedHub = hubInfo;
mPersistent = persistent;
@@ -85,6 +87,11 @@
}
mClientProxy = clientProxy;
+ try {
+ mId = Integer.valueOf(mClientProxy.getId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -98,6 +105,31 @@
}
/**
+ * Returns the system-wide unique identifier for this ContextHubClient.
+ *
+ * This value can be used as an identifier for the messaging channel between a
+ * ContextHubClient and the Context Hub. This may be used as a routing mechanism
+ * between various ContextHubClient objects within an application.
+ *
+ * The value returned by this method will remain the same if it is associated with
+ * the same client reference at the ContextHubService (for instance, the ID of a
+ * PendingIntent ContextHubClient will remain the same even if the local object
+ * has been regenerated with the equivalent PendingIntent). If the ContextHubClient
+ * is newly generated (e.g. any regeneration of a callback client, or generation
+ * of a non-equal PendingIntent client), the ID will not be the same.
+ *
+ * @return The ID of this ContextHubClient.
+ *
+ * @throws IllegalStateException if the ID was not set internally.
+ */
+ public int getId() {
+ if (mId == null) {
+ throw new IllegalStateException("ID was not set");
+ }
+ return mId;
+ }
+
+ /**
* Closes the connection for this client and the Context Hub Service.
*
* When this function is invoked, the messaging associated with this client is invalidated.
diff --git a/core/java/android/hardware/location/IContextHubClient.aidl b/core/java/android/hardware/location/IContextHubClient.aidl
index e33545c..2423a58 100644
--- a/core/java/android/hardware/location/IContextHubClient.aidl
+++ b/core/java/android/hardware/location/IContextHubClient.aidl
@@ -29,4 +29,7 @@
// Closes the connection with the Context Hub
void close();
+
+ // Returns the unique ID for this client.
+ int getId();
}
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 8a0211c..70fe5d6 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -16,11 +16,19 @@
package android.net;
+import static android.net.NetworkStats.METERED_ALL;
+import static android.net.NetworkStats.METERED_YES;
+import static android.net.NetworkTemplate.MATCH_CARRIER;
+import static android.net.NetworkTemplate.MATCH_MOBILE;
+import static android.net.NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_EXACT;
+
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.BackupUtils;
+import android.util.Log;
import android.util.Range;
import android.util.RecurrenceRule;
@@ -42,10 +50,25 @@
* @hide
*/
public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
+ private static final String TAG = NetworkPolicy.class.getSimpleName();
private static final int VERSION_INIT = 1;
private static final int VERSION_RULE = 2;
private static final int VERSION_RAPID = 3;
+ /**
+ * Initial Version of the NetworkTemplate backup serializer.
+ */
+ private static final int TEMPLATE_BACKUP_VERSION_1_INIT = 1;
+ /**
+ * Version of the NetworkTemplate backup serializer that added carrier template support.
+ */
+ private static final int TEMPLATE_BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE = 2;
+ /**
+ * Latest Version of the NetworkTemplate Backup Serializer.
+ */
+ private static final int TEMPLATE_BACKUP_VERSION_LATEST =
+ TEMPLATE_BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE;
+
public static final int CYCLE_NONE = -1;
public static final long WARNING_DISABLED = -1;
public static final long LIMIT_DISABLED = -1;
@@ -255,7 +278,7 @@
DataOutputStream out = new DataOutputStream(baos);
out.writeInt(VERSION_RAPID);
- out.write(template.getBytesForBackup());
+ out.write(getNetworkTemplateBytesForBackup());
cycleRule.writeToStream(out);
out.writeLong(warningBytes);
out.writeLong(limitBytes);
@@ -274,7 +297,7 @@
throw new BackupUtils.BadVersionException("Unknown backup version: " + version);
}
- final NetworkTemplate template = NetworkTemplate.getNetworkTemplateFromBackup(in);
+ final NetworkTemplate template = getNetworkTemplateFromBackup(in);
final RecurrenceRule cycleRule;
if (version >= VERSION_RULE) {
cycleRule = new RecurrenceRule(in);
@@ -298,4 +321,61 @@
return new NetworkPolicy(template, cycleRule, warningBytes, limitBytes, lastWarningSnooze,
lastLimitSnooze, lastRapidSnooze, metered, inferred);
}
+
+ @NonNull
+ private byte[] getNetworkTemplateBytesForBackup() throws IOException {
+ if (!template.isPersistable()) {
+ Log.wtf(TAG, "Trying to backup non-persistable template: " + this);
+ }
+
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final DataOutputStream out = new DataOutputStream(baos);
+
+ out.writeInt(TEMPLATE_BACKUP_VERSION_LATEST);
+
+ out.writeInt(template.getMatchRule());
+ BackupUtils.writeString(out, template.getSubscriberId());
+ BackupUtils.writeString(out, template.getNetworkId());
+ out.writeInt(template.getMeteredness());
+ out.writeInt(template.getSubscriberIdMatchRule());
+
+ return baos.toByteArray();
+ }
+
+ @NonNull
+ private static NetworkTemplate getNetworkTemplateFromBackup(DataInputStream in)
+ throws IOException, BackupUtils.BadVersionException {
+ int version = in.readInt();
+ if (version < TEMPLATE_BACKUP_VERSION_1_INIT || version > TEMPLATE_BACKUP_VERSION_LATEST) {
+ throw new BackupUtils.BadVersionException("Unknown Backup Serialization Version");
+ }
+
+ int matchRule = in.readInt();
+ final String subscriberId = BackupUtils.readString(in);
+ final String networkId = BackupUtils.readString(in);
+
+ final int metered;
+ final int subscriberIdMatchRule;
+ if (version >= TEMPLATE_BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE) {
+ metered = in.readInt();
+ subscriberIdMatchRule = in.readInt();
+ } else {
+ // For backward compatibility, fill the missing filters from match rules.
+ metered = (matchRule == MATCH_MOBILE
+ || matchRule == NetworkTemplate.MATCH_MOBILE_WILDCARD
+ || matchRule == MATCH_CARRIER) ? METERED_YES : METERED_ALL;
+ subscriberIdMatchRule = SUBSCRIBER_ID_MATCH_RULE_EXACT;
+ }
+
+ try {
+ return new NetworkTemplate(matchRule,
+ subscriberId, new String[]{subscriberId},
+ networkId, metered, NetworkStats.ROAMING_ALL,
+ NetworkStats.DEFAULT_NETWORK_ALL, NetworkTemplate.NETWORK_TYPE_ALL,
+ NetworkTemplate.OEM_MANAGED_ALL, subscriberIdMatchRule);
+ } catch (IllegalArgumentException e) {
+ throw new BackupUtils.BadVersionException(
+ "Restored network template contains unknown match rule " + matchRule, e);
+ }
+ }
}
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java
index 50a6bfc..b3f7345 100644
--- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java
@@ -151,7 +151,7 @@
/** Retrieve the allowed PLMN IDs, or an empty set if any PLMN ID is acceptable. */
@NonNull
- public Set<String> getAllowedPlmnIds() {
+ public Set<String> getAllowedOperatorPlmnIds() {
return Collections.unmodifiableSet(mAllowedNetworkPlmnIds);
}
@@ -211,7 +211,7 @@
}
/** This class is used to incrementally build WifiNetworkPriority objects. */
- public static class Builder extends VcnUnderlyingNetworkPriority.Builder<Builder> {
+ public static final class Builder extends VcnUnderlyingNetworkPriority.Builder<Builder> {
@NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>();
@NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>();
@@ -233,7 +233,7 @@
* and {@link SubscriptionInfo#getMncString()}.
*/
@NonNull
- public Builder setAllowedPlmnIds(@NonNull Set<String> allowedNetworkPlmnIds) {
+ public Builder setAllowedOperatorPlmnIds(@NonNull Set<String> allowedNetworkPlmnIds) {
validatePlmnIds(allowedNetworkPlmnIds);
mAllowedNetworkPlmnIds.clear();
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 31e38c0..55d3ecd 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -198,7 +198,10 @@
private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities";
@NonNull private final SortedSet<Integer> mExposedCapabilities;
- private static final String UNDERLYING_NETWORK_PRIORITIES_KEY = "mUnderlyingNetworkPriorities";
+ /** @hide */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static final String UNDERLYING_NETWORK_PRIORITIES_KEY = "mUnderlyingNetworkPriorities";
+
@NonNull private final LinkedHashSet<VcnUnderlyingNetworkPriority> mUnderlyingNetworkPriorities;
private static final String MAX_MTU_KEY = "mMaxMtu";
@@ -229,6 +232,8 @@
validate();
}
+ // Null check MUST be done for all new fields added to VcnGatewayConnectionConfig, to avoid
+ // crashes when parsing PersistableBundle built on old platforms.
/** @hide */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public VcnGatewayConnectionConfig(@NonNull PersistableBundle in) {
@@ -239,19 +244,30 @@
final PersistableBundle exposedCapsBundle =
in.getPersistableBundle(EXPOSED_CAPABILITIES_KEY);
- final PersistableBundle networkPrioritiesBundle =
- in.getPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY);
-
mGatewayConnectionName = in.getString(GATEWAY_CONNECTION_NAME_KEY);
mTunnelConnectionParams =
TunnelConnectionParamsUtils.fromPersistableBundle(tunnelConnectionParamsBundle);
mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
- mUnderlyingNetworkPriorities =
- new LinkedHashSet<>(
- PersistableBundleUtils.toList(
- networkPrioritiesBundle,
- VcnUnderlyingNetworkPriority::fromPersistableBundle));
+
+ final PersistableBundle networkPrioritiesBundle =
+ in.getPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY);
+
+ if (networkPrioritiesBundle == null) {
+ // UNDERLYING_NETWORK_PRIORITIES_KEY was added in Android T. Thus
+ // VcnGatewayConnectionConfig created on old platforms will not have this data and will
+ // be assigned with the default value
+ mUnderlyingNetworkPriorities =
+ new LinkedHashSet<>(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
+
+ } else {
+ mUnderlyingNetworkPriorities =
+ new LinkedHashSet<>(
+ PersistableBundleUtils.toList(
+ networkPrioritiesBundle,
+ VcnUnderlyingNetworkPriority::fromPersistableBundle));
+ }
+
mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
mMaxMtu = in.getInt(MAX_MTU_KEY);
@@ -420,6 +436,7 @@
mGatewayConnectionName,
mTunnelConnectionParams,
mExposedCapabilities,
+ mUnderlyingNetworkPriorities,
Arrays.hashCode(mRetryIntervalsMs),
mMaxMtu);
}
@@ -434,6 +451,7 @@
return mGatewayConnectionName.equals(rhs.mGatewayConnectionName)
&& mTunnelConnectionParams.equals(rhs.mTunnelConnectionParams)
&& mExposedCapabilities.equals(rhs.mExposedCapabilities)
+ && mUnderlyingNetworkPriorities.equals(rhs.mUnderlyingNetworkPriorities)
&& Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
&& mMaxMtu == rhs.mMaxMtu;
}
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java
index 2ba9169..85eb100 100644
--- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java
+++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java
@@ -79,7 +79,7 @@
}
final VcnWifiUnderlyingNetworkPriority rhs = (VcnWifiUnderlyingNetworkPriority) other;
- return mSsid == rhs.mSsid;
+ return mSsid.equals(rhs.mSsid);
}
/** @hide */
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index b069fb3..e2d7847 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -22,9 +22,11 @@
import android.app.AppOpsManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.ExceptionUtils;
+import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BinderCallHeavyHitterWatcher;
import com.android.internal.os.BinderCallHeavyHitterWatcher.BinderCallHeavyHitterListener;
import com.android.internal.os.BinderInternal;
@@ -140,33 +142,55 @@
/**
* Flag indicating whether we should be tracing transact calls.
*/
- private static volatile boolean sTracingEnabled = false;
+ private static volatile boolean sStackTrackingEnabled = false;
+
+ private static final Object sTracingUidsWriteLock = new Object();
+ private static volatile IntArray sTracingUidsImmutable = new IntArray();
/**
- * Enable Binder IPC tracing.
+ * Enable Binder IPC stack tracking. If enabled, every binder transaction will be logged to
+ * {@link TransactionTracker}.
*
* @hide
*/
- public static void enableTracing() {
- sTracingEnabled = true;
+ public static void enableStackTracking() {
+ sStackTrackingEnabled = true;
}
/**
- * Disable Binder IPC tracing.
+ * Disable Binder IPC stack tracking.
*
* @hide
*/
- public static void disableTracing() {
- sTracingEnabled = false;
+ public static void disableStackTracking() {
+ sStackTrackingEnabled = false;
}
/**
- * Check if binder transaction tracing is enabled.
+ * @hide
+ */
+ public static void enableTracingForUid(int uid) {
+ synchronized (sTracingUidsWriteLock) {
+ final IntArray copy = sTracingUidsImmutable.clone();
+ copy.add(uid);
+ sTracingUidsImmutable = copy;
+ }
+ }
+
+ /**
+ * Check if binder transaction stack tracking is enabled.
*
* @hide
*/
- public static boolean isTracingEnabled() {
- return sTracingEnabled;
+ public static boolean isStackTrackingEnabled() {
+ return sStackTrackingEnabled;
+ }
+
+ /**
+ * @hide
+ */
+ public static boolean isTracingEnabled(int callingUid) {
+ return sTracingUidsImmutable.indexOf(callingUid) != -1;
}
/**
@@ -288,6 +312,9 @@
private IInterface mOwner;
private String mDescriptor;
+ private volatile String[] mTransactionTraceNames = null;
+ private volatile String mSimpleDescriptor = null;
+ private static final int TRANSACTION_TRACE_NAME_ID_LIMIT = 1024;
/**
* Return the ID of the process that sent you the current transaction
@@ -884,6 +911,53 @@
}
/**
+ * @hide
+ */
+ @VisibleForTesting
+ public final @NonNull String getTransactionTraceName(int transactionCode) {
+ if (mTransactionTraceNames == null) {
+ final String descriptor = getSimpleDescriptor();
+ final int highestId = Math.min(getMaxTransactionId(), TRANSACTION_TRACE_NAME_ID_LIMIT);
+ final String[] transactionNames = new String[highestId + 1];
+ final StringBuffer buf = new StringBuffer();
+ for (int i = 0; i <= highestId; i++) {
+ String transactionName = getTransactionName(i + FIRST_CALL_TRANSACTION);
+ if (transactionName != null) {
+ buf.append(descriptor).append(':').append(transactionName);
+ } else {
+ buf.append(descriptor).append('#').append(i + FIRST_CALL_TRANSACTION);
+ }
+ transactionNames[i] = buf.toString();
+ buf.setLength(0);
+ }
+ mTransactionTraceNames = transactionNames;
+ mSimpleDescriptor = descriptor;
+ }
+ final int index = transactionCode - FIRST_CALL_TRANSACTION;
+ if (index < 0 || index >= mTransactionTraceNames.length) {
+ return mSimpleDescriptor + "#" + transactionCode;
+ }
+ return mTransactionTraceNames[index];
+ }
+
+ private String getSimpleDescriptor() {
+ final int dot = mDescriptor.lastIndexOf(".");
+ if (dot > 0) {
+ // Strip the package name
+ return mDescriptor.substring(dot + 1);
+ }
+ return mDescriptor;
+ }
+
+ /**
+ * @return The highest user-defined transaction id of all transactions.
+ * @hide
+ */
+ public int getMaxTransactionId() {
+ return 0;
+ }
+
+ /**
* Implemented to call the more convenient version
* {@link #dump(FileDescriptor, PrintWriter, String[])}.
*/
@@ -1181,7 +1255,8 @@
// Log any exceptions as warnings, don't silently suppress them.
// If the call was {@link IBinder#FLAG_ONEWAY} then these exceptions
// disappear into the ether.
- final boolean tracingEnabled = Binder.isTracingEnabled();
+ final boolean tracingEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_AIDL) &&
+ (Binder.isStackTrackingEnabled() || Binder.isTracingEnabled(callingUid));
try {
final BinderCallHeavyHitterWatcher heavyHitterWatcher = sHeavyHitterWatcher;
if (heavyHitterWatcher != null) {
@@ -1189,9 +1264,7 @@
heavyHitterWatcher.onTransaction(callingUid, getClass(), code);
}
if (tracingEnabled) {
- final String transactionName = getTransactionName(code);
- Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":"
- + (transactionName != null ? transactionName : code));
+ Trace.traceBegin(Trace.TRACE_TAG_AIDL, getTransactionTraceName(code));
}
if ((flags & FLAG_COLLECT_NOTED_APP_OPS) != 0) {
@@ -1226,7 +1299,7 @@
res = true;
} finally {
if (tracingEnabled) {
- Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
+ Trace.traceEnd(Trace.TRACE_TAG_AIDL);
}
if (observer != null) {
// The parcel RPC headers have been called during onTransact so we can now access
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 483b549..e929920 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -545,7 +545,7 @@
}
}
- final boolean tracingEnabled = Binder.isTracingEnabled();
+ final boolean tracingEnabled = Binder.isStackTrackingEnabled();
if (tracingEnabled) {
final Throwable tr = new Throwable();
Binder.getTransactionTracker().addTrace(tr);
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 8292f26..aa4b83a 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -192,14 +192,15 @@
// We only want to use ANGLE if the developer has explicitly chosen something other than
// default driver.
- final boolean requested = devOptIn.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE);
- if (requested) {
+ final boolean forceAngle = devOptIn.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE);
+ final boolean forceNative = devOptIn.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE);
+ if (forceAngle || forceNative) {
Log.v(TAG, "ANGLE developer option for " + packageName + ": " + devOptIn);
}
final boolean gameModeEnabledAngle = isAngleEnabledByGameMode(context, packageName);
- return requested || gameModeEnabledAngle;
+ return !forceNative && (forceAngle || gameModeEnabledAngle);
}
private int getVulkanVersion(PackageManager pm) {
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 8894c85..09eac79 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -4210,8 +4210,7 @@
* trying to instantiate an element.
*/
@Nullable
- public <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader,
- @NonNull Class<T> clazz) {
+ public <T> T readParcelable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
Objects.requireNonNull(clazz);
return readParcelableInternal(loader, clazz);
}
@@ -4222,10 +4221,6 @@
@SuppressWarnings("unchecked")
@Nullable
private <T> T readParcelableInternal(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
- if (clazz != null && !Parcelable.class.isAssignableFrom(clazz)) {
- throw new BadParcelableException("About to unparcel a parcelable object "
- + " but class required " + clazz.getName() + " is not Parcelable");
- }
Parcelable.Creator<?> creator = readParcelableCreatorInternal(loader, clazz);
if (creator == null) {
return null;
@@ -4388,9 +4383,16 @@
* The given class loader will be used to load any enclosed
* Parcelables.
* @return the Parcelable array, or null if the array is null
+ *
+ * @deprecated Use the type-safer version {@link #readParcelableArray(ClassLoader, Class)}
+ * starting from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the
+ * format to use {@link #createTypedArray(Parcelable.Creator)} if possible (eg. if the
+ * items' class is final) since this is also more performant. Note that changing to the
+ * latter also requires changing the writes.
*/
+ @Deprecated
@Nullable
- public final Parcelable[] readParcelableArray(@Nullable ClassLoader loader) {
+ public Parcelable[] readParcelableArray(@Nullable ClassLoader loader) {
int N = readInt();
if (N < 0) {
return null;
@@ -4454,8 +4456,7 @@
* deserializing the object.
*/
@Nullable
- public <T extends Serializable> T readSerializable(@Nullable ClassLoader loader,
- @NonNull Class<T> clazz) {
+ public <T> T readSerializable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
Objects.requireNonNull(clazz);
return readSerializableInternal(
loader == null ? getClass().getClassLoader() : loader, clazz);
@@ -4467,11 +4468,6 @@
@Nullable
private <T> T readSerializableInternal(@Nullable final ClassLoader loader,
@Nullable Class<T> clazz) {
- if (clazz != null && !Serializable.class.isAssignableFrom(clazz)) {
- throw new BadParcelableException("About to unparcel a serializable object "
- + " but class required " + clazz.getName() + " is not Serializable");
- }
-
String name = readString();
if (name == null) {
// For some reason we were unable to read the name of the Serializable (either there
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 29d4d78..563d6cc3 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2073,7 +2073,8 @@
@TestApi
@RequiresPermission(anyOf = {
android.Manifest.permission.MANAGE_USERS,
- android.Manifest.permission.CREATE_USERS})
+ android.Manifest.permission.CREATE_USERS,
+ android.Manifest.permission.QUERY_USERS})
@UserHandleAware
public @NonNull String getUserType() {
UserInfo userInfo = getUserInfo(mUserId);
@@ -2082,15 +2083,21 @@
/**
* Returns the user name of the context user. This call is only available to applications on
- * the system image; it requires the {@code android.permission.MANAGE_USERS} or {@code
- * android.permission.GET_ACCOUNTS_PRIVILEGED} permissions.
+ * the system image.
*
* @return the user name
*/
- @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
- android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED,
- android.Manifest.permission.CREATE_USERS}, conditional = true)
- @UserHandleAware
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS,
+ android.Manifest.permission.QUERY_USERS,
+ android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED})
+
+ @UserHandleAware(
+ requiresAnyOfPermissionsIfNotCaller = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS,
+ android.Manifest.permission.QUERY_USERS})
public @NonNull String getUserName() {
if (UserHandle.myUserId() == mUserId) {
try {
@@ -2153,8 +2160,10 @@
* @hide
*/
@SystemApi
- @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS})
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS})
@UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public boolean isPrimaryUser() {
final UserInfo user = getUserInfo(getContextUserIfAppropriate());
@@ -2184,7 +2193,8 @@
@SystemApi
@RequiresPermission(anyOf = {
Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS})
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS})
@UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public boolean isAdminUser() {
return isUserAdmin(getContextUserIfAppropriate());
@@ -2196,8 +2206,10 @@
* user.
*/
@UnsupportedAppUsage
- @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS})
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS})
public boolean isUserAdmin(@UserIdInt int userId) {
UserInfo user = getUserInfo(userId);
return user != null && user.isAdmin();
@@ -2394,8 +2406,10 @@
* @hide
*/
@SystemApi
- @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS})
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS})
@UserHandleAware
public @Nullable UserHandle getRestrictedProfileParent() {
final UserInfo info = getUserInfo(mUserId);
@@ -2412,8 +2426,10 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS})
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS})
public boolean isGuestUser(@UserIdInt int userId) {
UserInfo user = getUserInfo(userId);
return user != null && user.isGuest();
@@ -2426,8 +2442,10 @@
* @hide
*/
@SystemApi
- @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS})
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS})
@UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public boolean isGuestUser() {
UserInfo user = getUserInfo(getContextUserIfAppropriate());
@@ -2456,17 +2474,15 @@
/**
* Checks if the calling context user is running in a profile.
*
- * Requires {@link android.Manifest.permission#MANAGE_USERS} or
- * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the
- * caller must be in the same profile group of specified user.
- *
* @return whether the caller is in a profile.
* @hide
*/
@SystemApi
- @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
- android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
- @UserHandleAware
+ @UserHandleAware(
+ requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.QUERY_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS})
public boolean isProfile() {
return isProfile(mUserId);
}
@@ -2502,8 +2518,8 @@
enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
requiresAnyOfPermissionsIfNotCallerProfileGroup = {
android.Manifest.permission.MANAGE_USERS,
- android.Manifest.permission.INTERACT_ACROSS_USERS}
- )
+ android.Manifest.permission.QUERY_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS})
public boolean isManagedProfile() {
return isManagedProfile(getContextUserIfAppropriate());
}
@@ -2511,15 +2527,18 @@
/**
* Checks if the specified user is a managed profile.
* Requires {@link android.Manifest.permission#MANAGE_USERS} or
- * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or
+ * {@link android.Manifest.permission#QUERY_USERS} permission, otherwise the caller
* must be in the same profile group of specified user.
*
* @return whether the specified user is a managed profile.
* @hide
*/
@SystemApi
- @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
- Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.QUERY_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public boolean isManagedProfile(@UserIdInt int userId) {
if (userId == mUserId) {
// No need for synchronization. Once it becomes non-null, it'll be non-null forever.
@@ -2573,8 +2592,10 @@
* @return whether the context user is an ephemeral user.
* @hide
*/
- @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS})
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS})
@UserHandleAware
public boolean isEphemeralUser() {
return isUserEphemeral(mUserId);
@@ -2584,8 +2605,10 @@
* Returns whether the specified user is ephemeral.
* @hide
*/
- @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS})
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS})
public boolean isUserEphemeral(@UserIdInt int userId) {
final UserInfo user = getUserInfo(userId);
return user != null && user.isEphemeral();
@@ -2853,8 +2876,10 @@
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS})
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS})
public UserInfo getUserInfo(@UserIdInt int userId) {
try {
return mService.getUserInfo(userId);
@@ -2877,7 +2902,9 @@
@Deprecated
@SystemApi
@UserRestrictionSource
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.QUERY_USERS})
public int getUserRestrictionSource(@UserRestrictionKey String restrictionKey,
UserHandle userHandle) {
try {
@@ -2896,7 +2923,9 @@
* @return a list of user ids enforcing this restriction.
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.QUERY_USERS})
public List<EnforcingUser> getUserRestrictionSources(
@UserRestrictionKey String restrictionKey, UserHandle userHandle) {
try {
@@ -3963,15 +3992,17 @@
}
/**
- * Checks whether it's possible to add more managed profiles. Caller must hold the MANAGE_USERS
- * permission.
+ * Checks whether it's possible to add more managed profiles.
* if allowedToRemoveOne is true and if the user already has a managed profile, then return if
* we could add a new managed profile to this user after removing the existing one.
*
* @return true if more managed profiles can be added, false if limit has been reached.
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.QUERY_USERS
+ })
public boolean canAddMoreManagedProfiles(@UserIdInt int userId, boolean allowedToRemoveOne) {
try {
return mService.canAddMoreManagedProfiles(userId, allowedToRemoveOne);
@@ -3987,7 +4018,10 @@
* @return true if more profiles can be added, false if limit has been reached.
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.QUERY_USERS
+ })
public boolean canAddMoreProfilesToUser(@NonNull String userType, @UserIdInt int userId) {
try {
return mService.canAddMoreProfilesToUser(userType, userId, false);
@@ -4022,14 +4056,17 @@
* <p>Note that this includes all profile types (not including Restricted profiles).
*
* <p>Requires {@link android.Manifest.permission#MANAGE_USERS} or
- * {@link android.Manifest.permission#CREATE_USERS} if userId is not the calling user.
+ * {@link android.Manifest.permission#CREATE_USERS} or
+ * {@link android.Manifest.permission#QUERY_USERS} if userId is not the calling user.
* @param userId profiles of this user will be returned.
* @return the list of profiles.
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS}, conditional = true)
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS}, conditional = true)
public List<UserInfo> getProfiles(@UserIdInt int userId) {
try {
return mService.getProfiles(userId, false /* enabledOnly */);
@@ -4048,7 +4085,9 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.QUERY_USERS})
public boolean isSameProfileGroup(@NonNull UserHandle user, @NonNull UserHandle otherUser) {
return isSameProfileGroup(user.getIdentifier(), otherUser.getIdentifier());
}
@@ -4060,7 +4099,9 @@
* @return true if the two user ids are in the same profile group.
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.QUERY_USERS})
public boolean isSameProfileGroup(@UserIdInt int userId, int otherUserId) {
try {
return mService.isSameProfileGroup(userId, otherUserId);
@@ -4075,14 +4116,20 @@
* <p>Note that this includes all profile types (not including Restricted profiles).
*
* <p>Requires {@link android.Manifest.permission#MANAGE_USERS} or
- * {@link android.Manifest.permission#CREATE_USERS} if userId is not the calling user.
+ * {@link android.Manifest.permission#CREATE_USERS} or
+ * {@link android.Manifest.permission#QUERY_USERS} if userId is not the calling user.
* @param userId profiles of this user will be returned.
* @return the list of profiles.
+ * @deprecated use {@link #getUserProfiles()} instead.
+ *
* @hide
*/
+ @Deprecated
@UnsupportedAppUsage
- @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS}, conditional = true)
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS}, conditional = true)
public List<UserInfo> getEnabledProfiles(@UserIdInt int userId) {
try {
return mService.getProfiles(userId, true /* enabledOnly */);
@@ -4102,8 +4149,8 @@
enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
requiresAnyOfPermissionsIfNotCaller = {
Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS}
- )
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS})
public List<UserHandle> getUserProfiles() {
int[] userIds = getProfileIds(getContextUserIfAppropriate(), true /* enabledOnly */);
return convertUserIdsToUserHandles(userIds);
@@ -4118,9 +4165,11 @@
* @hide
*/
@SystemApi
- @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS}, conditional = true)
- @UserHandleAware
+ @UserHandleAware(
+ requiresAnyOfPermissionsIfNotCaller = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS})
public @NonNull List<UserHandle> getEnabledProfiles() {
return getProfiles(true);
}
@@ -4134,9 +4183,11 @@
* @hide
*/
@SystemApi
- @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS}, conditional = true)
- @UserHandleAware
+ @UserHandleAware(
+ requiresAnyOfPermissionsIfNotCaller = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS})
public @NonNull List<UserHandle> getAllProfiles() {
return getProfiles(false);
}
@@ -4149,9 +4200,11 @@
* @param enabledOnly whether to return only {@link UserInfo#isEnabled() enabled} profiles
* @return A non-empty list of UserHandles associated with the context user.
*/
- @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS}, conditional = true)
- @UserHandleAware
+ @UserHandleAware(
+ requiresAnyOfPermissionsIfNotCaller = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS})
private @NonNull List<UserHandle> getProfiles(boolean enabledOnly) {
final int[] userIds = getProfileIds(mUserId, enabledOnly);
return convertUserIdsToUserHandles(userIds);
@@ -4177,8 +4230,10 @@
*
* @hide
*/
- @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS}, conditional = true)
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS}, conditional = true)
public @NonNull int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly) {
try {
return mService.getProfileIds(userId, enabledOnly);
@@ -4192,8 +4247,10 @@
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS}, conditional = true)
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS}, conditional = true)
public int[] getProfileIdsWithDisabled(@UserIdInt int userId) {
return getProfileIds(userId, false /* enabledOnly */);
}
@@ -4202,8 +4259,10 @@
* @see #getProfileIds(int, boolean)
* @hide
*/
- @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS}, conditional = true)
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS}, conditional = true)
public int[] getEnabledProfileIds(@UserIdInt int userId) {
return getProfileIds(userId, true /* enabledOnly */);
}
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 20f6c10..f0e6624 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -52,6 +52,8 @@
import android.util.ArrayMap;
import android.util.ArraySet;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -150,7 +152,9 @@
private ArrayMap<UserHandle, Context> mUserContexts;
private PackageManager mPkgManager;
private AppOpsManager mAppOpsManager;
- private ArrayMap<Integer, ArrayList<AccessChainLink>> mAttributionChains = new ArrayMap<>();
+ @GuardedBy("mAttributionChains")
+ private final ArrayMap<Integer, ArrayList<AccessChainLink>> mAttributionChains =
+ new ArrayMap<>();
/**
* Constructor for PermissionUsageHelper
@@ -199,22 +203,24 @@
// if any link in the chain is finished, remove the chain. Then, find any other chains that
// contain this op/package/uid/tag combination, and remove them, as well.
// TODO ntmyren: be smarter about this
- mAttributionChains.remove(attributionChainId);
- int numChains = mAttributionChains.size();
- ArrayList<Integer> toRemove = new ArrayList<>();
- for (int i = 0; i < numChains; i++) {
- int chainId = mAttributionChains.keyAt(i);
- ArrayList<AccessChainLink> chain = mAttributionChains.valueAt(i);
- int chainSize = chain.size();
- for (int j = 0; j < chainSize; j++) {
- AccessChainLink link = chain.get(j);
- if (link.packageAndOpEquals(op, packageName, attributionTag, uid)) {
- toRemove.add(chainId);
- break;
+ synchronized (mAttributionChains) {
+ mAttributionChains.remove(attributionChainId);
+ int numChains = mAttributionChains.size();
+ ArrayList<Integer> toRemove = new ArrayList<>();
+ for (int i = 0; i < numChains; i++) {
+ int chainId = mAttributionChains.keyAt(i);
+ ArrayList<AccessChainLink> chain = mAttributionChains.valueAt(i);
+ int chainSize = chain.size();
+ for (int j = 0; j < chainSize; j++) {
+ AccessChainLink link = chain.get(j);
+ if (link.packageAndOpEquals(op, packageName, attributionTag, uid)) {
+ toRemove.add(chainId);
+ break;
+ }
}
}
+ mAttributionChains.removeAll(toRemove);
}
- mAttributionChains.removeAll(toRemove);
}
@Override
@@ -234,11 +240,13 @@
// If this is not a successful start, or it is not a chain, or it is untrusted, return
return;
}
- addLinkToChainIfNotPresent(AppOpsManager.opToPublicName(op), packageName, uid,
- attributionTag, attributionFlags, attributionChainId);
+ synchronized (mAttributionChains) {
+ addLinkToChainIfNotPresentLocked(AppOpsManager.opToPublicName(op), packageName, uid,
+ attributionTag, attributionFlags, attributionChainId);
+ }
}
- private void addLinkToChainIfNotPresent(String op, String packageName, int uid,
+ private void addLinkToChainIfNotPresentLocked(String op, String packageName, int uid,
String attributionTag, int attributionFlags, int attributionChainId) {
ArrayList<AccessChainLink> currentChain = mAttributionChains.computeIfAbsent(
@@ -544,42 +552,44 @@
}
}
- for (int i = 0; i < mAttributionChains.size(); i++) {
- List<AccessChainLink> usageList = mAttributionChains.valueAt(i);
- int lastVisible = usageList.size() - 1;
- // TODO ntmyren: remove this mic code once camera is converted to AttributionSource
- // if the list is empty or incomplete, do not show it.
- if (usageList.isEmpty() || !usageList.get(lastVisible).isEnd()
- || !usageList.get(0).isStart()
- || !usageList.get(lastVisible).usage.op.equals(OPSTR_RECORD_AUDIO)) {
- continue;
- }
-
- //TODO ntmyren: remove once camera etc. etc.
- for (AccessChainLink link: usageList) {
- proxyPackages.add(link.usage.getPackageIdHash());
- }
-
- AccessChainLink start = usageList.get(0);
- AccessChainLink lastVisibleLink = usageList.get(lastVisible);
- while (lastVisible > 0 && !shouldShowPackage(lastVisibleLink.usage.packageName)) {
- lastVisible--;
- lastVisibleLink = usageList.get(lastVisible);
- }
- String proxyLabel = null;
- if (!lastVisibleLink.usage.packageName.equals(start.usage.packageName)) {
- try {
- PackageManager userPkgManager =
- getUserContext(lastVisibleLink.usage.getUser()).getPackageManager();
- ApplicationInfo appInfo = userPkgManager.getApplicationInfo(
- lastVisibleLink.usage.packageName, 0);
- proxyLabel = appInfo.loadLabel(userPkgManager).toString();
- } catch (PackageManager.NameNotFoundException e) {
- // do nothing
+ synchronized (mAttributionChains) {
+ for (int i = 0; i < mAttributionChains.size(); i++) {
+ List<AccessChainLink> usageList = mAttributionChains.valueAt(i);
+ int lastVisible = usageList.size() - 1;
+ // TODO ntmyren: remove this mic code once camera is converted to AttributionSource
+ // if the list is empty or incomplete, do not show it.
+ if (usageList.isEmpty() || !usageList.get(lastVisible).isEnd()
+ || !usageList.get(0).isStart()
+ || !usageList.get(lastVisible).usage.op.equals(OPSTR_RECORD_AUDIO)) {
+ continue;
}
+ //TODO ntmyren: remove once camera etc. etc.
+ for (AccessChainLink link : usageList) {
+ proxyPackages.add(link.usage.getPackageIdHash());
+ }
+
+ AccessChainLink start = usageList.get(0);
+ AccessChainLink lastVisibleLink = usageList.get(lastVisible);
+ while (lastVisible > 0 && !shouldShowPackage(lastVisibleLink.usage.packageName)) {
+ lastVisible--;
+ lastVisibleLink = usageList.get(lastVisible);
+ }
+ String proxyLabel = null;
+ if (!lastVisibleLink.usage.packageName.equals(start.usage.packageName)) {
+ try {
+ PackageManager userPkgManager =
+ getUserContext(lastVisibleLink.usage.getUser()).getPackageManager();
+ ApplicationInfo appInfo = userPkgManager.getApplicationInfo(
+ lastVisibleLink.usage.packageName, 0);
+ proxyLabel = appInfo.loadLabel(userPkgManager).toString();
+ } catch (PackageManager.NameNotFoundException e) {
+ // do nothing
+ }
+
+ }
+ usagesAndLabels.put(start.usage, proxyLabel);
}
- usagesAndLabels.put(start.usage, proxyLabel);
}
for (int packageHash : mostRecentUsages.keySet()) {
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 21c1feb..22b9578 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -671,6 +671,14 @@
@TestApi
public static final String NAMESPACE_APP_COMPAT_OVERRIDES = "app_compat_overrides";
+ /**
+ * Namespace for all ultra wideband (uwb) related features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_UWB = "uwb";
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f5bcb54..ad6d85f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9327,6 +9327,16 @@
"emergency_gesture_sound_enabled";
/**
+ * The power button "cooldown" period in milliseconds after the Emergency gesture is
+ * triggered, during which single-key actions on the power button are suppressed. Cooldown
+ * period is disabled if set to zero.
+ *
+ * @hide
+ */
+ public static final String EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS =
+ "emergency_gesture_power_button_cooldown_period_ms";
+
+ /**
* Whether the camera launch gesture to double tap the power button when the screen is off
* should be disabled.
*
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 65a857e..e64d685 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1749,8 +1749,9 @@
/**
* Called when there has been a failure transferring the {@link AssistStructure} to
* the assistant. This may happen, for example, if the data is too large and results
- * in an out of memory exception, or the client has provided corrupt data. This will
- * be called immediately before {@link #onHandleAssist} and the AssistStructure supplied
+ * in an out of memory exception, the data has been cleared during transferring due to
+ * the new incoming assist data, or the client has provided corrupt data. This will be
+ * called immediately before {@link #onHandleAssist} and the AssistStructure supplied
* there afterwards will be null.
*
* @param failure The failure exception that was thrown when building the
@@ -1788,7 +1789,8 @@
* Called to receive data from the application that the user was currently viewing when
* an assist session is started. If the original show request did not specify
* {@link #SHOW_WITH_ASSIST}, {@link AssistState} parameter will only provide
- * {@link ActivityId}.
+ * {@link ActivityId}. If there was a failure to write the assist data to
+ * {@link AssistStructure}, the {@link AssistState#getAssistStructure()} will return null.
*
* <p>This method is called for all activities along with an index and count that indicates
* which activity the data is for. {@code index} will be between 0 and {@code count}-1 and
@@ -2213,7 +2215,8 @@
* @return If available, the structure definition of all windows currently
* displayed by the app. May be null if assist data has been disabled by the user
* or device policy; will be null if the original show request did not specify
- * {@link #SHOW_WITH_ASSIST}; will be an empty stub if the application has disabled assist
+ * {@link #SHOW_WITH_ASSIST} or the assist data has been corrupt when writing the data to
+ * {@link AssistStructure}; will be an empty stub if the application has disabled assist
* by marking its window as secure.
*/
public @Nullable AssistStructure getAssistStructure() {
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 83a6bc0..73ffd66 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -44,7 +44,6 @@
import android.graphics.BLASTBufferQueue;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.GraphicBuffer;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Point;
@@ -1930,9 +1929,7 @@
mScreenshotSize.set(mSurfaceSize.x, mSurfaceSize.y);
- GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(hardwareBuffer);
-
- t.setBuffer(mScreenshotSurfaceControl, graphicBuffer);
+ t.setBuffer(mScreenshotSurfaceControl, hardwareBuffer);
t.setColorSpace(mScreenshotSurfaceControl, screenshotBuffer.getColorSpace());
// Place on top everything else.
t.setLayer(mScreenshotSurfaceControl, Integer.MAX_VALUE);
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index cb1cff9..859fd80 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -247,8 +247,8 @@
} else if (listener.mSubId != null) {
subId = listener.mSubId;
}
- sRegistry.listenWithEventList(
- subId, pkg, featureId, listener.callback, eventsList, notifyNow);
+ sRegistry.listenWithEventList(false, false, subId, pkg, featureId,
+ listener.callback, eventsList, notifyNow);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -263,11 +263,13 @@
* @param events List events
* @param notifyNow Whether to notify instantly
*/
- private void listenFromCallback(int subId, @NonNull String pkg, @NonNull String featureId,
+ private void listenFromCallback(boolean renounceFineLocationAccess,
+ boolean renounceCoarseLocationAccess, int subId,
+ @NonNull String pkg, @NonNull String featureId,
@NonNull TelephonyCallback telephonyCallback, @NonNull int[] events,
boolean notifyNow) {
try {
- sRegistry.listenWithEventList(
+ sRegistry.listenWithEventList(renounceFineLocationAccess, renounceCoarseLocationAccess,
subId, pkg, featureId, telephonyCallback.callback, events, notifyNow);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1161,14 +1163,17 @@
*
* @param callback The {@link TelephonyCallback} object to register.
*/
- public void registerTelephonyCallback(@NonNull @CallbackExecutor Executor executor,
+ public void registerTelephonyCallback(boolean renounceFineLocationAccess,
+ boolean renounceCoarseLocationAccess,
+ @NonNull @CallbackExecutor Executor executor,
int subId, String pkgName, String attributionTag, @NonNull TelephonyCallback callback,
boolean notifyNow) {
if (callback == null) {
throw new IllegalStateException("telephony service is null.");
}
callback.init(executor);
- listenFromCallback(subId, pkgName, attributionTag, callback,
+ listenFromCallback(renounceFineLocationAccess, renounceCoarseLocationAccess, subId,
+ pkgName, attributionTag, callback,
getEventsFromCallback(callback).stream().mapToInt(i -> i).toArray(), notifyNow);
}
@@ -1179,6 +1184,7 @@
*/
public void unregisterTelephonyCallback(int subId, String pkgName, String attributionTag,
@NonNull TelephonyCallback callback, boolean notifyNow) {
- listenFromCallback(subId, pkgName, attributionTag, callback, new int[0], notifyNow);
+ listenFromCallback(false, false, subId,
+ pkgName, attributionTag, callback, new int[0], notifyNow);
}
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 6984e4d..4789231 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -874,6 +874,18 @@
? Math.max(fmDescent, Math.round(descents[breakIndex]))
: fmDescent;
+ // The fallback ascent/descent may be larger than top/bottom of the default font
+ // metrics. Adjust top/bottom with ascent/descent for avoiding unexpected
+ // clipping.
+ if (fallbackLineSpacing) {
+ if (ascent < fmTop) {
+ fmTop = ascent;
+ }
+ if (descent > fmBottom) {
+ fmBottom = descent;
+ }
+ }
+
v = out(source, here, endPos,
ascent, descent, fmTop, fmBottom,
v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 7e2792c..8124510 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -58,6 +58,10 @@
*/
public static final String SETTINGS_APP_LANGUAGE_SELECTION = "settings_app_language_selection";
+ /** @hide */
+ public static final String SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS =
+ "settings_enable_monitor_phantom_procs";
+
private static final Map<String, String> DEFAULT_FLAGS;
static {
@@ -81,6 +85,7 @@
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "true");
DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "true");
DEFAULT_FLAGS.put(SETTINGS_APP_LANGUAGE_SELECTION, "false");
+ DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
}
private static final Set<String> PERSISTENT_FLAGS;
@@ -89,6 +94,7 @@
PERSISTENT_FLAGS.add(SETTINGS_APP_LANGUAGE_SELECTION);
PERSISTENT_FLAGS.add(SETTINGS_PROVIDER_MODEL);
PERSISTENT_FLAGS.add(SETTINGS_SUPPORT_LARGE_SCREEN);
+ PERSISTENT_FLAGS.add(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
}
/**
diff --git a/core/java/android/util/IntArray.java b/core/java/android/util/IntArray.java
index b77265b..7b28b8a 100644
--- a/core/java/android/util/IntArray.java
+++ b/core/java/android/util/IntArray.java
@@ -34,7 +34,7 @@
private int[] mValues;
private int mSize;
- private IntArray(int[] array, int size) {
+ private IntArray(int[] array, int size) {
mValues = array;
mSize = Preconditions.checkArgumentInRange(size, 0, array.length, "size");
}
@@ -178,10 +178,8 @@
}
@Override
- public IntArray clone() throws CloneNotSupportedException {
- final IntArray clone = (IntArray) super.clone();
- clone.mValues = mValues.clone();
- return clone;
+ public IntArray clone() {
+ return new IntArray(mValues.clone(), mSize);
}
/**
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index b8e50fc..adb8b86 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1495,6 +1495,15 @@
*/
public static final int TOOL_TYPE_ERASER = 4;
+ /**
+ * Tool type constant: The tool is a palm and should be rejected.
+ *
+ * @see #getToolType
+ *
+ * @hide
+ */
+ public static final int TOOL_TYPE_PALM = 5;
+
// NOTE: If you add a new tool type here you must also add it to:
// native/include/android/input.h
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index af7d86c..3b52709 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -120,6 +120,8 @@
long relativeToObject, int zorder);
private static native void nativeSetPosition(long transactionObj, long nativeObject,
float x, float y);
+ private static native void nativeSetScale(long transactionObj, long nativeObject,
+ float x, float y);
private static native void nativeSetSize(long transactionObj, long nativeObject, int w, int h);
private static native void nativeSetTransparentRegionHint(long transactionObj,
long nativeObject, Region region);
@@ -202,9 +204,13 @@
private static native void nativeReparent(long transactionObj, long nativeObject,
long newParentNativeObject);
private static native void nativeSetBuffer(long transactionObj, long nativeObject,
- GraphicBuffer buffer);
+ HardwareBuffer buffer);
+ private static native void nativeSetBufferTransform(long transactionObj, long nativeObject,
+ int transform);
private static native void nativeSetColorSpace(long transactionObj, long nativeObject,
int colorSpace);
+ private static native void nativeSetDamageRegion(long transactionObj, long nativeObject,
+ Region region);
private static native void nativeOverrideHdrTypes(IBinder displayToken, int[] modes);
@@ -1333,7 +1339,6 @@
* Set the initial visibility for the SurfaceControl.
*
* @param hidden Whether the Surface is initially HIDDEN.
- * @hide
*/
@NonNull
public Builder setHidden(boolean hidden) {
@@ -2840,16 +2845,38 @@
}
/**
- * @hide
+ * Sets the SurfaceControl to the specified position relative to the parent
+ * SurfaceControl
+ *
+ * @param sc The SurfaceControl to change position
+ * @param x the X position
+ * @param y the Y position
+ * @return this transaction
*/
- @UnsupportedAppUsage
- public Transaction setPosition(SurfaceControl sc, float x, float y) {
+ @NonNull
+ public Transaction setPosition(@NonNull SurfaceControl sc, float x, float y) {
checkPreconditions(sc);
nativeSetPosition(mNativeObject, sc.mNativeObject, x, y);
return this;
}
/**
+ * Sets the SurfaceControl to the specified scale with (0, 0) as the center point
+ * of the scale.
+ *
+ * @param sc The SurfaceControl to change scale
+ * @param scaleX the X scale
+ * @param scaleY the Y scale
+ * @return this transaction
+ */
+ @NonNull
+ public Transaction setScale(@NonNull SurfaceControl sc, float scaleX, float scaleY) {
+ checkPreconditions(sc);
+ nativeSetScale(mNativeObject, sc.mNativeObject, scaleX, scaleY);
+ return this;
+ }
+
+ /**
* Set the default buffer size for the SurfaceControl, if there is a
* {@link Surface} associated with the control, then
* this will be the default size for buffers dequeued from it.
@@ -3056,7 +3083,9 @@
* @param sc SurfaceControl to set crop of.
* @param crop Bounds of the crop to apply.
* @hide
+ * @deprecated Use {@link #setCrop(SurfaceControl, Rect)} instead.
*/
+ @Deprecated
@UnsupportedAppUsage
public Transaction setWindowCrop(SurfaceControl sc, Rect crop) {
checkPreconditions(sc);
@@ -3071,6 +3100,28 @@
}
/**
+ * Bounds the surface and its children to the bounds specified. Size of the surface will be
+ * ignored and only the crop and buffer size will be used to determine the bounds of the
+ * surface. If no crop is specified and the surface has no buffer, the surface bounds is
+ * only constrained by the size of its parent bounds.
+ *
+ * @param sc SurfaceControl to set crop of.
+ * @param crop Bounds of the crop to apply.
+ * @return this This transaction for chaining
+ */
+ public @NonNull Transaction setCrop(@NonNull SurfaceControl sc, @Nullable Rect crop) {
+ checkPreconditions(sc);
+ if (crop != null) {
+ nativeSetWindowCrop(mNativeObject, sc.mNativeObject,
+ crop.left, crop.top, crop.right, crop.bottom);
+ } else {
+ nativeSetWindowCrop(mNativeObject, sc.mNativeObject, 0, 0, 0, 0);
+ }
+
+ return this;
+ }
+
+ /**
* Same as {@link Transaction#setWindowCrop(SurfaceControl, Rect)} but sets the crop rect
* top left at 0, 0.
*
@@ -3215,11 +3266,34 @@
}
/**
- * Sets the opacity of the surface. Setting the flag is equivalent to creating the
- * Surface with the {@link #OPAQUE} flag.
- * @hide
+ * Indicates whether the surface must be considered opaque, even if its pixel format is
+ * set to translucent. This can be useful if an application needs full RGBA 8888 support
+ * for instance but will still draw every pixel opaque.
+ * <p>
+ * This flag only determines whether opacity will be sampled from the alpha channel.
+ * Plane-alpha from calls to setAlpha() can still result in blended composition
+ * regardless of the opaque setting.
+ *
+ * Combined effects are (assuming a buffer format with an alpha channel):
+ * <ul>
+ * <li>OPAQUE + alpha(1.0) == opaque composition
+ * <li>OPAQUE + alpha(0.x) == blended composition
+ * <li>OPAQUE + alpha(0.0) == no composition
+ * <li>!OPAQUE + alpha(1.0) == blended composition
+ * <li>!OPAQUE + alpha(0.x) == blended composition
+ * <li>!OPAQUE + alpha(0.0) == no composition
+ * </ul>
+ * If the underlying buffer lacks an alpha channel, it is as if setOpaque(true)
+ * were set automatically.
+ *
+ * @see Builder#setOpaque(boolean)
+ *
+ * @param sc The SurfaceControl to update
+ * @param isOpaque true if the buffer's alpha should be ignored, false otherwise
+ * @return this
*/
- public Transaction setOpaque(SurfaceControl sc, boolean isOpaque) {
+ @NonNull
+ public Transaction setOpaque(@NonNull SurfaceControl sc, boolean isOpaque) {
checkPreconditions(sc);
if (isOpaque) {
nativeSetFlags(mNativeObject, sc.mNativeObject, SURFACE_OPAQUE, SURFACE_OPAQUE);
@@ -3498,14 +3572,57 @@
* created as type {@link #FX_SURFACE_BLAST}
*
* @hide
+ * @deprecated Use {@link #setBuffer(SurfaceControl, HardwareBuffer)} instead
*/
+ @Deprecated
public Transaction setBuffer(SurfaceControl sc, GraphicBuffer buffer) {
+ return setBuffer(sc, HardwareBuffer.createFromGraphicBuffer(buffer));
+ }
+
+ /**
+ * Updates the HardwareBuffer displayed for the SurfaceControl.
+ *
+ * Note that the buffer must be allocated with {@link HardwareBuffer#USAGE_COMPOSER_OVERLAY}
+ * as well as {@link HardwareBuffer#USAGE_GPU_SAMPLED_IMAGE} as the surface control might
+ * be composited using either an overlay or using the GPU.
+ *
+ * @param sc The SurfaceControl to update
+ * @param buffer The buffer to be displayed
+ * @return this
+ */
+ public @NonNull Transaction setBuffer(@NonNull SurfaceControl sc,
+ @Nullable HardwareBuffer buffer) {
checkPreconditions(sc);
nativeSetBuffer(mNativeObject, sc.mNativeObject, buffer);
return this;
}
/**
+ * Sets the buffer transform that should be applied to the current buffer.
+ *
+ * @param sc The SurfaceControl to update
+ * @param transform The transform to apply to the buffer.
+ * @return this
+ */
+ public @NonNull Transaction setBufferTransform(@NonNull SurfaceControl sc,
+ /* TODO: Mark the intdef */ int transform) {
+ checkPreconditions(sc);
+ nativeSetBufferTransform(mNativeObject, sc.mNativeObject, transform);
+ return this;
+ }
+
+ /**
+ * Updates the region for the content on this surface updated in this transaction.
+ *
+ * If unspecified, the complete surface is assumed to be damaged.
+ */
+ public @NonNull Transaction setDamageRegion(@NonNull SurfaceControl sc,
+ @Nullable Region region) {
+ nativeSetDamageRegion(mNativeObject, sc.mNativeObject, region);
+ return this;
+ }
+
+ /**
* Set the color space for the SurfaceControl. The supported color spaces are SRGB
* and Display P3, other color spaces will be treated as SRGB. This can only be used for
* SurfaceControls that were created as type {@link #FX_SURFACE_BLAST}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c76245b..70505fc 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -533,10 +533,11 @@
boolean mReportNextDraw;
/**
- * Set if the reportDraw was requested from WM. If just a local report draw was invoked, there's
- * no need to report back to system server and can just apply immediately on the client.
+ * Set whether the draw should use blast sync. This is in case the draw is canceled,
+ * but will be rescheduled. We still want the next draw to be sync.
*/
- boolean mReportDrawToWm;
+ boolean mNextDrawUseBlastSync;
+
boolean mFullRedrawNeeded;
boolean mNewSurfaceNeeded;
boolean mForceNextWindowRelayout;
@@ -2761,7 +2762,7 @@
}
}
final boolean wasReportNextDraw = mReportNextDraw;
- boolean useBlastSync = false;
+ boolean useBlastSync = mNextDrawUseBlastSync;
if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
|| mForceNextWindowRelayout) {
@@ -3292,9 +3293,11 @@
mPendingTransitions.clear();
}
performDraw(useBlastSync);
+ mNextDrawUseBlastSync = false;
} else {
if (isViewVisible) {
// Try again
+ mNextDrawUseBlastSync = useBlastSync;
scheduleTraversals();
} else {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
@@ -3984,17 +3987,8 @@
}
mDrawsNeededToReport = 0;
- if (!mReportDrawToWm) {
- if (DEBUG_BLAST) {
- Log.d(mTag, "No need to report finishDrawing. Apply immediately");
- }
- mSurfaceChangedTransaction.apply();
- return;
- }
-
try {
mWindowSession.finishDrawing(mWindow, mSurfaceChangedTransaction);
- mReportDrawToWm = false;
} catch (RemoteException e) {
Log.e(mTag, "Unable to report draw finished", e);
mSurfaceChangedTransaction.apply();
@@ -4916,13 +4910,14 @@
}
}
- private void updateColorModeIfNeeded(int colorMode) {
+ private void updateColorModeIfNeeded(@ActivityInfo.ColorMode int colorMode) {
if (mAttachInfo.mThreadedRenderer == null) {
return;
}
// TODO: Centralize this sanitization? Why do we let setting bad modes?
// Alternatively, can we just let HWUI figure it out? Do we need to care here?
- if (!getConfiguration().isScreenWideColorGamut()) {
+ if (colorMode != ActivityInfo.COLOR_MODE_A8
+ && !getConfiguration().isScreenWideColorGamut()) {
colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
}
mAttachInfo.mThreadedRenderer.setColorMode(colorMode);
@@ -9604,7 +9599,6 @@
if (mReportNextDraw == false) {
drawPending();
}
- mReportDrawToWm = true;
mReportNextDraw = true;
}
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 3b15db2..b85fe7c 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -836,20 +836,25 @@
boolean beginBatchEdit();
/**
- * Tell the editor that you are done with a batch edit previously
- * initiated with {@link #beginBatchEdit}. This ends the latest
- * batch only.
+ * Tell the editor that you are done with a batch edit previously initiated with
+ * {@link #beginBatchEdit()}. This ends the latest batch only.
*
- * <p><strong>IME authors:</strong> make sure you call this
- * exactly once for each call to {@link #beginBatchEdit}.</p>
+ * <p><strong>IME authors:</strong> make sure you call this exactly once for each call to
+ * {@link #beginBatchEdit()}.</p>
*
- * <p><strong>Editor authors:</strong> please be careful about
- * batch edit nesting. Updates still to be held back until the end
- * of the last batch edit.</p>
+ * <p><strong>Editor authors:</strong> please be careful about batch edit nesting. Updates still
+ * to be held back until the end of the last batch edit. In case you are delegating this API
+ * call to the one obtained from
+ * {@link android.widget.EditText#onCreateInputConnection(EditorInfo)}, there was an off-by-one
+ * that had returned {@code true} when its nested batch edit count becomes {@code 0} as a result
+ * of invoking this API. This bug is fixed in {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+ * </p>
*
- * @return true if there is still a batch edit in progress after closing
- * the latest one (in other words, if the nesting count is > 0), false
- * otherwise or if the input connection is no longer valid.
+ * @return For editor authors, you must return {@code true} if a batch edit is still in progress
+ * after closing the latest one (in other words, if the nesting count is still a
+ * positive number). Return {@code false} otherwise. For IME authors, you will
+ * always receive {@code true} as long as the request was sent to the editor, and
+ * receive {@code false} only if the input connection is no longer valid.
*/
boolean endBatchEdit();
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 357dbc0..2702c2d 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -437,7 +437,10 @@
if (view.getViewTranslationResponse() != null
&& view.getViewTranslationResponse().equals(response)) {
if (callback instanceof TextViewTranslationCallback) {
- if (((TextViewTranslationCallback) callback).isShowingTranslation()) {
+ TextViewTranslationCallback textViewCallback =
+ (TextViewTranslationCallback) callback;
+ if (textViewCallback.isShowingTranslation()
+ || textViewCallback.isAnimationRunning()) {
if (DEBUG) {
Log.d(TAG, "Duplicate ViewTranslationResponse for " + autofillId
+ ". Ignoring.");
@@ -623,7 +626,7 @@
private void addViewIfNeeded(IntArray sourceViewIds, View view) {
final AutofillId autofillId = view.getAutofillId();
- if ((sourceViewIds.indexOf(autofillId.getViewId()) >= 0)
+ if (autofillId != null && (sourceViewIds.indexOf(autofillId.getViewId()) >= 0)
&& !mViews.containsKey(autofillId)) {
mViews.put(autofillId, new WeakReference<>(view));
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 3d4d9ec..dfd853a 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -3696,18 +3696,21 @@
}
private void initializeFrom(@NonNull RemoteViews src, @Nullable RemoteViews hierarchyRoot) {
+ if (hierarchyRoot == null) {
+ mBitmapCache = src.mBitmapCache;
+ mApplicationInfoCache = src.mApplicationInfoCache;
+ } else {
+ mBitmapCache = hierarchyRoot.mBitmapCache;
+ mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache;
+ }
if (hierarchyRoot == null || src.mIsRoot) {
// If there's no provided root, or if src was itself a root, then this RemoteViews is
// the root of the new hierarchy.
mIsRoot = true;
- mBitmapCache = new BitmapCache();
- mApplicationInfoCache = new ApplicationInfoCache();
hierarchyRoot = this;
} else {
// Otherwise, we're a descendant in the hierarchy.
mIsRoot = false;
- mBitmapCache = hierarchyRoot.mBitmapCache;
- mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache;
}
mApplication = src.mApplication;
mLayoutId = src.mLayoutId;
diff --git a/core/java/android/widget/TextViewTranslationCallback.java b/core/java/android/widget/TextViewTranslationCallback.java
index 4a78f3e..942be21 100644
--- a/core/java/android/widget/TextViewTranslationCallback.java
+++ b/core/java/android/widget/TextViewTranslationCallback.java
@@ -46,6 +46,7 @@
private TranslationTransformationMethod mTranslationTransformation;
private boolean mIsShowingTranslation = false;
+ private boolean mAnimationRunning = false;
private boolean mIsTextPaddingEnabled = false;
private CharSequence mPaddedText;
private int mAnimationDurationMillis = 250; // default value
@@ -92,6 +93,7 @@
(TextView) view,
() -> {
mIsShowingTranslation = true;
+ mAnimationRunning = false;
// TODO(b/178353965): well-handle setTransformationMethod.
((TextView) view).setTransformationMethod(transformation);
});
@@ -124,6 +126,7 @@
(TextView) view,
() -> {
mIsShowingTranslation = false;
+ mAnimationRunning = false;
((TextView) view).setTransformationMethod(transformation);
});
if (!TextUtils.isEmpty(mContentDescription)) {
@@ -162,6 +165,13 @@
return mIsShowingTranslation;
}
+ /**
+ * Returns whether the view is running animation to show or hide the translation.
+ */
+ public boolean isAnimationRunning() {
+ return mAnimationRunning;
+ }
+
@Override
public void enableContentPadding() {
mIsTextPaddingEnabled = true;
@@ -230,6 +240,7 @@
mAnimator.end();
// Note: mAnimator is now null; do not use again here.
}
+ mAnimationRunning = true;
int fadedOutColor = colorWithAlpha(view.getCurrentTextColor(), 0);
mAnimator = ValueAnimator.ofArgb(view.getCurrentTextColor(), fadedOutColor);
mAnimator.addUpdateListener(
diff --git a/core/java/android/window/PictureInPictureSurfaceTransaction.java b/core/java/android/window/PictureInPictureSurfaceTransaction.java
index dbf7eb3..2bf2f319 100644
--- a/core/java/android/window/PictureInPictureSurfaceTransaction.java
+++ b/core/java/android/window/PictureInPictureSurfaceTransaction.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Matrix;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,9 +35,10 @@
* @hide
*/
public final class PictureInPictureSurfaceTransaction implements Parcelable {
+ private static final float NOT_SET = -1f;
- public final float mPositionX;
- public final float mPositionY;
+ public final float mAlpha;
+ public final PointF mPosition;
public final float[] mFloat9;
@@ -45,33 +47,37 @@
public final float mCornerRadius;
- private final Rect mWindowCrop = new Rect();
+ private final Rect mWindowCrop;
- public PictureInPictureSurfaceTransaction(Parcel in) {
- mPositionX = in.readFloat();
- mPositionY = in.readFloat();
+ private PictureInPictureSurfaceTransaction(Parcel in) {
+ mAlpha = in.readFloat();
+ mPosition = in.readTypedObject(PointF.CREATOR);
mFloat9 = new float[9];
in.readFloatArray(mFloat9);
mRotation = in.readFloat();
mCornerRadius = in.readFloat();
- mWindowCrop.set(Objects.requireNonNull(in.readTypedObject(Rect.CREATOR)));
+ mWindowCrop = in.readTypedObject(Rect.CREATOR);
}
- public PictureInPictureSurfaceTransaction(float positionX, float positionY,
- float[] float9, float rotation, float cornerRadius,
+ private PictureInPictureSurfaceTransaction(float alpha, @Nullable PointF position,
+ @Nullable float[] float9, float rotation, float cornerRadius,
@Nullable Rect windowCrop) {
- mPositionX = positionX;
- mPositionY = positionY;
- mFloat9 = Arrays.copyOf(float9, 9);
- mRotation = rotation;
- mCornerRadius = cornerRadius;
- if (windowCrop != null) {
- mWindowCrop.set(windowCrop);
+ mAlpha = alpha;
+ mPosition = position;
+ if (float9 == null) {
+ mFloat9 = new float[9];
+ Matrix.IDENTITY_MATRIX.getValues(mFloat9);
+ mRotation = 0;
+ } else {
+ mFloat9 = Arrays.copyOf(float9, 9);
+ mRotation = rotation;
}
+ mCornerRadius = cornerRadius;
+ mWindowCrop = (windowCrop == null) ? null : new Rect(windowCrop);
}
public PictureInPictureSurfaceTransaction(PictureInPictureSurfaceTransaction other) {
- this(other.mPositionX, other.mPositionY,
+ this(other.mAlpha, other.mPosition,
other.mFloat9, other.mRotation, other.mCornerRadius, other.mWindowCrop);
}
@@ -82,13 +88,18 @@
return matrix;
}
+ /** @return {@code true} if this transaction contains setting corner radius. */
+ public boolean hasCornerRadiusSet() {
+ return mCornerRadius > 0;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PictureInPictureSurfaceTransaction)) return false;
PictureInPictureSurfaceTransaction that = (PictureInPictureSurfaceTransaction) o;
- return Objects.equals(mPositionX, that.mPositionX)
- && Objects.equals(mPositionY, that.mPositionY)
+ return Objects.equals(mAlpha, that.mAlpha)
+ && Objects.equals(mPosition, that.mPosition)
&& Arrays.equals(mFloat9, that.mFloat9)
&& Objects.equals(mRotation, that.mRotation)
&& Objects.equals(mCornerRadius, that.mCornerRadius)
@@ -97,7 +108,7 @@
@Override
public int hashCode() {
- return Objects.hash(mPositionX, mPositionY, Arrays.hashCode(mFloat9),
+ return Objects.hash(mAlpha, mPosition, Arrays.hashCode(mFloat9),
mRotation, mCornerRadius, mWindowCrop);
}
@@ -108,8 +119,8 @@
@Override
public void writeToParcel(Parcel out, int flags) {
- out.writeFloat(mPositionX);
- out.writeFloat(mPositionY);
+ out.writeFloat(mAlpha);
+ out.writeTypedObject(mPosition, 0 /* flags */);
out.writeFloatArray(mFloat9);
out.writeFloat(mRotation);
out.writeFloat(mCornerRadius);
@@ -120,8 +131,8 @@
public String toString() {
final Matrix matrix = getMatrix();
return "PictureInPictureSurfaceTransaction("
- + " posX=" + mPositionX
- + " posY=" + mPositionY
+ + " alpha=" + mAlpha
+ + " position=" + mPosition
+ " matrix=" + matrix.toShortString()
+ " rotation=" + mRotation
+ " cornerRadius=" + mCornerRadius
@@ -134,11 +145,20 @@
@NonNull SurfaceControl surfaceControl,
@NonNull SurfaceControl.Transaction tx) {
final Matrix matrix = surfaceTransaction.getMatrix();
- tx.setMatrix(surfaceControl, matrix, new float[9])
- .setPosition(surfaceControl,
- surfaceTransaction.mPositionX, surfaceTransaction.mPositionY)
- .setWindowCrop(surfaceControl, surfaceTransaction.mWindowCrop)
- .setCornerRadius(surfaceControl, surfaceTransaction.mCornerRadius);
+ tx.setMatrix(surfaceControl, matrix, new float[9]);
+ if (surfaceTransaction.mPosition != null) {
+ tx.setPosition(surfaceControl,
+ surfaceTransaction.mPosition.x, surfaceTransaction.mPosition.y);
+ }
+ if (surfaceTransaction.mWindowCrop != null) {
+ tx.setWindowCrop(surfaceControl, surfaceTransaction.mWindowCrop);
+ }
+ if (surfaceTransaction.hasCornerRadiusSet()) {
+ tx.setCornerRadius(surfaceControl, surfaceTransaction.mCornerRadius);
+ }
+ if (surfaceTransaction.mAlpha != NOT_SET) {
+ tx.setAlpha(surfaceControl, surfaceTransaction.mAlpha);
+ }
}
public static final @android.annotation.NonNull Creator<PictureInPictureSurfaceTransaction>
@@ -151,4 +171,44 @@
return new PictureInPictureSurfaceTransaction[size];
}
};
+
+ public static class Builder {
+ private float mAlpha = NOT_SET;
+ private PointF mPosition;
+ private float[] mFloat9;
+ private float mRotation;
+ private float mCornerRadius = NOT_SET;
+ private Rect mWindowCrop;
+
+ public Builder setAlpha(float alpha) {
+ mAlpha = alpha;
+ return this;
+ }
+
+ public Builder setPosition(float x, float y) {
+ mPosition = new PointF(x, y);
+ return this;
+ }
+
+ public Builder setTransform(@NonNull float[] float9, float rotation) {
+ mFloat9 = Arrays.copyOf(float9, 9);
+ mRotation = rotation;
+ return this;
+ }
+
+ public Builder setCornerRadius(float cornerRadius) {
+ mCornerRadius = cornerRadius;
+ return this;
+ }
+
+ public Builder setWindowCrop(@NonNull Rect windowCrop) {
+ mWindowCrop = new Rect(windowCrop);
+ return this;
+ }
+
+ public PictureInPictureSurfaceTransaction build() {
+ return new PictureInPictureSurfaceTransaction(mAlpha, mPosition,
+ mFloat9, mRotation, mCornerRadius, mWindowCrop);
+ }
+ }
}
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 5e75797..4745220e 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -23,6 +23,7 @@
import android.app.WindowConfiguration;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ShortcutInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
@@ -435,6 +436,21 @@
}
/**
+ * Starts activity(s) from a shortcut.
+ * @param callingPackage The package launching the shortcut.
+ * @param shortcutInfo Information about the shortcut to start
+ * @param options bundle containing ActivityOptions for the task's top activity.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction startShortcut(@NonNull String callingPackage,
+ @NonNull ShortcutInfo shortcutInfo, @Nullable Bundle options) {
+ mHierarchyOps.add(HierarchyOp.createForStartShortcut(
+ callingPackage, shortcutInfo, options));
+ return this;
+ }
+
+ /**
* Creates a new TaskFragment with the given options.
* @param taskFragmentOptions the options used to create the TaskFragment.
*/
@@ -957,11 +973,16 @@
public static final int HIERARCHY_OP_TYPE_REPARENT_CHILDREN = 11;
public static final int HIERARCHY_OP_TYPE_PENDING_INTENT = 12;
public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS = 13;
+ public static final int HIERARCHY_OP_TYPE_START_SHORTCUT = 14;
// The following key(s) are for use with mLaunchOptions:
// When launching a task (eg. from recents), this is the taskId to be launched.
public static final String LAUNCH_KEY_TASK_ID = "android:transaction.hop.taskId";
+ // When starting from a shortcut, this contains the calling package.
+ public static final String LAUNCH_KEY_SHORTCUT_CALLING_PACKAGE =
+ "android:transaction.hop.shortcut_calling_package";
+
private final int mType;
// Container we are performing the operation on.
@@ -999,6 +1020,9 @@
@Nullable
private PendingIntent mPendingIntent;
+ @Nullable
+ private ShortcutInfo mShortcutInfo;
+
public static HierarchyOp createForReparent(
@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT)
@@ -1058,6 +1082,17 @@
.build();
}
+ /** Create a hierarchy op for starting a shortcut. */
+ public static HierarchyOp createForStartShortcut(@NonNull String callingPackage,
+ @NonNull ShortcutInfo shortcutInfo, @Nullable Bundle options) {
+ final Bundle fullOptions = options == null ? new Bundle() : options;
+ fullOptions.putString(LAUNCH_KEY_SHORTCUT_CALLING_PACKAGE, callingPackage);
+ return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_START_SHORTCUT)
+ .setShortcutInfo(shortcutInfo)
+ .setLaunchOptions(fullOptions)
+ .build();
+ }
+
/** Create a hierarchy op for setting launch adjacent flag root. */
public static HierarchyOp createForSetLaunchAdjacentFlagRoot(IBinder container,
boolean clearRoot) {
@@ -1085,6 +1120,7 @@
mActivityIntent = copy.mActivityIntent;
mTaskFragmentCreationOptions = copy.mTaskFragmentCreationOptions;
mPendingIntent = copy.mPendingIntent;
+ mShortcutInfo = copy.mShortcutInfo;
}
protected HierarchyOp(Parcel in) {
@@ -1100,6 +1136,7 @@
mActivityIntent = in.readTypedObject(Intent.CREATOR);
mTaskFragmentCreationOptions = in.readTypedObject(TaskFragmentCreationParams.CREATOR);
mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
+ mShortcutInfo = in.readTypedObject(ShortcutInfo.CREATOR);
}
public int getType() {
@@ -1170,6 +1207,11 @@
return mPendingIntent;
}
+ @Nullable
+ public ShortcutInfo getShortcutInfo() {
+ return mShortcutInfo;
+ }
+
@Override
public String toString() {
switch (mType) {
@@ -1212,6 +1254,9 @@
case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS:
return "{SetAdjacentTaskFragments: container=" + mContainer
+ " adjacentContainer=" + mReparent + "}";
+ case HIERARCHY_OP_TYPE_START_SHORTCUT:
+ return "{StartShortcut: options=" + mLaunchOptions + " info=" + mShortcutInfo
+ + "}";
default:
return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
+ " mToTop=" + mToTop
@@ -1234,6 +1279,7 @@
dest.writeTypedObject(mActivityIntent, flags);
dest.writeTypedObject(mTaskFragmentCreationOptions, flags);
dest.writeTypedObject(mPendingIntent, flags);
+ dest.writeTypedObject(mShortcutInfo, flags);
}
@Override
@@ -1287,6 +1333,9 @@
@Nullable
private PendingIntent mPendingIntent;
+ @Nullable
+ private ShortcutInfo mShortcutInfo;
+
Builder(int type) {
mType = type;
}
@@ -1347,6 +1396,11 @@
return this;
}
+ Builder setShortcutInfo(@Nullable ShortcutInfo shortcutInfo) {
+ mShortcutInfo = shortcutInfo;
+ return this;
+ }
+
HierarchyOp build() {
final HierarchyOp hierarchyOp = new HierarchyOp(mType);
hierarchyOp.mContainer = mContainer;
@@ -1364,6 +1418,7 @@
hierarchyOp.mActivityIntent = mActivityIntent;
hierarchyOp.mPendingIntent = mPendingIntent;
hierarchyOp.mTaskFragmentCreationOptions = mTaskFragmentCreationOptions;
+ hierarchyOp.mShortcutInfo = mShortcutInfo;
return hierarchyOp;
}
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index b331a9e..4ba7ef2 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -15,7 +15,6 @@
*/
package android.window;
-import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets;
import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
import static android.window.ConfigurationHelper.isDifferentDisplay;
import static android.window.ConfigurationHelper.shouldUpdateResources;
@@ -222,14 +221,7 @@
() -> windowContext.dispatchConfigurationChanged(newConfig));
}
- // Dispatch onConfigurationChanged only if there's a significant public change to
- // make it compatible with the original behavior.
- final Configuration[] sizeConfigurations = context.getResources()
- .getSizeConfigurations();
- final SizeConfigurationBuckets buckets = sizeConfigurations != null
- ? new SizeConfigurationBuckets(sizeConfigurations) : null;
- final int diff = diffPublicWithSizeBuckets(mConfiguration, newConfig, buckets);
-
+ final int diff = mConfiguration.diffPublicOnly(newConfig);
if (shouldReportConfigChange && diff != 0
&& context instanceof WindowProviderService) {
final WindowProviderService windowProviderService = (WindowProviderService) context;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 52122ee..bc3c2f5 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -171,9 +171,6 @@
private AppPredictor mWorkAppPredictor;
private boolean mShouldDisplayLandscape;
- private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4;
- private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8;
-
@UnsupportedAppUsage
public ChooserActivity() {
}
@@ -294,6 +291,7 @@
private int mCurrAvailableWidth = 0;
private int mLastNumberOfChildren = -1;
+ private int mMaxTargetsPerRow = 1;
private static final String TARGET_DETAILS_FRAGMENT_TAG = "targetDetailsFragment";
@@ -681,8 +679,9 @@
mCallerChooserTargets = targets;
}
- mShouldDisplayLandscape = shouldDisplayLandscape(
- getResources().getConfiguration().orientation);
+ mMaxTargetsPerRow = getResources().getInteger(R.integer.config_chooser_max_targets_per_row);
+ mShouldDisplayLandscape =
+ shouldDisplayLandscape(getResources().getConfiguration().orientation);
setRetainInOnStop(intent.getBooleanExtra(EXTRA_PRIVATE_RETAIN_IN_ON_STOP, false));
super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents,
null, false);
@@ -915,7 +914,7 @@
adapter,
getPersonalProfileUserHandle(),
/* workProfileUserHandle= */ null,
- isSendAction(getTargetIntent()), getMaxTargetsPerRow());
+ isSendAction(getTargetIntent()), mMaxTargetsPerRow);
}
private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForTwoProfiles(
@@ -944,7 +943,7 @@
selectedProfile,
getPersonalProfileUserHandle(),
getWorkProfileUserHandle(),
- isSendAction(getTargetIntent()), getMaxTargetsPerRow());
+ isSendAction(getTargetIntent()), mMaxTargetsPerRow);
}
private int findSelectedProfile() {
@@ -1112,6 +1111,7 @@
}
mShouldDisplayLandscape = shouldDisplayLandscape(newConfig.orientation);
+ mMaxTargetsPerRow = getResources().getInteger(R.integer.config_chooser_max_targets_per_row);
adjustPreviewWidth(newConfig.orientation, null);
updateStickyContentPreview();
}
@@ -2560,7 +2560,7 @@
// and b/150936654
recyclerView.setAdapter(gridAdapter);
((GridLayoutManager) recyclerView.getLayoutManager()).setSpanCount(
- getMaxTargetsPerRow());
+ mMaxTargetsPerRow);
}
UserHandle currentUserHandle = mChooserMultiProfilePagerAdapter.getCurrentUserHandle();
@@ -2724,7 +2724,7 @@
@Override // ChooserListCommunicator
public int getMaxRankedTargets() {
- return getMaxTargetsPerRow();
+ return mMaxTargetsPerRow;
}
@Override // ChooserListCommunicator
@@ -3075,13 +3075,6 @@
}
}
- int getMaxTargetsPerRow() {
- int maxTargets = MAX_TARGETS_PER_ROW_PORTRAIT;
- if (mShouldDisplayLandscape) {
- maxTargets = MAX_TARGETS_PER_ROW_LANDSCAPE;
- }
- return maxTargets;
- }
/**
* Adapter for all types of items and targets in ShareSheet.
* Note that ranked sections like Direct Share - while appearing grid-like - are handled on the
@@ -3149,7 +3142,11 @@
return false;
}
- int newWidth = width / getMaxTargetsPerRow();
+ // Limit width to the maximum width of the chooser activity
+ int maxWidth = getResources().getDimensionPixelSize(R.dimen.chooser_width);
+ width = Math.min(maxWidth, width);
+
+ int newWidth = width / mMaxTargetsPerRow;
if (newWidth != mChooserTargetWidth) {
mChooserTargetWidth = newWidth;
return true;
@@ -3184,7 +3181,7 @@
+ getAzLabelRowCount()
+ Math.ceil(
(float) mChooserListAdapter.getAlphaTargetCount()
- / getMaxTargetsPerRow())
+ / mMaxTargetsPerRow)
);
}
@@ -3224,7 +3221,7 @@
public int getCallerAndRankedTargetRowCount() {
return (int) Math.ceil(
((float) mChooserListAdapter.getCallerTargetCount()
- + mChooserListAdapter.getRankedTargetCount()) / getMaxTargetsPerRow());
+ + mChooserListAdapter.getRankedTargetCount()) / mMaxTargetsPerRow);
}
// There can be at most one row in the listview, that is internally
@@ -3423,7 +3420,7 @@
parentGroup.addView(row2);
mDirectShareViewHolder = new DirectShareViewHolder(parentGroup,
- Lists.newArrayList(row1, row2), getMaxTargetsPerRow(), viewType,
+ Lists.newArrayList(row1, row2), mMaxTargetsPerRow, viewType,
mChooserMultiProfilePagerAdapter::getActiveListAdapter);
loadViewsIntoGroup(mDirectShareViewHolder);
@@ -3432,7 +3429,7 @@
ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row, parent,
false);
ItemGroupViewHolder holder =
- new SingleRowViewHolder(row, getMaxTargetsPerRow(), viewType);
+ new SingleRowViewHolder(row, mMaxTargetsPerRow, viewType);
loadViewsIntoGroup(holder);
return holder;
@@ -3524,7 +3521,7 @@
final int serviceCount = mChooserListAdapter.getServiceTargetCount();
final int serviceRows = (int) Math.ceil((float) serviceCount / getMaxRankedTargets());
if (position < serviceRows) {
- return position * getMaxTargetsPerRow();
+ return position * mMaxTargetsPerRow;
}
position -= serviceRows;
@@ -3533,7 +3530,7 @@
+ mChooserListAdapter.getRankedTargetCount();
final int callerAndRankedRows = getCallerAndRankedTargetRowCount();
if (position < callerAndRankedRows) {
- return serviceCount + position * getMaxTargetsPerRow();
+ return serviceCount + position * mMaxTargetsPerRow;
}
position -= getAzLabelRowCount() + callerAndRankedRows;
@@ -3546,7 +3543,7 @@
if (mDirectShareViewHolder != null && canExpandDirectShare) {
mDirectShareViewHolder.handleScroll(
mChooserMultiProfilePagerAdapter.getActiveAdapterView(), y, oldy,
- getMaxTargetsPerRow());
+ mMaxTargetsPerRow);
}
}
diff --git a/core/java/com/android/internal/inputmethod/EditableInputConnection.java b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
index 410e68b..29bb311 100644
--- a/core/java/com/android/internal/inputmethod/EditableInputConnection.java
+++ b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
@@ -89,7 +89,7 @@
// contribution to mTextView's nested batch edit count is zero.
mTextView.endBatchEdit();
mBatchEditNesting--;
- return true;
+ return mBatchEditNesting > 0;
}
}
return false;
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index a0a0f32..6ba0279 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -43,7 +43,8 @@
void removeOnSubscriptionsChangedListener(String pkg,
IOnSubscriptionsChangedListener callback);
- void listenWithEventList(in int subId, String pkg, String featureId,
+ void listenWithEventList(in boolean renounceFineLocationAccess,
+ in boolean renounceCoarseLocationAccess, in int subId, String pkg, String featureId,
IPhoneStateListener callback, in int[] events, boolean notifyNow);
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
void notifyCallStateForAllSubs(int state, String incomingNumber);
diff --git a/core/java/com/android/internal/util/AnnotationValidations.java b/core/java/com/android/internal/util/AnnotationValidations.java
index cf5e48f..e1b3802 100644
--- a/core/java/com/android/internal/util/AnnotationValidations.java
+++ b/core/java/com/android/internal/util/AnnotationValidations.java
@@ -26,7 +26,7 @@
import android.annotation.UserIdInt;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.PackageInfoFlags;
+import android.content.pm.PackageManager.PackageInfoFlagsBits;
import android.content.pm.PackageManager.PermissionResult;
import android.os.UserHandle;
@@ -160,8 +160,8 @@
}
public static void validate(
- Class<PackageInfoFlags> annotation, PackageInfoFlags ignored, int value) {
- validateIntFlags(annotation, value,
+ Class<PackageInfoFlagsBits> annotation, PackageInfoFlagsBits ignored, long value) {
+ validateLongFlags(annotation, value,
flagsUpTo(PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS));
}
@@ -212,7 +212,12 @@
invalid(annotation, "0x" + Integer.toHexString(value));
}
}
-
+ private static void validateLongFlags(
+ Class<? extends Annotation> annotation, long value, int validBits) {
+ if ((validBits & value) != validBits) {
+ invalid(annotation, "0x" + Long.toHexString(value));
+ }
+ }
private static void invalid(Class<? extends Annotation> annotation, Object value) {
invalid("@" + annotation.getSimpleName(), value);
}
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index e6deada..78bb53d 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -871,6 +871,10 @@
if (newGroup == null) {
newGroup = MessagingGroup.createGroup(mMessagingLinearLayout);
mAddedGroups.add(newGroup);
+ } else if (newGroup.getParent() != mMessagingLinearLayout) {
+ throw new IllegalStateException(
+ "group parent was " + newGroup.getParent() + " but expected "
+ + mMessagingLinearLayout);
}
newGroup.setImageDisplayLocation(mIsCollapsed
? IMAGE_DISPLAY_LOCATION_EXTERNAL
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index f30b844..9e06e33 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -32,7 +32,6 @@
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
-import android.util.Pools;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
@@ -57,8 +56,8 @@
*/
@RemoteViews.RemoteView
public class MessagingGroup extends LinearLayout implements MessagingLinearLayout.MessagingChild {
- private static Pools.SimplePool<MessagingGroup> sInstancePool
- = new Pools.SynchronizedPool<>(10);
+ private static final MessagingPool<MessagingGroup> sInstancePool =
+ new MessagingPool<>(10);
/**
* Images are displayed inline.
@@ -338,7 +337,7 @@
}
public static void dropCache() {
- sInstancePool = new Pools.SynchronizedPool<>(10);
+ sInstancePool.clear();
}
@Override
diff --git a/core/java/com/android/internal/widget/MessagingImageMessage.java b/core/java/com/android/internal/widget/MessagingImageMessage.java
index 27689d4..f7955c3 100644
--- a/core/java/com/android/internal/widget/MessagingImageMessage.java
+++ b/core/java/com/android/internal/widget/MessagingImageMessage.java
@@ -28,7 +28,6 @@
import android.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
-import android.util.Pools;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.ImageView;
@@ -44,8 +43,8 @@
@RemoteViews.RemoteView
public class MessagingImageMessage extends ImageView implements MessagingMessage {
private static final String TAG = "MessagingImageMessage";
- private static Pools.SimplePool<MessagingImageMessage> sInstancePool
- = new Pools.SynchronizedPool<>(10);
+ private static final MessagingPool<MessagingImageMessage> sInstancePool =
+ new MessagingPool<>(10);
private final MessagingMessageState mState = new MessagingMessageState(this);
private final int mMinImageHeight;
private final Path mPath = new Path();
@@ -194,7 +193,7 @@
}
public static void dropCache() {
- sInstancePool = new Pools.SynchronizedPool<>(10);
+ sInstancePool.clear();
}
@Override
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index e1602a9..21ca196 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -419,6 +419,10 @@
if (newGroup == null) {
newGroup = MessagingGroup.createGroup(mMessagingLinearLayout);
mAddedGroups.add(newGroup);
+ } else if (newGroup.getParent() != mMessagingLinearLayout) {
+ throw new IllegalStateException(
+ "group parent was " + newGroup.getParent() + " but expected "
+ + mMessagingLinearLayout);
}
newGroup.setImageDisplayLocation(mIsCollapsed
? IMAGE_DISPLAY_LOCATION_EXTERNAL
diff --git a/core/java/com/android/internal/widget/MessagingPool.java b/core/java/com/android/internal/widget/MessagingPool.java
new file mode 100644
index 0000000..9c0fe4b
--- /dev/null
+++ b/core/java/com/android/internal/widget/MessagingPool.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+package com.android.internal.widget;
+
+import android.util.Log;
+import android.util.Pools;
+import android.view.View;
+
+/**
+ * A trivial wrapper around Pools.SynchronizedPool which allows clearing the pool, as well as
+ * disabling the pool class altogether.
+ * @param <T> the type of object in the pool
+ */
+public class MessagingPool<T extends View> implements Pools.Pool<T> {
+ private static final boolean ENABLED = false; // disabled to test b/208508846
+ private static final String TAG = "MessagingPool";
+ private final int mMaxPoolSize;
+ private Pools.SynchronizedPool<T> mCurrentPool;
+
+ public MessagingPool(int maxPoolSize) {
+ mMaxPoolSize = maxPoolSize;
+ if (ENABLED) {
+ mCurrentPool = new Pools.SynchronizedPool<>(mMaxPoolSize);
+ }
+ }
+
+ @Override
+ public T acquire() {
+ if (!ENABLED) {
+ return null;
+ }
+ T instance = mCurrentPool.acquire();
+ if (instance.getParent() != null) {
+ Log.wtf(TAG, "acquired " + instance + " with parent " + instance.getParent());
+ return null;
+ }
+ return instance;
+ }
+
+ @Override
+ public boolean release(T instance) {
+ if (instance.getParent() != null) {
+ Log.wtf(TAG, "releasing " + instance + " with parent " + instance.getParent());
+ return false;
+ }
+ if (!ENABLED) {
+ return false;
+ }
+ return mCurrentPool.release(instance);
+ }
+
+ /** Clear the pool */
+ public void clear() {
+ if (ENABLED) {
+ mCurrentPool = new Pools.SynchronizedPool<>(mMaxPoolSize);
+ }
+ }
+
+}
diff --git a/core/java/com/android/internal/widget/MessagingTextMessage.java b/core/java/com/android/internal/widget/MessagingTextMessage.java
index d778c59..19791db 100644
--- a/core/java/com/android/internal/widget/MessagingTextMessage.java
+++ b/core/java/com/android/internal/widget/MessagingTextMessage.java
@@ -24,7 +24,6 @@
import android.content.Context;
import android.text.Layout;
import android.util.AttributeSet;
-import android.util.Pools;
import android.view.LayoutInflater;
import android.widget.RemoteViews;
@@ -36,8 +35,8 @@
@RemoteViews.RemoteView
public class MessagingTextMessage extends ImageFloatingTextView implements MessagingMessage {
- private static Pools.SimplePool<MessagingTextMessage> sInstancePool
- = new Pools.SynchronizedPool<>(20);
+ private static final MessagingPool<MessagingTextMessage> sInstancePool =
+ new MessagingPool<>(20);
private final MessagingMessageState mState = new MessagingMessageState(this);
public MessagingTextMessage(@NonNull Context context) {
@@ -92,7 +91,7 @@
}
public static void dropCache() {
- sInstancePool = new Pools.SynchronizedPool<>(10);
+ sInstancePool.clear();
}
@Override
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 86d7810..8c23b21 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -881,6 +881,12 @@
return static_cast<jint>(bag->entry_count);
}
+static jint NativeGetParentThemeIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const auto parentThemeResId = assetmanager->GetParentThemeResourceId(resid);
+ return parentThemeResId.value_or(0);
+}
+
static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name,
jstring def_type, jstring def_package) {
ScopedUtfChars name_utf8(env, name);
@@ -1464,6 +1470,8 @@
{"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray},
{"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize},
{"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray},
+ {"nativeGetParentThemeIdentifier", "(JI)I",
+ (void*)NativeGetParentThemeIdentifier},
// AssetManager resource name/ID methods.
{"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index fb5fded..67d0c52 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -597,6 +597,14 @@
transaction->setPosition(ctrl, x, y);
}
+static void nativeSetScale(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
+ jfloat xScale, jfloat yScale) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ transaction->setMatrix(ctrl, xScale, 0, 0, yScale);
+}
+
static void nativeSetGeometry(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
jobject sourceObj, jobject dstObj, jlong orientation) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -620,9 +628,19 @@
jobject bufferObject) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
- sp<GraphicBuffer> buffer(
- android_graphics_GraphicBuffer_getNativeGraphicsBuffer(env, bufferObject));
- transaction->setBuffer(ctrl, buffer);
+ sp<GraphicBuffer> graphicBuffer(GraphicBuffer::fromAHardwareBuffer(
+ android_hardware_HardwareBuffer_getNativeHardwareBuffer(env, bufferObject)));
+ transaction->setBuffer(ctrl, graphicBuffer);
+}
+
+static void nativeSetBufferTransform(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jlong nativeObject, jint transform) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ transaction->setTransform(ctrl, transform);
+ bool transformToInverseDisplay = (NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY & transform) ==
+ NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
+ transaction->setTransformToDisplayInverse(ctrl, transformToInverseDisplay);
}
static void nativeSetColorSpace(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
@@ -740,6 +758,37 @@
}
}
+static void nativeSetDamageRegion(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jlong nativeObject, jobject regionObj) {
+ SurfaceControl* const surfaceControl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+ if (regionObj == nullptr) {
+ transaction->setSurfaceDamageRegion(surfaceControl, Region::INVALID_REGION);
+ return;
+ }
+
+ graphics::RegionIterator iterator(env, regionObj);
+ if (!iterator.isValid()) {
+ transaction->setSurfaceDamageRegion(surfaceControl, Region::INVALID_REGION);
+ return;
+ }
+
+ Region region;
+ while (!iterator.isDone()) {
+ ARect rect = iterator.getRect();
+ region.orSelf(static_cast<const Rect&>(rect));
+ iterator.next();
+ }
+
+ if (region.getBounds().isEmpty()) {
+ transaction->setSurfaceDamageRegion(surfaceControl, Region::INVALID_REGION);
+ return;
+ }
+
+ transaction->setSurfaceDamageRegion(surfaceControl, region);
+}
+
static void nativeSetAlpha(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jfloat alpha) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -1905,10 +1954,14 @@
(void*)nativeSetRelativeLayer },
{"nativeSetPosition", "(JJFF)V",
(void*)nativeSetPosition },
+ {"nativeSetScale", "(JJFF)V",
+ (void*)nativeSetScale },
{"nativeSetSize", "(JJII)V",
(void*)nativeSetSize },
{"nativeSetTransparentRegionHint", "(JJLandroid/graphics/Region;)V",
(void*)nativeSetTransparentRegionHint },
+ { "nativeSetDamageRegion", "(JJLandroid/graphics/Region;)V",
+ (void*)nativeSetDamageRegion },
{"nativeSetAlpha", "(JJF)V",
(void*)nativeSetAlpha },
{"nativeSetColor", "(JJ[F)V",
@@ -2018,8 +2071,9 @@
(void*)nativeGetDisplayedContentSample },
{"nativeSetGeometry", "(JJLandroid/graphics/Rect;Landroid/graphics/Rect;J)V",
(void*)nativeSetGeometry },
- {"nativeSetBuffer", "(JJLandroid/graphics/GraphicBuffer;)V",
+ {"nativeSetBuffer", "(JJLandroid/hardware/HardwareBuffer;)V",
(void*)nativeSetBuffer },
+ {"nativeSetBufferTransform", "(JJI)V", (void*) nativeSetBufferTransform},
{"nativeSetColorSpace", "(JJI)V",
(void*)nativeSetColorSpace },
{"nativeSyncInputWindows", "(J)V",
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 211f78e..23453876 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -380,6 +380,7 @@
optional bool pip_auto_enter_enabled = 31;
optional bool in_size_compat_mode = 32;
optional float min_aspect_ratio = 33;
+ optional bool provides_max_bounds = 34;
}
/* represents WindowToken */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6e2c807..0894877 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2066,7 +2066,7 @@
<permission android:name="android.permission.BLUETOOTH_PRIVILEGED"
android:protectionLevel="signature|privileged" />
- <!-- Control access to email providers exclusively for Bluetooth
+ <!-- @SystemApi Control access to email providers exclusively for Bluetooth
@hide
-->
<permission android:name="android.permission.BLUETOOTH_MAP"
@@ -2791,6 +2791,11 @@
<permission android:name="android.permission.CREATE_USERS"
android:protectionLevel="signature" />
+ <!-- @SystemApi @hide Allows an application to call APIs that allow it to query users on the
+ device. -->
+ <permission android:name="android.permission.QUERY_USERS"
+ android:protectionLevel="signature|role" />
+
<!-- Allows an application to access data blobs across users. -->
<permission android:name="android.permission.ACCESS_BLOBS_ACROSS_USERS"
android:protectionLevel="signature|privileged|development|role" />
@@ -2798,7 +2803,7 @@
<!-- @SystemApi @hide Allows an application to set the profile owners and the device owner.
This permission is not available to third party applications.-->
<permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
- android:protectionLevel="signature|role"
+ android:protectionLevel="signature|role|setup"
android:label="@string/permlab_manageProfileAndDeviceOwners"
android:description="@string/permdesc_manageProfileAndDeviceOwners" />
@@ -5531,10 +5536,18 @@
<!-- Allows an application to interact with the currently active
{@link com.android.server.communal.CommunalManagerService}.
- @hide -->
+ @hide
+ @TestApi -->
<permission android:name="android.permission.WRITE_COMMUNAL_STATE"
android:protectionLevel="signature" />
+ <!-- Allows an application to view information from the currently active
+ {@link com.android.server.communal.CommunalManagerService}.
+ @hide
+ @SystemApi -->
+ <permission android:name="android.permission.READ_COMMUNAL_STATE"
+ android:protectionLevel="signature|privileged"/>
+
<!-- Allows the holder to manage whether the system can bind to services
provided by instant apps. This permission is intended to protect
test/development fucntionality and should be used only in such cases.
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 90caacc..933b4d2 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -20,8 +20,10 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:layout_gravity="center"
android:maxCollapsedHeight="0dp"
android:maxCollapsedHeightSmall="56dp"
+ android:maxWidth="@dimen/chooser_width"
android:id="@id/contentPanel">
<RelativeLayout
diff --git a/core/res/res/layout/chooser_grid_preview_image.xml b/core/res/res/layout/chooser_grid_preview_image.xml
index 0d04d7f3..52692b0 100644
--- a/core/res/res/layout/chooser_grid_preview_image.xml
+++ b/core/res/res/layout/chooser_grid_preview_image.xml
@@ -34,7 +34,7 @@
<view class="com.android.internal.app.ChooserActivity$RoundedRectImageView"
android:id="@+id/content_preview_image_1_large"
android:layout_width="120dp"
- android:layout_height="140dp"
+ android:layout_height="104dp"
android:layout_alignParentTop="true"
android:adjustViewBounds="true"
android:gravity="center"
@@ -44,7 +44,7 @@
android:id="@+id/content_preview_image_2_large"
android:visibility="gone"
android:layout_width="120dp"
- android:layout_height="140dp"
+ android:layout_height="104dp"
android:layout_alignParentTop="true"
android:layout_toRightOf="@id/content_preview_image_1_large"
android:layout_marginLeft="10dp"
diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml
index 34b6a54..d686dd2 100644
--- a/core/res/res/values-sw600dp/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -51,5 +51,7 @@
<!-- If true, show multiuser switcher by default unless the user specifically disables it. -->
<bool name="config_showUserSwitcherByDefault">true</bool>
+
+ <integer name="config_chooser_max_targets_per_row">6</integer>
</resources>
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 02ed848..e8f15fd 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -110,4 +110,7 @@
<dimen name="immersive_mode_cling_width">380dp</dimen>
<dimen name="floating_toolbar_preferred_width">544dp</dimen>
+
+ <dimen name="chooser_width">624dp</dimen>
+
</resources>
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 606d0f2..92bea34 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -27,11 +27,11 @@
<!-- The percentage of the screen width to use for the default width or height of
picture-in-picture windows. Regardless of the percent set here, calculated size will never
be smaller than @dimen/default_minimal_size_pip_resizable_task. -->
- <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.14</item>
+ <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.2</item>
<!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows.
These values are in DPs and will be converted to pixel sizes internally. -->
- <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">56x27</string>
+ <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">24x24</string>
<!-- The default gravity for the picture-in-picture window.
Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 91e4074..0a6ef7c 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2808,6 +2808,22 @@
<attr name="attributionTags" />
</declare-styleable>
+ <!-- @hide The <code>apex-system-service</code> tag declares an apex system service
+ that is contained within an application.
+
+ The apex system service tag appears as a child tag of the
+ {@link #AndroidManifestApplication application} tag. -->
+ <declare-styleable name="AndroidManifestApexSystemService"
+ parent="AndroidManifestApplication">
+ <!-- The fully qualified class name of the system service. -->
+ <attr name="name" />
+ <!-- The filepath to the .jar that contains the system service. If this is not provided, it
+ is assumed that the system service exists in SYSTEMSERVERCLASSPATH. -->
+ <attr name="path" />
+ <attr name="minSdkVersion" />
+ <attr name="maxSdkVersion" />
+ </declare-styleable>
+
<!-- The <code>receiver</code> tag declares an
{@link android.content.BroadcastReceiver} class that is available
as part of the package's application components, allowing the
@@ -3051,6 +3067,7 @@
<declare-styleable name="AndroidManifestMetaData"
parent="AndroidManifestApplication
AndroidManifestActivity
+ AndroidManifestApexSystemService
AndroidManifestReceiver
AndroidManifestProvider
AndroidManifestService
@@ -3085,6 +3102,7 @@
<declare-styleable name="AndroidManifestProperty"
parent="AndroidManifestApplication
AndroidManifestActivity
+ AndroidManifestApexSystemService
AndroidManifestReceiver
AndroidManifestProvider
AndroidManifestService">
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ccde348..ddec469 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2123,6 +2123,8 @@
<string name="config_systemTelevisionRemoteService" translatable="false">@string/config_tvRemoteServicePackage</string>
<!-- The name of the package that will hold the device management role -->
<string name="config_deviceManager" translatable="false"></string>
+ <!-- The name of the package that will hold the app protection service role. -->
+ <string name="config_systemAppProtectionService" translatable="false"></string>
<!-- The name of the package that will be allowed to change its components' label/icon. -->
<string name="config_overrideComponentUiPackage" translatable="false">com.android.stk</string>
@@ -2381,7 +2383,7 @@
<!-- If supported and enabled, are dreams activated when asleep and charging? (by default) -->
<bool name="config_dreamsActivatedOnSleepByDefault">false</bool>
<!-- ComponentName of the default dream (Settings.Secure.DEFAULT_SCREENSAVER_COMPONENT) -->
- <string name="config_dreamsDefaultComponent" translatable="false">com.google.android.deskclock/com.android.deskclock.Screensaver</string>
+ <string name="config_dreamsDefaultComponent" translatable="false">com.android.deskclock/com.android.deskclock.Screensaver</string>
<!-- Are we allowed to dream while not plugged in? -->
<bool name="config_dreamsEnabledOnBattery">false</bool>
@@ -5514,4 +5516,6 @@
<!-- Add configurations here, example: -->
<!-- <item>SUPL_HOST=supl.google.com</item> -->
</string-array>
+
+ <integer name="config_chooser_max_targets_per_row">4</integer>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index c40ec4f..a877bd3 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -899,7 +899,8 @@
<dimen name="seekbar_thumb_exclusion_max_size">48dp</dimen>
<!-- chooser/resolver (sharesheet) spacing -->
- <dimen name="chooser_corner_radius">16dp</dimen>
+ <dimen name="chooser_width">412dp</dimen>
+ <dimen name="chooser_corner_radius">28dp</dimen>
<dimen name="chooser_row_text_option_translate">25dp</dimen>
<dimen name="chooser_view_spacing">18dp</dimen>
<dimen name="chooser_edge_margin_thin">16dp</dimen>
@@ -916,7 +917,7 @@
<dimen name="resolver_icon_size">32dp</dimen>
<dimen name="resolver_button_bar_spacing">0dp</dimen>
<dimen name="resolver_badge_size">18dp</dimen>
- <dimen name="resolver_icon_margin">16dp</dimen>
+ <dimen name="resolver_icon_margin">8dp</dimen>
<dimen name="resolver_small_margin">18dp</dimen>
<dimen name="resolver_edge_margin">24dp</dimen>
<dimen name="resolver_elevation">1dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index ca80def..1aa3ac2 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3267,6 +3267,8 @@
<public name="config_systemSupervision" />
<!-- @hide @SystemApi -->
<public name="config_deviceManager" />
+ <!-- @hide @SystemApi -->
+ <public name="config_systemAppProtectionService" />
</staging-public-group>
<staging-public-group type="dimen" first-id="0x01db0000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 134235d..55bf24b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -275,6 +275,7 @@
<java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" />
<java-symbol type="bool" name="config_avoidGfxAccel" />
<java-symbol type="bool" name="config_bluetooth_address_validation" />
+ <java-symbol type="integer" name="config_chooser_max_targets_per_row" />
<java-symbol type="bool" name="config_flipToScreenOffEnabled" />
<java-symbol type="integer" name="config_flipToScreenOffMaxLatencyMicros" />
<java-symbol type="bool" name="config_bluetooth_sco_off_call" />
@@ -2861,6 +2862,7 @@
<java-symbol type="layout" name="date_picker_month_item_material" />
<java-symbol type="id" name="month_view" />
<java-symbol type="integer" name="config_zen_repeat_callers_threshold" />
+ <java-symbol type="dimen" name="chooser_width" />
<java-symbol type="dimen" name="chooser_corner_radius" />
<java-symbol type="string" name="chooser_no_direct_share_targets" />
<java-symbol type="drawable" name="chooser_row_layer_list" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index bcd794e..32d72b37 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -23,6 +23,7 @@
],
aidl: {
+ generate_get_transaction_name: true,
local_include_dirs: ["aidl"],
},
@@ -46,6 +47,7 @@
"androidx.test.ext.junit",
"androidx.test.runner",
"androidx.test.rules",
+ "kotlin-test",
"mockito-target-minus-junit4",
"ub-uiautomator",
"platform-test-annotations",
diff --git a/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java b/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java
index 0456029..98485c0 100644
--- a/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java
+++ b/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java
@@ -18,8 +18,7 @@
import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
import android.annotation.Nullable;
import android.platform.test.annotations.Presubmit;
@@ -146,24 +145,17 @@
private static void testNeverConstrainDisplayApis(String packageName, long version,
boolean expected) {
- boolean result = ConstrainDisplayApisConfig.neverConstrainDisplayApis(
- buildApplicationInfo(packageName, version));
- if (expected) {
- assertTrue(result);
- } else {
- assertFalse(result);
- }
+ ConstrainDisplayApisConfig config = new ConstrainDisplayApisConfig();
+ assertEquals(expected,
+ config.getNeverConstrainDisplayApis(buildApplicationInfo(packageName, version)));
}
private static void testAlwaysConstrainDisplayApis(String packageName, long version,
boolean expected) {
- boolean result = ConstrainDisplayApisConfig.alwaysConstrainDisplayApis(
- buildApplicationInfo(packageName, version));
- if (expected) {
- assertTrue(result);
- } else {
- assertFalse(result);
- }
+ ConstrainDisplayApisConfig config = new ConstrainDisplayApisConfig();
+
+ assertEquals(expected,
+ config.getAlwaysConstrainDisplayApis(buildApplicationInfo(packageName, version)));
}
private static ApplicationInfo buildApplicationInfo(String packageName, long version) {
diff --git a/core/tests/coretests/src/android/hardware/input/VirtualKeyEventTest.java b/core/tests/coretests/src/android/hardware/input/VirtualKeyEventTest.java
new file mode 100644
index 0000000..37cc9b7
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/input/VirtualKeyEventTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+package android.hardware.input;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.testng.Assert.assertThrows;
+
+import android.view.KeyEvent;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class VirtualKeyEventTest {
+
+ @Test
+ public void keyEvent_emptyBuilder() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualKeyEvent.Builder().build());
+ }
+
+ @Test
+ public void keyEvent_noKeyCode() {
+ assertThrows(IllegalArgumentException.class,
+ () -> new VirtualKeyEvent.Builder().setAction(VirtualKeyEvent.ACTION_DOWN).build());
+ }
+
+ @Test
+ public void keyEvent_noAction() {
+ assertThrows(IllegalArgumentException.class,
+ () -> new VirtualKeyEvent.Builder().setKeyCode(KeyEvent.KEYCODE_A).build());
+ }
+
+ @Test
+ public void keyEvent_created() {
+ final VirtualKeyEvent event = new VirtualKeyEvent.Builder()
+ .setAction(VirtualKeyEvent.ACTION_DOWN)
+ .setKeyCode(KeyEvent.KEYCODE_A).build();
+ assertWithMessage("Incorrect key code").that(event.getKeyCode()).isEqualTo(
+ KeyEvent.KEYCODE_A);
+ assertWithMessage("Incorrect action").that(event.getAction()).isEqualTo(
+ VirtualKeyEvent.ACTION_DOWN);
+ }
+}
diff --git a/core/tests/coretests/src/android/hardware/input/VirtualMouseButtonEventTest.java b/core/tests/coretests/src/android/hardware/input/VirtualMouseButtonEventTest.java
new file mode 100644
index 0000000..789e0bb
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/input/VirtualMouseButtonEventTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+package android.hardware.input;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.testng.Assert.assertThrows;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class VirtualMouseButtonEventTest {
+
+ @Test
+ public void buttonEvent_emptyBuilder() {
+ assertThrows(IllegalArgumentException.class,
+ () -> new VirtualMouseButtonEvent.Builder().build());
+ }
+
+ @Test
+ public void buttonEvent_noButtonCode() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualMouseButtonEvent.Builder()
+ .setAction(VirtualMouseButtonEvent.ACTION_BUTTON_RELEASE).build());
+ }
+
+ @Test
+ public void buttonEvent_noAction() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualMouseButtonEvent.Builder()
+ .setButtonCode(VirtualMouseButtonEvent.BUTTON_BACK).build());
+ }
+
+ @Test
+ public void buttonEvent_created() {
+ final VirtualMouseButtonEvent event = new VirtualMouseButtonEvent.Builder()
+ .setAction(VirtualMouseButtonEvent.ACTION_BUTTON_PRESS)
+ .setButtonCode(VirtualMouseButtonEvent.BUTTON_BACK).build();
+ assertWithMessage("Incorrect button code").that(event.getButtonCode()).isEqualTo(
+ VirtualMouseButtonEvent.BUTTON_BACK);
+ assertWithMessage("Incorrect action").that(event.getAction()).isEqualTo(
+ VirtualMouseButtonEvent.ACTION_BUTTON_PRESS);
+ }
+}
diff --git a/core/tests/coretests/src/android/hardware/input/VirtualMouseRelativeEventTest.java b/core/tests/coretests/src/android/hardware/input/VirtualMouseRelativeEventTest.java
new file mode 100644
index 0000000..c050816
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/input/VirtualMouseRelativeEventTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.hardware.input;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class VirtualMouseRelativeEventTest {
+
+ @Test
+ public void relativeEvent_created() {
+ final VirtualMouseRelativeEvent event = new VirtualMouseRelativeEvent.Builder()
+ .setRelativeX(-5f)
+ .setRelativeY(8f).build();
+ assertWithMessage("Incorrect x value").that(event.getRelativeX()).isEqualTo(-5f);
+ assertWithMessage("Incorrect y value").that(event.getRelativeY()).isEqualTo(8f);
+ }
+}
diff --git a/core/tests/coretests/src/android/hardware/input/VirtualMouseScrollEventTest.java b/core/tests/coretests/src/android/hardware/input/VirtualMouseScrollEventTest.java
new file mode 100644
index 0000000..2259c74
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/input/VirtualMouseScrollEventTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package android.hardware.input;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.testng.Assert.assertThrows;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class VirtualMouseScrollEventTest {
+
+ @Test
+ public void scrollEvent_xOutOfRange() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualMouseScrollEvent.Builder()
+ .setXAxisMovement(1.5f)
+ .setYAxisMovement(1.0f));
+ }
+
+ @Test
+ public void scrollEvent_yOutOfRange() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualMouseScrollEvent.Builder()
+ .setXAxisMovement(0.5f)
+ .setYAxisMovement(1.1f));
+ }
+
+ @Test
+ public void scrollEvent_created() {
+ final VirtualMouseScrollEvent event = new VirtualMouseScrollEvent.Builder()
+ .setXAxisMovement(-1f)
+ .setYAxisMovement(1f).build();
+ assertWithMessage("Incorrect x value").that(event.getXAxisMovement()).isEqualTo(-1f);
+ assertWithMessage("Incorrect y value").that(event.getYAxisMovement()).isEqualTo(1f);
+ }
+}
+
diff --git a/core/tests/coretests/src/android/hardware/input/VirtualTouchEventTest.java b/core/tests/coretests/src/android/hardware/input/VirtualTouchEventTest.java
new file mode 100644
index 0000000..3f504a0
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/input/VirtualTouchEventTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+package android.hardware.input;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.testng.Assert.assertThrows;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class VirtualTouchEventTest {
+
+ @Test
+ public void touchEvent_emptyBuilder() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder().build());
+ }
+
+ @Test
+ public void touchEvent_noAction() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+ .setX(0f)
+ .setY(1f)
+ .setPointerId(1)
+ .build());
+ }
+
+ @Test
+ public void touchEvent_noPointerId() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+ .setAction(VirtualTouchEvent.ACTION_DOWN)
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+ .setX(0f)
+ .setY(1f)
+ .build());
+ }
+
+ @Test
+ public void touchEvent_noToolType() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+ .setAction(VirtualTouchEvent.ACTION_DOWN)
+ .setX(0f)
+ .setY(1f)
+ .setPointerId(1)
+ .build());
+ }
+
+ @Test
+ public void touchEvent_noX() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+ .setAction(VirtualTouchEvent.ACTION_DOWN)
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+ .setY(1f)
+ .setPointerId(1)
+ .build());
+ }
+
+
+ @Test
+ public void touchEvent_noY() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+ .setAction(VirtualTouchEvent.ACTION_DOWN)
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+ .setX(0f)
+ .setPointerId(1)
+ .build());
+ }
+
+ @Test
+ public void touchEvent_created() {
+ final VirtualTouchEvent event = new VirtualTouchEvent.Builder()
+ .setAction(VirtualTouchEvent.ACTION_DOWN)
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+ .setX(0f)
+ .setY(1f)
+ .setPointerId(1)
+ .build();
+ assertWithMessage("Incorrect action").that(event.getAction()).isEqualTo(
+ VirtualTouchEvent.ACTION_DOWN);
+ assertWithMessage("Incorrect tool type").that(event.getToolType()).isEqualTo(
+ VirtualTouchEvent.TOOL_TYPE_FINGER);
+ assertWithMessage("Incorrect x").that(event.getX()).isEqualTo(0f);
+ assertWithMessage("Incorrect y").that(event.getY()).isEqualTo(1f);
+ assertWithMessage("Incorrect pointer id").that(event.getPointerId()).isEqualTo(1);
+ }
+
+ @Test
+ public void touchEvent_created_withPressureAndAxis() {
+ final VirtualTouchEvent event = new VirtualTouchEvent.Builder()
+ .setAction(VirtualTouchEvent.ACTION_DOWN)
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+ .setX(0f)
+ .setY(1f)
+ .setPointerId(1)
+ .setPressure(0.5f)
+ .setMajorAxisSize(10f)
+ .build();
+ assertWithMessage("Incorrect action").that(event.getAction()).isEqualTo(
+ VirtualTouchEvent.ACTION_DOWN);
+ assertWithMessage("Incorrect tool type").that(event.getToolType()).isEqualTo(
+ VirtualTouchEvent.TOOL_TYPE_FINGER);
+ assertWithMessage("Incorrect x").that(event.getX()).isEqualTo(0f);
+ assertWithMessage("Incorrect y").that(event.getY()).isEqualTo(1f);
+ assertWithMessage("Incorrect pointer id").that(event.getPointerId()).isEqualTo(1);
+ assertWithMessage("Incorrect pressure").that(event.getPressure()).isEqualTo(0.5f);
+ assertWithMessage("Incorrect major axis size").that(event.getMajorAxisSize()).isEqualTo(
+ 10f);
+ }
+
+ @Test
+ public void touchEvent_cancelUsedImproperly() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+ .setAction(VirtualTouchEvent.ACTION_CANCEL)
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+ .setX(0f)
+ .setY(1f)
+ .setPointerId(1)
+ .build());
+ }
+
+ @Test
+ public void touchEvent_palmUsedImproperly() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+ .setAction(VirtualTouchEvent.ACTION_MOVE)
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_PALM)
+ .setX(0f)
+ .setY(1f)
+ .setPointerId(1)
+ .build());
+ }
+
+ @Test
+ public void touchEvent_palmAndCancelUsedProperly() {
+ final VirtualTouchEvent event = new VirtualTouchEvent.Builder()
+ .setAction(VirtualTouchEvent.ACTION_CANCEL)
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_PALM)
+ .setX(0f)
+ .setY(1f)
+ .setPointerId(1)
+ .setPressure(0.5f)
+ .setMajorAxisSize(10f)
+ .build();
+ assertWithMessage("Incorrect action").that(event.getAction()).isEqualTo(
+ VirtualTouchEvent.ACTION_CANCEL);
+ assertWithMessage("Incorrect tool type").that(event.getToolType()).isEqualTo(
+ VirtualTouchEvent.TOOL_TYPE_PALM);
+ assertWithMessage("Incorrect x").that(event.getX()).isEqualTo(0f);
+ assertWithMessage("Incorrect y").that(event.getY()).isEqualTo(1f);
+ assertWithMessage("Incorrect pointer id").that(event.getPointerId()).isEqualTo(1);
+ assertWithMessage("Incorrect pressure").that(event.getPressure()).isEqualTo(0.5f);
+ assertWithMessage("Incorrect major axis size").that(event.getMajorAxisSize()).isEqualTo(
+ 10f);
+ }
+}
diff --git a/core/tests/coretests/src/android/net/NetworkPolicyTest.kt b/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
new file mode 100644
index 0000000..d936cad
--- /dev/null
+++ b/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package android.net
+
+import android.text.format.Time.TIMEZONE_UTC
+import androidx.test.runner.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.io.ByteArrayInputStream
+import java.io.DataInputStream
+import java.time.ZoneId
+import kotlin.test.assertEquals
+
+private const val TEST_IMSI1 = "TESTIMSI1"
+private const val TEST_SSID1 = "TESTISSID1"
+
+@RunWith(AndroidJUnit4::class)
+class NetworkPolicyTest {
+ @Test
+ fun testTemplateBackupRestore() {
+ assertPolicyBackupRestore(createTestPolicyForTemplate(
+ NetworkTemplate.buildTemplateWifi(TEST_SSID1)))
+ assertPolicyBackupRestore(createTestPolicyForTemplate(
+ NetworkTemplate.buildTemplateMobileAll(TEST_IMSI1)))
+ assertPolicyBackupRestore(createTestPolicyForTemplate(
+ NetworkTemplate.buildTemplateCarrierMetered(TEST_IMSI1)))
+ }
+
+ private fun createTestPolicyForTemplate(template: NetworkTemplate): NetworkPolicy {
+ return NetworkPolicy(template, NetworkPolicy.buildRule(5, ZoneId.of(TIMEZONE_UTC)),
+ NetworkPolicy.WARNING_DISABLED, NetworkPolicy.LIMIT_DISABLED,
+ NetworkPolicy.SNOOZE_NEVER, NetworkPolicy.SNOOZE_NEVER, NetworkPolicy.SNOOZE_NEVER,
+ /*metered*/ false, /*inferred*/ true)
+ }
+
+ private fun assertPolicyBackupRestore(policy: NetworkPolicy) {
+ val bytes = policy.bytesForBackup
+ val stream = DataInputStream(ByteArrayInputStream(bytes))
+ val restored = NetworkPolicy.getNetworkPolicyFromBackup(stream)
+ assertEquals(policy, restored)
+ }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/os/AidlTest.java b/core/tests/coretests/src/android/os/AidlTest.java
index 4c51415..5f54b09 100644
--- a/core/tests/coretests/src/android/os/AidlTest.java
+++ b/core/tests/coretests/src/android/os/AidlTest.java
@@ -27,11 +27,12 @@
public class AidlTest extends TestCase {
private IAidlTest mRemote;
+ private AidlObject mLocal;
@Override
protected void setUp() throws Exception {
super.setUp();
- AidlObject mLocal = new AidlObject();
+ mLocal = new AidlObject();
mRemote = IAidlTest.Stub.asInterface(mLocal);
}
@@ -417,5 +418,24 @@
}
assertEquals(good, true);
}
+
+ @SmallTest
+ public void testGetTransactionName() throws Exception {
+ assertEquals(15, mLocal.getMaxTransactionId());
+
+ assertEquals("booleanArray",
+ mLocal.getTransactionName(IAidlTest.Stub.TRANSACTION_booleanArray));
+ assertEquals("voidSecurityException",
+ mLocal.getTransactionName(IAidlTest.Stub.TRANSACTION_voidSecurityException));
+ assertEquals("parcelableIn",
+ mLocal.getTransactionName(IAidlTest.Stub.TRANSACTION_parcelableIn));
+
+ assertEquals("IAidlTest:booleanArray",
+ mLocal.getTransactionTraceName(IAidlTest.Stub.TRANSACTION_booleanArray));
+ assertEquals("IAidlTest:voidSecurityException",
+ mLocal.getTransactionTraceName(IAidlTest.Stub.TRANSACTION_voidSecurityException));
+ assertEquals("IAidlTest:parcelableIn",
+ mLocal.getTransactionTraceName(IAidlTest.Stub.TRANSACTION_parcelableIn));
+ }
}
diff --git a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
index 6be9306..bd4d80d 100644
--- a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
+++ b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
@@ -169,13 +169,13 @@
assertEquals("19/1/2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + HOUR,
FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("19/1/2009–22/1/2009",
+ assertEquals("19/1/2009 – 22/1/2009",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY,
FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("19/1/2009–22/4/2009",
+ assertEquals("19/1/2009 – 22/4/2009",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH,
FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("19/1/2009–9/2/2012",
+ assertEquals("19/1/2009 – 9/2/2012",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR,
FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
@@ -251,35 +251,35 @@
assertEquals("19–22 de enero de 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, 0));
- assertEquals("19–22 de ene. de 2009",
+ assertEquals("19–22 de ene de 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY,
FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun, 19 de ene. – jue, 22 de ene. de 2009",
+ assertEquals("lun, 19 de ene – jue, 22 de ene de 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY,
FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
- assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009",
+ assertEquals("lunes, 19 de enero – jueves, 22 de enero de 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
- assertEquals("19 de enero–22 de abril de 2009",
+ assertEquals("19 de enero – 22 de abril de 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, 0));
- assertEquals("19 de ene. – 22 de abr. 2009",
+ assertEquals("19 de ene – 22 de abr 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH,
FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun, 19 de ene. – mié, 22 de abr. de 2009",
+ assertEquals("lun, 19 de ene – mié, 22 de abr de 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH,
FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
assertEquals("enero–abril de 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
- assertEquals("19 de ene. de 2009 – 9 de feb. de 2012",
+ assertEquals("19 de ene de 2009 – 9 de feb de 2012",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR,
FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("ene. de 2009 – feb. de 2012",
+ assertEquals("ene de 2009 – feb de 2012",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR,
FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
- assertEquals("19 de enero de 2009–9 de febrero de 2012",
+ assertEquals("19 de enero de 2009 – 9 de febrero de 2012",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, 0));
- assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 2012",
+ assertEquals("lunes, 19 de enero de 2009 – jueves, 9 de febrero de 2012",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
// The same tests but for es_ES.
@@ -291,10 +291,10 @@
assertEquals("lun, 19 ene – jue, 22 ene 2009",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY,
FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
- assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009",
+ assertEquals("lunes, 19 de enero – jueves, 22 de enero de 2009",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
- assertEquals("19 de enero–22 de abril de 2009",
+ assertEquals("19 de enero – 22 de abril de 2009",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, 0));
assertEquals("19 ene – 22 abr 2009",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH,
@@ -311,9 +311,9 @@
assertEquals("ene 2009 – feb 2012",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR,
FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
- assertEquals("19 de enero de 2009–9 de febrero de 2012",
+ assertEquals("19 de enero de 2009 – 9 de febrero de 2012",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, 0));
- assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 2012",
+ assertEquals("lunes, 19 de enero de 2009 – jueves, 9 de febrero de 2012",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index cfcbc7d..45504c0 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -132,6 +132,8 @@
});
}
+ private static final String TEST_MIME_TYPE = "application/TestType";
+
private static final int CONTENT_PREVIEW_IMAGE = 1;
private static final int CONTENT_PREVIEW_FILE = 2;
private static final int CONTENT_PREVIEW_TEXT = 3;
@@ -564,7 +566,7 @@
assertEquals(mActivityRule.getActivityResult().getResultCode(), RESULT_OK);
}
- @Test @Ignore
+ @Test
public void copyTextToClipboardLogging() throws Exception {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -585,17 +587,15 @@
onView(withId(R.id.chooser_copy_button)).perform(click());
verify(mockLogger, atLeastOnce()).write(logMakerCaptor.capture());
- // First is Activity shown, Second is "with preview"
- assertThat(logMakerCaptor.getAllValues().get(2).getCategory(),
+
+ // The last captured event is the selection of the target.
+ assertThat(logMakerCaptor.getValue().getCategory(),
is(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SYSTEM_TARGET));
- assertThat(logMakerCaptor
- .getAllValues().get(2)
- .getSubtype(),
- is(1));
+ assertThat(logMakerCaptor.getValue().getSubtype(), is(1));
}
- @Test @Ignore
+ @Test
public void testNearbyShareLogging() throws Exception {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -614,12 +614,12 @@
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
- assertThat(logger.numCalls(), is(6));
- // first one should be SHARESHEET_TRIGGERED uievent
- assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(0).event.getId(),
+
+ // SHARESHEET_TRIGGERED:
+ assertThat(logger.event(0).getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
- // second one should be SHARESHEET_STARTED event
+
+ // SHARESHEET_STARTED:
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
@@ -628,22 +628,37 @@
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
assertThat(logger.get(1).previewType, is(3));
- // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
- assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(2).event.getId(),
+
+ // SHARESHEET_APP_LOAD_COMPLETE:
+ assertThat(logger.event(2).getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
- // fourth and fifth are just artifacts of test set-up
- // sixth one should be ranking atom with SHARESHEET_NEARBY_TARGET_SELECTED event
- assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
- assertThat(logger.get(5).targetType,
+
+ // SHARESHEET_DIRECT_LOAD_COMPLETE:
+ assertThat(logger.event(3).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE.getId()));
+
+ // Fifth and sixth are just artifacts of test set-up:
+ assertThat(logger.event(4).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW.getId()));
+ assertThat(logger.event(5).getId(),
+ is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_EXPANDED.getId()));
+
+ // SHARESHEET_EDIT_TARGET_SELECTED:
+ assertThat(logger.get(6).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
+ assertThat(logger.get(6).targetType,
is(ChooserActivityLogger
.SharesheetTargetSelectedEvent.SHARESHEET_NEARBY_TARGET_SELECTED.getId()));
+
+ // No more events.
+ assertThat(logger.numCalls(), is(7));
}
- @Test @Ignore
+ @Test
public void testEditImageLogs() throws Exception {
Intent sendIntent = createSendImageIntent(
Uri.parse("android.resource://com.android.frameworks.coretests/"
@@ -668,11 +683,12 @@
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
- // first one should be SHARESHEET_TRIGGERED uievent
- assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(0).event.getId(),
+
+ // SHARESHEET_TRIGGERED:
+ assertThat(logger.event(0).getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
- // second one should be SHARESHEET_STARTED event
+
+ // SHARESHEET_STARTED:
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("image/png"));
@@ -681,17 +697,32 @@
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
assertThat(logger.get(1).previewType, is(1));
- // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
- assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(2).event.getId(),
+
+ // SHARESHEET_APP_LOAD_COMPLETE:
+ assertThat(logger.event(2).getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
- // fourth and fifth are just artifacts of test set-up
- // sixth one should be ranking atom with SHARESHEET_EDIT_TARGET_SELECTED event
- assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
- assertThat(logger.get(5).targetType,
+
+ // SHARESHEET_DIRECT_LOAD_COMPLETE:
+ assertThat(logger.event(3).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE.getId()));
+
+ // Fifth and sixth are just artifacts of test set-up:
+ assertThat(logger.event(4).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW.getId()));
+ assertThat(logger.event(5).getId(),
+ is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_EXPANDED.getId()));
+
+ // SHARESHEET_EDIT_TARGET_SELECTED:
+ assertThat(logger.get(6).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
+ assertThat(logger.get(6).targetType,
is(ChooserActivityLogger
.SharesheetTargetSelectedEvent.SHARESHEET_EDIT_TARGET_SELECTED.getId()));
+
+ // No more events.
+ assertThat(logger.numCalls(), is(7));
}
@@ -778,7 +809,7 @@
@Test
public void testOnCreateLogging() {
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
MetricsLogger mockLogger = sOverrides.metricsLogger;
ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
@@ -794,7 +825,7 @@
assertThat(logMakerCaptor
.getAllValues().get(0)
.getTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE),
- is("TestType"));
+ is(TEST_MIME_TYPE));
assertThat(logMakerCaptor
.getAllValues().get(0)
.getSubtype(),
@@ -804,7 +835,7 @@
@Test
public void testOnCreateLoggingFromWorkProfile() {
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
sOverrides.alternateProfileSetting = MetricsEvent.MANAGED_PROFILE;
MetricsLogger mockLogger = sOverrides.metricsLogger;
ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
@@ -820,7 +851,7 @@
assertThat(logMakerCaptor
.getAllValues().get(0)
.getTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE),
- is("TestType"));
+ is(TEST_MIME_TYPE));
assertThat(logMakerCaptor
.getAllValues().get(0)
.getSubtype(),
@@ -1476,7 +1507,7 @@
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
markWorkProfileUserAvailable();
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
@@ -1490,7 +1521,7 @@
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
@@ -1512,7 +1543,7 @@
workProfileTargets);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
markWorkProfileUserAvailable();
final ChooserWrapperActivity activity =
@@ -1538,7 +1569,7 @@
createResolvedComponentsForTest(workProfileTargets);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
final ChooserWrapperActivity activity =
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
@@ -1561,7 +1592,7 @@
createResolvedComponentsForTest(workProfileTargets);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
ResolveInfo[] chosen = new ResolveInfo[1];
sOverrides.onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
@@ -1597,7 +1628,7 @@
sOverrides.hasCrossProfileIntents = false;
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
final ChooserWrapperActivity activity =
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
@@ -1623,7 +1654,7 @@
sOverrides.isQuietModeEnabled = true;
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
ResolverActivity.ENABLE_TABBED_VIEW = true;
final ChooserWrapperActivity activity =
@@ -1649,7 +1680,7 @@
createResolvedComponentsForTest(0);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
final ChooserWrapperActivity activity =
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
@@ -1676,7 +1707,7 @@
sOverrides.isQuietModeEnabled = true;
sOverrides.hasCrossProfileIntents = false;
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
@@ -1701,7 +1732,7 @@
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
sOverrides.isQuietModeEnabled = true;
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
@@ -1714,7 +1745,7 @@
.check(matches(isDisplayed()));
}
- @Test @Ignore
+ @Test
public void testAppTargetLogging() throws InterruptedException {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -1743,12 +1774,12 @@
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
- assertThat(logger.numCalls(), is(6));
- // first one should be SHARESHEET_TRIGGERED uievent
- assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(0).event.getId(),
+
+ // SHARESHEET_TRIGGERED:
+ assertThat(logger.event(0).getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
- // second one should be SHARESHEET_STARTED event
+
+ // SHARESHEET_STARTED:
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
@@ -1757,17 +1788,32 @@
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
assertThat(logger.get(1).previewType, is(3));
- // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
- assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(2).event.getId(),
+
+ // SHARESHEET_APP_LOAD_COMPLETE:
+ assertThat(logger.event(2).getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
- // fourth and fifth are just artifacts of test set-up
- // sixth one should be ranking atom with SHARESHEET_APP_TARGET_SELECTED event
- assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
- assertThat(logger.get(5).targetType,
+
+ // SHARESHEET_DIRECT_LOAD_COMPLETE:
+ assertThat(logger.event(3).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE.getId()));
+
+ // Fifth and sixth are just artifacts of test set-up:
+ assertThat(logger.event(4).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW.getId()));
+ assertThat(logger.event(5).getId(),
+ is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_EXPANDED.getId()));
+
+ // SHARESHEET_EDIT_TARGET_SELECTED:
+ assertThat(logger.get(6).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
+ assertThat(logger.get(6).targetType,
is(ChooserActivityLogger
.SharesheetTargetSelectedEvent.SHARESHEET_APP_TARGET_SELECTED.getId()));
+
+ // No more events.
+ assertThat(logger.numCalls(), is(7));
}
@Test @Ignore
@@ -1850,7 +1896,7 @@
.SharesheetTargetSelectedEvent.SHARESHEET_SERVICE_TARGET_SELECTED.getId()));
}
- @Test @Ignore
+ @Test
public void testEmptyDirectRowLogging() throws InterruptedException {
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
@@ -1874,12 +1920,12 @@
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
- assertThat(logger.numCalls(), is(6));
- // first one should be SHARESHEET_TRIGGERED uievent
- assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(0).event.getId(),
+
+ // SHARESHEET_TRIGGERED:
+ assertThat(logger.event(0).getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
- // second one should be SHARESHEET_STARTED event
+
+ // SHARESHEET_STARTED:
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
@@ -1888,20 +1934,30 @@
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
assertThat(logger.get(1).previewType, is(3));
- // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
- assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(2).event.getId(),
+
+ // SHARESHEET_APP_LOAD_COMPLETE:
+ assertThat(logger.event(2).getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
- // fourth and fifth are just artifacts of test set-up
- // sixth one should be ranking atom with SHARESHEET_EMPTY_DIRECT_SHARE_ROW event
- assertThat(logger.get(5).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(5).event.getId(),
+
+ // SHARESHEET_DIRECT_LOAD_COMPLETE:
+ assertThat(logger.event(3).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE.getId()));
+
+ // SHARESHEET_EMPTY_DIRECT_SHARE_ROW:
+ assertThat(logger.event(4).getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW.getId()));
+
+ // Sixth is just an artifact of test set-up:
+ assertThat(logger.event(5).getId(),
+ is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_EXPANDED.getId()));
+
+ assertThat(logger.numCalls(), is(6));
}
- @Test @Ignore
+ @Test
public void testCopyTextToClipboardLogging() throws Exception {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -1920,12 +1976,12 @@
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
- assertThat(logger.numCalls(), is(6));
- // first one should be SHARESHEET_TRIGGERED uievent
- assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(0).event.getId(),
+
+ // SHARESHEET_TRIGGERED:
+ assertThat(logger.event(0).getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
- // second one should be SHARESHEET_STARTED event
+
+ // SHARESHEET_STARTED:
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
@@ -1934,20 +1990,35 @@
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
assertThat(logger.get(1).previewType, is(3));
- // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
- assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(2).event.getId(),
+
+ // SHARESHEET_APP_LOAD_COMPLETE:
+ assertThat(logger.event(2).getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
- // fourth and fifth are just artifacts of test set-up
- // sixth one should be ranking atom with SHARESHEET_COPY_TARGET_SELECTED event
- assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
- assertThat(logger.get(5).targetType,
+
+ // SHARESHEET_DIRECT_LOAD_COMPLETE:
+ assertThat(logger.event(3).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE.getId()));
+
+ // Fifth and sixth are just artifacts of test set-up:
+ assertThat(logger.event(4).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW.getId()));
+ assertThat(logger.event(5).getId(),
+ is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_EXPANDED.getId()));
+
+ // SHARESHEET_EDIT_TARGET_SELECTED:
+ assertThat(logger.get(6).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
+ assertThat(logger.get(6).targetType,
is(ChooserActivityLogger
.SharesheetTargetSelectedEvent.SHARESHEET_COPY_TARGET_SELECTED.getId()));
+
+ // No more events.
+ assertThat(logger.numCalls(), is(7));
}
- @Test @Ignore
+ @Test
public void testSwitchProfileLogging() throws InterruptedException {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
@@ -1959,7 +2030,7 @@
createResolvedComponentsForTest(workProfileTargets);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
final ChooserWrapperActivity activity =
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
@@ -1971,42 +2042,65 @@
ChooserActivityLoggerFake logger =
(ChooserActivityLoggerFake) activity.getChooserActivityLogger();
- assertThat(logger.numCalls(), is(8));
- // first one should be SHARESHEET_TRIGGERED uievent
- assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(0).event.getId(),
+
+ // SHARESHEET_TRIGGERED:
+ assertThat(logger.event(0).getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
- // second one should be SHARESHEET_STARTED event
+
+ // SHARESHEET_STARTED:
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
- assertThat(logger.get(1).mimeType, is("TestType"));
+ assertThat(logger.get(1).mimeType, is(TEST_MIME_TYPE));
assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
assertThat(logger.get(1).previewType, is(3));
- // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
- assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(2).event.getId(),
+
+ // SHARESHEET_APP_LOAD_COMPLETE:
+ assertThat(logger.event(2).getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
- // fourth one is artifact of test setup
- // fifth one is switch to work profile
- assertThat(logger.get(4).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(4).event.getId(),
+
+ // SHARESHEET_DIRECT_LOAD_COMPLETE:
+ assertThat(logger.event(3).getId(),
is(ChooserActivityLogger
- .SharesheetStandardEvent.SHARESHEET_PROFILE_CHANGED.getId()));
- // sixth one should be SHARESHEET_APP_LOAD_COMPLETE uievent
- assertThat(logger.get(5).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(5).event.getId(),
+ .SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE.getId()));
+
+ // Fifth is just an artifact of test set-up:
+ assertThat(logger.event(4).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW.getId()));
+
+ // SHARESHEET_PROFILE_CHANGED:
+ assertThat(logger.event(5).getId(),
+ is(ChooserActivityLogger.SharesheetStandardEvent
+ .SHARESHEET_PROFILE_CHANGED.getId()));
+
+ // Repeat the loading steps in the new profile:
+
+ // SHARESHEET_APP_LOAD_COMPLETE:
+ assertThat(logger.event(6).getId(),
is(ChooserActivityLogger
.SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
- // seventh one is artifact of test setup
- // eigth one is switch to work profile
- assertThat(logger.get(7).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
- assertThat(logger.get(7).event.getId(),
+
+ // SHARESHEET_DIRECT_LOAD_COMPLETE:
+ assertThat(logger.event(7).getId(),
is(ChooserActivityLogger
- .SharesheetStandardEvent.SHARESHEET_PROFILE_CHANGED.getId()));
+ .SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE.getId()));
+
+ // Ninth is again an artifact of test set-up:
+ assertThat(logger.event(8).getId(),
+ is(ChooserActivityLogger
+ .SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW.getId()));
+
+ // SHARESHEET_PROFILE_CHANGED:
+ assertThat(logger.event(9).getId(),
+ is(ChooserActivityLogger.SharesheetStandardEvent
+ .SHARESHEET_PROFILE_CHANGED.getId()));
+
+ // No more events (this profile was already loaded).
+ assertThat(logger.numCalls(), is(10));
}
@Test
@@ -2019,7 +2113,7 @@
Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
ResolveInfo[] chosen = new ResolveInfo[1];
sOverrides.onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
@@ -2043,7 +2137,7 @@
Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
ResolveInfo[] chosen = new ResolveInfo[1];
sOverrides.onSafelyStartCallback = targetInfo -> {
chosen[0] = targetInfo.getResolveInfo();
@@ -2244,7 +2338,7 @@
return null;
};
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
@@ -2275,7 +2369,7 @@
return null;
};
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
@@ -2299,7 +2393,7 @@
createResolvedComponentsForTest(3);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
sOverrides.isWorkProfileUserRunning = false;
final ChooserWrapperActivity activity =
@@ -2331,7 +2425,7 @@
return null;
};
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
waitForIdle();
@@ -2355,7 +2449,7 @@
createResolvedComponentsForTest(3);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
- sendIntent.setType("TestType");
+ sendIntent.setType(TEST_MIME_TYPE);
sOverrides.isWorkProfileUserUnlocked = false;
final ChooserWrapperActivity activity =
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 756425e..0b8dc3f 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -180,6 +180,7 @@
<assign-permission name="android.permission.WATCH_APPOPS" uid="cameraserver" />
<assign-permission name="android.permission.MANAGE_APP_OPS_MODES" uid="cameraserver" />
<assign-permission name="android.permission.OBSERVE_SENSOR_PRIVACY" uid="cameraserver" />
+ <assign-permission name="android.permission.REAL_GET_TASKS" uid="cameraserver" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 60cb9d3..81db63e 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -519,6 +519,9 @@
<permission name="android.permission.LOCK_DEVICE" />
<!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
<permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
+ <!-- Permission required for CTS test - CommunalManagerTest -->
+ <permission name="android.permission.WRITE_COMMUNAL_STATE" />
+ <permission name="android.permission.READ_COMMUNAL_STATE" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java
index 9a41cb4..fa173072 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java
@@ -189,7 +189,10 @@
if (!actualPerm.containsAll(expectedPerm)) {
return buildDescription(tree)
.setMessage("Method " + method.name.toString() + "() annotated " + expectedPerm
- + " but too wide; only invokes methods requiring " + actualPerm)
+ + " but too wide; only invokes methods requiring " + actualPerm
+ + "\n If calling an AIDL interface, it can be annotated by adding:"
+ + "\n @JavaPassthrough(annotation=\""
+ + "@android.annotation.RequiresPermission(...)\")")
.build();
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index af19bd0..b8e8b01 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -295,11 +295,12 @@
@NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity,
@NonNull TaskFragmentContainer secondaryContainer,
@NonNull SplitRule splitRule) {
+ SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity,
+ secondaryContainer, splitRule);
+ // Remove container later to prevent pinning escaping toast showing in lock task mode.
if (splitRule instanceof SplitPairRule && ((SplitPairRule) splitRule).shouldClearTop()) {
removeExistingSecondaryContainers(wct, primaryContainer);
}
- SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity,
- secondaryContainer, splitRule);
mSplitContainers.add(splitContainer);
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 81be21c..ade5731 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -112,8 +112,7 @@
secondaryContainer.setLastRequestedBounds(secondaryRectBounds);
// Set adjacent to each other so that the containers below will be invisible.
- setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
- secondaryContainer.getTaskFragmentToken(), rule);
+ setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule);
mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
@@ -149,8 +148,7 @@
secondaryActivity, secondaryRectBounds, primaryContainer);
// Set adjacent to each other so that the containers below will be invisible.
- setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
- secondaryContainer.getTaskFragmentToken(), rule);
+ setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule);
mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
@@ -269,8 +267,22 @@
final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds);
- setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
- secondaryContainer.getTaskFragmentToken(), rule);
+ setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule);
+ }
+
+ private void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskFragmentContainer primaryContainer,
+ @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule) {
+ final Rect parentBounds = getParentContainerBounds(primaryContainer);
+ // Clear adjacent TaskFragments if the container is shown in fullscreen, or the
+ // secondaryContainer could not be finished.
+ if (!shouldShowSideBySide(parentBounds, splitRule)) {
+ setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
+ null /* secondary */, null /* splitRule */);
+ } else {
+ setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
+ secondaryContainer.getTaskFragmentToken(), splitRule);
+ }
}
/**
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml b/libs/WindowManager/Shell/res/color/tv_pip_menu_icon.xml
similarity index 65%
copy from libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
copy to libs/WindowManager/Shell/res/color/tv_pip_menu_icon.xml
index 22cd384..2758704 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
+++ b/libs/WindowManager/Shell/res/color/tv_pip_menu_icon.xml
@@ -14,8 +14,10 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="@color/size_compat_background"/>
- <corners android:radius="@dimen/size_compat_hint_corner_radius"/>
-</shape>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"
+ android:color="@color/tv_pip_menu_icon_focused" />
+ <item android:state_enabled="false"
+ android:color="@color/tv_pip_menu_icon_disabled" />
+ <item android:color="@color/tv_pip_menu_icon_unfocused" />
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml b/libs/WindowManager/Shell/res/color/tv_pip_menu_icon_bg.xml
similarity index 72%
copy from libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
copy to libs/WindowManager/Shell/res/color/tv_pip_menu_icon_bg.xml
index 22cd384..4f5e63d 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
+++ b/libs/WindowManager/Shell/res/color/tv_pip_menu_icon_bg.xml
@@ -14,8 +14,8 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="@color/size_compat_background"/>
- <corners android:radius="@dimen/size_compat_hint_corner_radius"/>
-</shape>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"
+ android:color="@color/tv_pip_menu_icon_bg_focused" />
+ <item android:color="@color/tv_pip_menu_icon_bg_unfocused" />
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml b/libs/WindowManager/Shell/res/drawable/compat_hint_bubble.xml
similarity index 85%
rename from libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
rename to libs/WindowManager/Shell/res/drawable/compat_hint_bubble.xml
index 22cd384..26848b1 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
+++ b/libs/WindowManager/Shell/res/drawable/compat_hint_bubble.xml
@@ -16,6 +16,6 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
- <solid android:color="@color/size_compat_background"/>
- <corners android:radius="@dimen/size_compat_hint_corner_radius"/>
+ <solid android:color="@color/compat_controls_background"/>
+ <corners android:radius="@dimen/compat_hint_corner_radius"/>
</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml b/libs/WindowManager/Shell/res/drawable/compat_hint_point.xml
similarity index 88%
rename from libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
rename to libs/WindowManager/Shell/res/drawable/compat_hint_point.xml
index af9063a..0e0ca37 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
+++ b/libs/WindowManager/Shell/res/drawable/compat_hint_point.xml
@@ -15,11 +15,11 @@
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/size_compat_hint_point_width"
+ android:width="@dimen/compat_hint_point_width"
android:height="8dp"
android:viewportWidth="10"
android:viewportHeight="8">
<path
- android:fillColor="@color/size_compat_background"
+ android:fillColor="@color/compat_controls_background"
android:pathData="M10,0 l-4.1875,6.6875 a1,1 0 0,1 -1.625,0 l-4.1875,-6.6875z"/>
</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
index 18caa35..ab74e43 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
@@ -20,16 +20,16 @@
android:viewportWidth="48"
android:viewportHeight="48">
<path
- android:fillColor="@color/size_compat_background"
+ android:fillColor="@color/compat_controls_background"
android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0" />
<group
android:translateX="12"
android:translateY="12">
<path
- android:fillColor="@color/size_compat_text"
+ android:fillColor="@color/compat_controls_text"
android:pathData="M6,13c0,-1.65 0.67,-3.15 1.76,-4.24L6.34,7.34C4.9,8.79 4,10.79 4,13c0,4.08 3.05,7.44 7,7.93v-2.02C8.17,18.43 6,15.97 6,13z"/>
<path
- android:fillColor="@color/size_compat_text"
+ android:fillColor="@color/compat_controls_text"
android:pathData="M20,13c0,-4.42 -3.58,-8 -8,-8c-0.06,0 -0.12,0.01 -0.18,0.01v0l1.09,-1.09L11.5,2.5L8,6l3.5,3.5l1.41,-1.41l-1.08,-1.08C11.89,7.01 11.95,7 12,7c3.31,0 6,2.69 6,6c0,2.97 -2.17,5.43 -5,5.91v2.02C16.95,20.44 20,17.08 20,13z"/>
</group>
</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/tv_pip_button_focused.xml b/libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml
similarity index 74%
rename from libs/WindowManager/Shell/res/drawable/tv_pip_button_focused.xml
rename to libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml
index cce1303..1938f45 100644
--- a/libs/WindowManager/Shell/res/drawable/tv_pip_button_focused.xml
+++ b/libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml
@@ -14,5 +14,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="#9AFFFFFF" android:radius="17dp" />
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="@dimen/pip_menu_button_radius" />
+ <solid android:color="@color/tv_pip_menu_icon_bg" />
+</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml b/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml
similarity index 77%
copy from libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
copy to libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml
index 22cd384..9bc0311 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
+++ b/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml
@@ -15,7 +15,8 @@
~ limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="@color/size_compat_background"/>
- <corners android:radius="@dimen/size_compat_hint_corner_radius"/>
+ android:shape="rectangle">
+ <corners android:radius="@dimen/pip_menu_border_radius" />
+ <stroke android:width="@dimen/pip_menu_border_width"
+ android:color="@color/tv_pip_menu_focus_border" />
</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
new file mode 100644
index 0000000..c04e258e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
@@ -0,0 +1,46 @@
+<?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"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipToPadding="false"
+ android:paddingEnd="@dimen/compat_hint_padding_end"
+ android:paddingBottom="5dp"
+ android:clickable="true">
+
+ <TextView
+ android:id="@+id/compat_mode_hint_text"
+ android:layout_width="188dp"
+ android:layout_height="wrap_content"
+ android:lineSpacingExtra="4sp"
+ android:background="@drawable/compat_hint_bubble"
+ android:padding="16dp"
+ android:textAlignment="viewStart"
+ android:textColor="@color/compat_controls_text"
+ android:textSize="14sp"/>
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end"
+ android:src="@drawable/compat_hint_point"
+ android:paddingHorizontal="@dimen/compat_hint_corner_radius"
+ android:contentDescription="@null"/>
+
+</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
similarity index 82%
rename from libs/WindowManager/Shell/res/layout/size_compat_ui.xml
rename to libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
index 82ebee2..6f946b2 100644
--- a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
+++ b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
@@ -14,10 +14,15 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.wm.shell.sizecompatui.SizeCompatRestartButton
+<com.android.wm.shell.compatui.CompatUILayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="bottom|end">
+
+ <include android:id="@+id/size_compat_hint"
+ layout="@layout/compat_mode_hint"/>
<FrameLayout
android:layout_width="@dimen/size_compat_button_width"
@@ -36,4 +41,4 @@
</FrameLayout>
-</com.android.wm.shell.sizecompatui.SizeCompatRestartButton>
+</com.android.wm.shell.compatui.CompatUILayout>
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
deleted file mode 100644
index d0e7c42..0000000
--- a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
+++ /dev/null
@@ -1,58 +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.
--->
-<com.android.wm.shell.sizecompatui.SizeCompatHintPopup
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
-
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:clipToPadding="false"
- android:paddingBottom="5dp">
-
- <LinearLayout
- android:id="@+id/size_compat_hint_popup"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:clickable="true">
-
- <TextView
- android:layout_width="188dp"
- android:layout_height="wrap_content"
- android:lineSpacingExtra="4sp"
- android:background="@drawable/size_compat_hint_bubble"
- android:padding="16dp"
- android:text="@string/restart_button_description"
- android:textAlignment="viewStart"
- android:textColor="@color/size_compat_text"
- android:textSize="14sp"/>
-
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end"
- android:src="@drawable/size_compat_hint_point"
- android:paddingHorizontal="@dimen/size_compat_hint_corner_radius"
- android:contentDescription="@null"/>
-
- </LinearLayout>
-
- </FrameLayout>
-
-</com.android.wm.shell.sizecompatui.SizeCompatHintPopup>
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
index 49e2379..5b90c99 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
@@ -18,35 +18,54 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tv_pip_menu"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#CC000000">
+ android:layout_height="match_parent">
- <LinearLayout
- android:id="@+id/tv_pip_menu_action_buttons"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginTop="350dp"
- android:orientation="horizontal"
- android:alpha="0">
+ <FrameLayout
+ android:id="@+id/tv_pip_menu_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:alpha="0" >
- <com.android.wm.shell.pip.tv.TvPipMenuActionButton
- android:id="@+id/tv_pip_menu_fullscreen_button"
- android:layout_width="@dimen/picture_in_picture_button_width"
- android:layout_height="wrap_content"
- android:src="@drawable/pip_ic_fullscreen_white"
- android:text="@string/pip_fullscreen" />
+ <HorizontalScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_centerHorizontal="true"
+ android:gravity="center"
+ android:scrollbars="none"
+ android:requiresFadingEdge="vertical"
+ android:fadingEdgeLength="30dp">
- <com.android.wm.shell.pip.tv.TvPipMenuActionButton
- android:id="@+id/tv_pip_menu_close_button"
- android:layout_width="@dimen/picture_in_picture_button_width"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/picture_in_picture_button_start_margin"
- android:src="@drawable/pip_ic_close_white"
- android:text="@string/pip_close" />
+ <LinearLayout
+ android:id="@+id/tv_pip_menu_action_buttons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingStart="@dimen/pip_menu_button_wrapper_margin"
+ android:paddingEnd="@dimen/pip_menu_button_wrapper_margin"
+ android:gravity="center"
+ android:orientation="horizontal">
- <!-- More TvPipMenuActionButtons may be added here at runtime. -->
+ <com.android.wm.shell.pip.tv.TvPipMenuActionButton
+ android:id="@+id/tv_pip_menu_fullscreen_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/pip_ic_fullscreen_white"
+ android:text="@string/pip_fullscreen" />
- </LinearLayout>
+ <com.android.wm.shell.pip.tv.TvPipMenuActionButton
+ android:id="@+id/tv_pip_menu_close_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/pip_ic_close_white"
+ android:text="@string/pip_close" />
+ <!-- More TvPipMenuActionButtons may be added here at runtime. -->
+
+ </LinearLayout>
+ </HorizontalScrollView>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/tv_pip_menu_border"/>
+ </FrameLayout>
</FrameLayout>
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
index 5925008..f9d0968 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
@@ -15,36 +15,20 @@
limitations under the License.
-->
<!-- Layout for TvPipMenuActionButton -->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/button"
+ android:layout_width="@dimen/pip_menu_button_size"
+ android:layout_height="@dimen/pip_menu_button_size"
+ android:layout_marginStart="@dimen/pip_menu_button_margin"
+ android:layout_marginEnd="@dimen/pip_menu_button_margin"
+ android:background="@drawable/tv_pip_button_bg"
+ android:focusable="true">
- <ImageView android:id="@+id/button"
- android:layout_width="34dp"
- android:layout_height="34dp"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"
- android:focusable="true"
- android:src="@drawable/tv_pip_button_focused"
- android:importantForAccessibility="yes" />
-
- <ImageView android:id="@+id/icon"
- android:layout_width="34dp"
- android:layout_height="34dp"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"
- android:padding="5dp"
- android:importantForAccessibility="no" />
-
- <TextView android:id="@+id/desc"
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:layout_below="@id/icon"
- android:layout_centerHorizontal="true"
- android:layout_marginTop="3dp"
- android:gravity="center"
- android:text="@string/pip_fullscreen"
- android:alpha="0"
- android:fontFamily="sans-serif"
- android:textSize="12sp"
- android:textColor="#EEEEEE"
- android:importantForAccessibility="no" />
-</merge>
+ <ImageView android:id="@+id/icon"
+ android:layout_width="@dimen/pip_menu_icon_size"
+ android:layout_height="@dimen/pip_menu_icon_size"
+ android:layout_gravity="center"
+ android:duplicateParentState="true"
+ android:tint="@color/tv_pip_menu_icon" />
+</FrameLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu_additional_action_button.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu_additional_action_button.xml
deleted file mode 100644
index bf4eb26..0000000
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu_additional_action_button.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.wm.shell.pip.tv.TvPipMenuActionButton
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/picture_in_picture_button_width"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/picture_in_picture_button_start_margin" />
diff --git a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
index 7920fd2..e41ebc4 100644
--- a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
+++ b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
@@ -16,7 +16,12 @@
-->
<resources>
<!-- The dimensions to user for picture-in-picture action buttons. -->
- <dimen name="picture_in_picture_button_width">100dp</dimen>
- <dimen name="picture_in_picture_button_start_margin">-50dp</dimen>
+ <dimen name="pip_menu_button_size">40dp</dimen>
+ <dimen name="pip_menu_button_radius">20dp</dimen>
+ <dimen name="pip_menu_icon_size">20dp</dimen>
+ <dimen name="pip_menu_button_margin">4dp</dimen>
+ <dimen name="pip_menu_button_wrapper_margin">26dp</dimen>
+ <dimen name="pip_menu_border_width">2dp</dimen>
+ <dimen name="pip_menu_border_radius">0dp</dimen>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index 23a2172..cf596f7 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -30,9 +30,9 @@
<color name="bubbles_dark">@color/GM2_grey_800</color>
<color name="bubbles_icon_tint">@color/GM2_grey_700</color>
- <!-- Size Compat Restart Button -->
- <color name="size_compat_background">@android:color/system_neutral1_800</color>
- <color name="size_compat_text">@android:color/system_neutral1_50</color>
+ <!-- Compat controls UI -->
+ <color name="compat_controls_background">@android:color/system_neutral1_800</color>
+ <color name="compat_controls_text">@android:color/system_neutral1_50</color>
<!-- GM2 colors -->
<color name="GM2_grey_200">#E8EAED</color>
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml b/libs/WindowManager/Shell/res/values/colors_tv.xml
similarity index 62%
copy from libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
copy to libs/WindowManager/Shell/res/values/colors_tv.xml
index 22cd384..17387fa 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
+++ b/libs/WindowManager/Shell/res/values/colors_tv.xml
@@ -14,8 +14,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="@color/size_compat_background"/>
- <corners android:radius="@dimen/size_compat_hint_corner_radius"/>
-</shape>
\ No newline at end of file
+<resources>
+ <color name="tv_pip_menu_icon_focused">#0E0E0F</color>
+ <color name="tv_pip_menu_icon_unfocused">#E8EAED</color>
+ <color name="tv_pip_menu_icon_disabled">#80868B</color>
+ <color name="tv_pip_menu_icon_bg_focused">#E8EAED</color>
+ <color name="tv_pip_menu_icon_bg_unfocused">#777777</color>
+ <color name="tv_pip_menu_focus_border">#CCE8EAED</color>
+</resources>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 9e77578..18e91f4 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -206,11 +206,15 @@
<!-- The height of the size compat restart button including padding. -->
<dimen name="size_compat_button_height">64dp</dimen>
- <!-- The radius of the corners of the size compat hint bubble. -->
- <dimen name="size_compat_hint_corner_radius">28dp</dimen>
+ <!-- The radius of the corners of the compat hint bubble. -->
+ <dimen name="compat_hint_corner_radius">28dp</dimen>
- <!-- The width of the size compat hint point. -->
- <dimen name="size_compat_hint_point_width">10dp</dimen>
+ <!-- The width of the compat hint point. -->
+ <dimen name="compat_hint_point_width">10dp</dimen>
+
+ <!-- The end padding for the compat hint. Computed as (size_compat_button_width / 2
+ - compat_hint_corner_radius - compat_hint_point_width /2). -->
+ <dimen name="compat_hint_padding_end">7dp</dimen>
<!-- The width of the brand image on staring surface. -->
<dimen name="starting_surface_brand_image_width">200dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 8e98b82..8b3a356 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -52,8 +52,8 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.recents.RecentTasksController;
-import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.startingsurface.StartingWindowController;
import java.io.PrintWriter;
@@ -69,7 +69,7 @@
* TODO(b/167582004): may consider consolidating this class and TaskOrganizer
*/
public class ShellTaskOrganizer extends TaskOrganizer implements
- SizeCompatUIController.SizeCompatUICallback {
+ CompatUIController.CompatUICallback {
// Intentionally using negative numbers here so the positive numbers can be used
// for task id specific listeners that will be added later.
@@ -98,9 +98,9 @@
default void onTaskInfoChanged(RunningTaskInfo taskInfo) {}
default void onTaskVanished(RunningTaskInfo taskInfo) {}
default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {}
- /** Whether this task listener supports size compat UI. */
- default boolean supportSizeCompatUI() {
- // All TaskListeners should support size compat except PIP.
+ /** Whether this task listener supports compat UI. */
+ default boolean supportCompatUI() {
+ // All TaskListeners should support compat UI except PIP.
return true;
}
/** Attaches the a child window surface to the task surface. */
@@ -159,11 +159,11 @@
private StartingWindowController mStartingWindow;
/**
- * In charge of showing size compat UI. Can be {@code null} if device doesn't support size
+ * In charge of showing compat UI. Can be {@code null} if device doesn't support size
* compat.
*/
@Nullable
- private final SizeCompatUIController mSizeCompatUI;
+ private final CompatUIController mCompatUI;
@Nullable
private final Optional<RecentTasksController> mRecentTasks;
@@ -172,32 +172,32 @@
private RunningTaskInfo mLastFocusedTaskInfo;
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
- this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */,
+ this(null /* taskOrganizerController */, mainExecutor, context, null /* compatUI */,
Optional.empty() /* recentTasksController */);
}
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
- SizeCompatUIController sizeCompatUI) {
- this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI,
+ CompatUIController compatUI) {
+ this(null /* taskOrganizerController */, mainExecutor, context, compatUI,
Optional.empty() /* recentTasksController */);
}
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
- SizeCompatUIController sizeCompatUI,
+ CompatUIController compatUI,
Optional<RecentTasksController> recentTasks) {
- this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI,
+ this(null /* taskOrganizerController */, mainExecutor, context, compatUI,
recentTasks);
}
@VisibleForTesting
ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor,
- Context context, @Nullable SizeCompatUIController sizeCompatUI,
+ Context context, @Nullable CompatUIController compatUI,
Optional<RecentTasksController> recentTasks) {
super(taskOrganizerController, mainExecutor);
- mSizeCompatUI = sizeCompatUI;
+ mCompatUI = compatUI;
mRecentTasks = recentTasks;
- if (sizeCompatUI != null) {
- sizeCompatUI.setSizeCompatUICallback(this);
+ if (compatUI != null) {
+ compatUI.setCompatUICallback(this);
}
}
@@ -428,7 +428,7 @@
listener.onTaskAppeared(info.getTaskInfo(), info.getLeash());
}
notifyLocusVisibilityIfNeeded(info.getTaskInfo());
- notifySizeCompatUI(info.getTaskInfo(), listener);
+ notifyCompatUI(info.getTaskInfo(), listener);
}
/**
@@ -459,8 +459,8 @@
}
notifyLocusVisibilityIfNeeded(taskInfo);
if (updated || !taskInfo.equalsForSizeCompat(data.getTaskInfo())) {
- // Notify the size compat UI if the listener or task info changed.
- notifySizeCompatUI(taskInfo, newListener);
+ // Notify the compat UI if the listener or task info changed.
+ notifyCompatUI(taskInfo, newListener);
}
if (data.getTaskInfo().getWindowingMode() != taskInfo.getWindowingMode()) {
// Notify the recent tasks when a task changes windowing modes
@@ -504,8 +504,8 @@
listener.onTaskVanished(taskInfo);
}
notifyLocusVisibilityIfNeeded(taskInfo);
- // Pass null for listener to remove the size compat UI on this task if there is any.
- notifySizeCompatUI(taskInfo, null /* taskListener */);
+ // Pass null for listener to remove the compat UI on this task if there is any.
+ notifyCompatUI(taskInfo, null /* taskListener */);
// Notify the recent tasks that a task has been removed
mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskRemoved(taskInfo));
}
@@ -618,28 +618,28 @@
}
/**
- * Notifies {@link SizeCompatUIController} about the size compat info changed on the give Task
+ * Notifies {@link CompatUIController} about the compat info changed on the give Task
* to update the UI accordingly.
*
* @param taskInfo the new Task info
* @param taskListener listener to handle the Task Surface placement. {@code null} if task is
* vanished.
*/
- private void notifySizeCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener) {
- if (mSizeCompatUI == null) {
+ private void notifyCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener) {
+ if (mCompatUI == null) {
return;
}
- // The task is vanished or doesn't support size compat UI, notify to remove size compat UI
+ // The task is vanished or doesn't support compat UI, notify to remove compat UI
// on this Task if there is any.
- if (taskListener == null || !taskListener.supportSizeCompatUI()
+ if (taskListener == null || !taskListener.supportCompatUI()
|| !taskInfo.topActivityInSizeCompat || !taskInfo.isVisible) {
- mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
+ mCompatUI.onCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
null /* taskConfig */, null /* taskListener */);
return;
}
- mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
+ mCompatUI.onCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
taskInfo.configuration, taskListener);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 2f3214d..54e743f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -41,6 +41,7 @@
import android.window.WindowContainerTransaction;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
@@ -77,6 +78,7 @@
private final ShellTaskOrganizer mTaskOrganizer;
private final Executor mShellExecutor;
private final SyncTransactionQueue mSyncQueue;
+ private final TaskViewTransitions mTaskViewTransitions;
private ActivityManager.RunningTaskInfo mTaskInfo;
private WindowContainerToken mTaskToken;
@@ -92,17 +94,27 @@
private final Rect mTmpRootRect = new Rect();
private final int[] mTmpLocation = new int[2];
- public TaskView(Context context, ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue) {
+ public TaskView(Context context, ShellTaskOrganizer organizer,
+ TaskViewTransitions taskViewTransitions, SyncTransactionQueue syncQueue) {
super(context, null, 0, 0, true /* disableBackgroundLayer */);
mTaskOrganizer = organizer;
mShellExecutor = organizer.getExecutor();
mSyncQueue = syncQueue;
+ mTaskViewTransitions = taskViewTransitions;
+ if (mTaskViewTransitions != null) {
+ mTaskViewTransitions.addTaskView(this);
+ }
setUseAlpha();
getHolder().addCallback(this);
mGuard.open("release");
}
+ /** Until all users are converted, we may have mixed-use (eg. Car). */
+ private boolean isUsingShellTransitions() {
+ return mTaskViewTransitions != null && Transitions.ENABLE_SHELL_TRANSITIONS;
+ }
+
/**
* Only one listener may be set on the view, throws an exception otherwise.
*/
@@ -129,6 +141,14 @@
@NonNull ActivityOptions options, @Nullable Rect launchBounds) {
prepareActivityOptions(options, launchBounds);
LauncherApps service = mContext.getSystemService(LauncherApps.class);
+ if (isUsingShellTransitions()) {
+ mShellExecutor.execute(() -> {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.startShortcut(mContext.getPackageName(), shortcut, options.toBundle());
+ mTaskViewTransitions.startTaskView(wct, this);
+ });
+ return;
+ }
try {
service.startShortcut(shortcut, null /* sourceBounds */, options.toBundle());
} catch (Exception e) {
@@ -148,6 +168,14 @@
public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
@NonNull ActivityOptions options, @Nullable Rect launchBounds) {
prepareActivityOptions(options, launchBounds);
+ if (isUsingShellTransitions()) {
+ mShellExecutor.execute(() -> {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.sendPendingIntent(pendingIntent, fillInIntent, options.toBundle());
+ mTaskViewTransitions.startTaskView(wct, this);
+ });
+ return;
+ }
try {
pendingIntent.send(mContext, 0 /* code */, fillInIntent,
null /* onFinished */, null /* handler */, null /* requiredPermission */,
@@ -177,6 +205,16 @@
mObscuredTouchRect = obscuredRect;
}
+ private void onLocationChanged(WindowContainerTransaction wct) {
+ // Update based on the screen bounds
+ getBoundsOnScreen(mTmpRect);
+ getRootView().getBoundsOnScreen(mTmpRootRect);
+ if (!mTmpRootRect.contains(mTmpRect)) {
+ mTmpRect.offsetTo(0, 0);
+ }
+ wct.setBounds(mTaskToken, mTmpRect);
+ }
+
/**
* Call when view position or size has changed. Do not call when animating.
*/
@@ -184,15 +222,12 @@
if (mTaskToken == null) {
return;
}
- // Update based on the screen bounds
- getBoundsOnScreen(mTmpRect);
- getRootView().getBoundsOnScreen(mTmpRootRect);
- if (!mTmpRootRect.contains(mTmpRect)) {
- mTmpRect.offsetTo(0, 0);
- }
+ // Sync Transactions can't operate simultaneously with shell transition collection.
+ // The transition animation (upon showing) will sync the location itself.
+ if (isUsingShellTransitions() && mTaskViewTransitions.hasPending()) return;
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setBounds(mTaskToken, mTmpRect);
+ onLocationChanged(wct);
mSyncQueue.queue(wct);
}
@@ -217,6 +252,9 @@
private void performRelease() {
getHolder().removeCallback(this);
+ if (mTaskViewTransitions != null) {
+ mTaskViewTransitions.removeTaskView(this);
+ }
mShellExecutor.execute(() -> {
mTaskOrganizer.removeListener(this);
resetTaskInfo();
@@ -254,6 +292,10 @@
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl leash) {
+ if (isUsingShellTransitions()) {
+ // Everything else handled by enter transition.
+ return;
+ }
mTaskInfo = taskInfo;
mTaskToken = taskInfo.token;
mTaskLeash = leash;
@@ -288,6 +330,8 @@
@Override
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ // Unlike Appeared, we can't yet guarantee that vanish will happen within a transition that
+ // we know about -- so leave clean-up here even if shell transitions are enabled.
if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
if (mListener != null) {
@@ -355,6 +399,10 @@
// Nothing to update, task is not yet available
return;
}
+ if (isUsingShellTransitions()) {
+ mTaskViewTransitions.setTaskViewVisible(this, true /* visible */);
+ return;
+ }
// Reparent the task when this surface is created
mTransaction.reparent(mTaskLeash, getSurfaceControl())
.show(mTaskLeash)
@@ -380,6 +428,11 @@
return;
}
+ if (isUsingShellTransitions()) {
+ mTaskViewTransitions.setTaskViewVisible(this, false /* visible */);
+ return;
+ }
+
// Unparent the task when this surface is destroyed
mTransaction.reparent(mTaskLeash, null).apply();
updateTaskVisibility();
@@ -421,4 +474,91 @@
super.onDetachedFromWindow();
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
}
+
+ ActivityManager.RunningTaskInfo getTaskInfo() {
+ return mTaskInfo;
+ }
+
+ void prepareHideAnimation(@NonNull SurfaceControl.Transaction finishTransaction) {
+ if (mTaskToken == null) {
+ // Nothing to update, task is not yet available
+ return;
+ }
+
+ finishTransaction.reparent(mTaskLeash, null).apply();
+
+ if (mListener != null) {
+ final int taskId = mTaskInfo.taskId;
+ mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated /* visible */);
+ }
+ }
+
+ /**
+ * Called when the associated Task closes. If the TaskView is just being hidden, prepareHide
+ * is used instead.
+ */
+ void prepareCloseAnimation() {
+ if (mTaskToken != null) {
+ if (mListener != null) {
+ final int taskId = mTaskInfo.taskId;
+ mListenerExecutor.execute(() -> {
+ mListener.onTaskRemovalStarted(taskId);
+ });
+ }
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
+ }
+ resetTaskInfo();
+ }
+
+ void prepareOpenAnimation(final boolean newTask,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash,
+ WindowContainerTransaction wct) {
+ mTaskInfo = taskInfo;
+ mTaskToken = mTaskInfo.token;
+ mTaskLeash = leash;
+ if (mSurfaceCreated) {
+ // Surface is ready, so just reparent the task to this surface control
+ startTransaction.reparent(mTaskLeash, getSurfaceControl())
+ .show(mTaskLeash)
+ .apply();
+ // Also reparent on finishTransaction since the finishTransaction will reparent back
+ // to its "original" parent by default.
+ finishTransaction.reparent(mTaskLeash, getSurfaceControl())
+ .setPosition(mTaskLeash, 0, 0)
+ .apply();
+
+ // TODO: determine if this is really necessary or not
+ onLocationChanged(wct);
+ } else {
+ // The surface has already been destroyed before the task has appeared,
+ // so go ahead and hide the task entirely
+ wct.setHidden(mTaskToken, true /* hidden */);
+ // listener callback is below
+ }
+ if (newTask) {
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true /* intercept */);
+ }
+
+ if (mTaskInfo.taskDescription != null) {
+ int backgroundColor = mTaskInfo.taskDescription.getBackgroundColor();
+ setResizeBackgroundColor(startTransaction, backgroundColor);
+ }
+
+ if (mListener != null) {
+ final int taskId = mTaskInfo.taskId;
+ final ComponentName baseActivity = mTaskInfo.baseActivity;
+
+ mListenerExecutor.execute(() -> {
+ if (newTask) {
+ mListener.onTaskCreated(taskId, baseActivity);
+ }
+ // Even if newTask, send a visibilityChange if the surface was destroyed.
+ if (!newTask || !mSurfaceCreated) {
+ mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated /* visible */);
+ }
+ });
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
index 8286d10..42844b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
@@ -31,13 +31,24 @@
private final ShellTaskOrganizer mTaskOrganizer;
private final ShellExecutor mShellExecutor;
private final SyncTransactionQueue mSyncQueue;
+ private final TaskViewTransitions mTaskViewTransitions;
private final TaskViewFactory mImpl = new TaskViewFactoryImpl();
public TaskViewFactoryController(ShellTaskOrganizer taskOrganizer,
+ ShellExecutor shellExecutor, SyncTransactionQueue syncQueue,
+ TaskViewTransitions taskViewTransitions) {
+ mTaskOrganizer = taskOrganizer;
+ mShellExecutor = shellExecutor;
+ mSyncQueue = syncQueue;
+ mTaskViewTransitions = taskViewTransitions;
+ }
+
+ public TaskViewFactoryController(ShellTaskOrganizer taskOrganizer,
ShellExecutor shellExecutor, SyncTransactionQueue syncQueue) {
mTaskOrganizer = taskOrganizer;
mShellExecutor = shellExecutor;
mSyncQueue = syncQueue;
+ mTaskViewTransitions = null;
}
public TaskViewFactory asTaskViewFactory() {
@@ -46,7 +57,7 @@
/** Creates an {@link TaskView} */
public void create(@UiContext Context context, Executor executor, Consumer<TaskView> onCreate) {
- TaskView taskView = new TaskView(context, mTaskOrganizer, mSyncQueue);
+ TaskView taskView = new TaskView(context, mTaskOrganizer, mTaskViewTransitions, mSyncQueue);
executor.execute(() -> {
onCreate.accept(taskView);
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java
new file mode 100644
index 0000000..83335ac
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java
@@ -0,0 +1,253 @@
+/*
+ * 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.
+ */
+
+package com.android.wm.shell;
+
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.os.IBinder;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.ArrayList;
+
+/**
+ * Handles Shell Transitions that involve TaskView tasks.
+ */
+public class TaskViewTransitions implements Transitions.TransitionHandler {
+ private static final String TAG = "TaskViewTransitions";
+
+ private final ArrayList<TaskView> mTaskViews = new ArrayList<>();
+ private final ArrayList<PendingTransition> mPending = new ArrayList<>();
+ private final Transitions mTransitions;
+ private final boolean[] mRegistered = new boolean[]{ false };
+
+ /**
+ * TaskView makes heavy use of startTransition. Only one shell-initiated transition can be
+ * in-flight (collecting) at a time (because otherwise, the operations could get merged into
+ * a single transition). So, keep a queue here until we add a queue in server-side.
+ */
+ private static class PendingTransition {
+ final @WindowManager.TransitionType int mType;
+ final WindowContainerTransaction mWct;
+ final @NonNull TaskView mTaskView;
+ IBinder mClaimed;
+
+ PendingTransition(@WindowManager.TransitionType int type,
+ @Nullable WindowContainerTransaction wct, @NonNull TaskView taskView) {
+ mType = type;
+ mWct = wct;
+ mTaskView = taskView;
+ }
+ }
+
+ public TaskViewTransitions(Transitions transitions) {
+ mTransitions = transitions;
+ // Defer registration until the first TaskView because we want this to be the "first" in
+ // priority when handling requests.
+ // TODO(210041388): register here once we have an explicit ordering mechanism.
+ }
+
+ void addTaskView(TaskView tv) {
+ synchronized (mRegistered) {
+ if (!mRegistered[0]) {
+ mRegistered[0] = true;
+ mTransitions.addHandler(this);
+ }
+ }
+ mTaskViews.add(tv);
+ }
+
+ void removeTaskView(TaskView tv) {
+ mTaskViews.remove(tv);
+ // Note: Don't unregister handler since this is a singleton with lifetime bound to Shell
+ }
+
+ /**
+ * Looks through the pending transitions for one matching `taskView`.
+ * @param taskView the pending transition should be for this.
+ * @param closing When true, this only returns a pending transition of the close/hide type.
+ * Otherwise it selects open/show.
+ * @param latest When true, this will only check the most-recent pending transition for the
+ * specified taskView. If it doesn't match `closing`, this will return null even
+ * if there is a match earlier. The idea behind this is to check the state of
+ * the taskviews "as if all transitions already happened".
+ */
+ private PendingTransition findPending(TaskView taskView, boolean closing, boolean latest) {
+ for (int i = mPending.size() - 1; i >= 0; --i) {
+ if (mPending.get(i).mTaskView != taskView) continue;
+ if (Transitions.isClosingType(mPending.get(i).mType) == closing) {
+ return mPending.get(i);
+ }
+ if (latest) {
+ return null;
+ }
+ }
+ return null;
+ }
+
+ private PendingTransition findPending(IBinder claimed) {
+ for (int i = 0; i < mPending.size(); ++i) {
+ if (mPending.get(i).mClaimed != claimed) continue;
+ return mPending.get(i);
+ }
+ return null;
+ }
+
+ /** @return whether there are pending transitions on TaskViews. */
+ public boolean hasPending() {
+ return !mPending.isEmpty();
+ }
+
+ @Override
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @Nullable TransitionRequestInfo request) {
+ final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
+ if (triggerTask == null) {
+ return null;
+ }
+ final TaskView taskView = findTaskView(triggerTask);
+ if (taskView == null) return null;
+ // Opening types should all be initiated by shell
+ if (!Transitions.isClosingType(request.getType())) return null;
+ PendingTransition pending = findPending(taskView, true /* closing */, false /* latest */);
+ if (pending == null) {
+ pending = new PendingTransition(request.getType(), null, taskView);
+ }
+ if (pending.mClaimed != null) {
+ throw new IllegalStateException("Task is closing in 2 collecting transitions?"
+ + " This state doesn't make sense");
+ }
+ pending.mClaimed = transition;
+ return new WindowContainerTransaction();
+ }
+
+ private TaskView findTaskView(ActivityManager.RunningTaskInfo taskInfo) {
+ for (int i = 0; i < mTaskViews.size(); ++i) {
+ if (mTaskViews.get(i).getTaskInfo() == null) continue;
+ if (taskInfo.token.equals(mTaskViews.get(i).getTaskInfo().token)) {
+ return mTaskViews.get(i);
+ }
+ }
+ return null;
+ }
+
+ void startTaskView(WindowContainerTransaction wct, TaskView taskView) {
+ mPending.add(new PendingTransition(TRANSIT_OPEN, wct, taskView));
+ startNextTransition();
+ }
+
+ void setTaskViewVisible(TaskView taskView, boolean visible) {
+ PendingTransition pending = findPending(taskView, !visible, true /* latest */);
+ if (pending != null) {
+ // Already opening or creating a task, so no need to do anything here.
+ return;
+ }
+ if (taskView.getTaskInfo() == null) {
+ // Nothing to update, task is not yet available
+ return;
+ }
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setHidden(taskView.getTaskInfo().token, !visible /* hidden */);
+ pending = new PendingTransition(
+ visible ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK, wct, taskView);
+ mPending.add(pending);
+ startNextTransition();
+ // visibility is reported in transition.
+ }
+
+ private void startNextTransition() {
+ if (mPending.isEmpty()) return;
+ final PendingTransition pending = mPending.get(0);
+ if (pending.mClaimed != null) {
+ // Wait for this to start animating.
+ return;
+ }
+ pending.mClaimed = mTransitions.startTransition(pending.mType, pending.mWct, this);
+ }
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ PendingTransition pending = findPending(transition);
+ if (pending == null) return false;
+ mPending.remove(pending);
+ TaskView taskView = pending.mTaskView;
+ final ArrayList<TransitionInfo.Change> tasks = new ArrayList<>();
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ final TransitionInfo.Change chg = info.getChanges().get(i);
+ if (chg.getTaskInfo() == null) continue;
+ tasks.add(chg);
+ }
+ if (tasks.isEmpty()) {
+ Slog.e(TAG, "Got a TaskView transition with no task.");
+ return false;
+ }
+ WindowContainerTransaction wct = null;
+ for (int i = 0; i < tasks.size(); ++i) {
+ TransitionInfo.Change chg = tasks.get(i);
+ if (Transitions.isClosingType(chg.getMode())) {
+ final boolean isHide = chg.getMode() == TRANSIT_TO_BACK;
+ TaskView tv = findTaskView(chg.getTaskInfo());
+ if (tv == null) {
+ throw new IllegalStateException("TaskView transition is closing a "
+ + "non-taskview task ");
+ }
+ if (isHide) {
+ tv.prepareHideAnimation(finishTransaction);
+ } else {
+ tv.prepareCloseAnimation();
+ }
+ } else if (Transitions.isOpeningType(chg.getMode())) {
+ final boolean taskIsNew = chg.getMode() == TRANSIT_OPEN;
+ if (wct == null) wct = new WindowContainerTransaction();
+ TaskView tv = taskView;
+ if (!taskIsNew) {
+ tv = findTaskView(chg.getTaskInfo());
+ if (tv == null) {
+ throw new IllegalStateException("TaskView transition is showing a "
+ + "non-taskview task ");
+ }
+ }
+ tv.prepareOpenAnimation(taskIsNew, startTransaction, finishTransaction,
+ chg.getTaskInfo(), chg.getLeash(), wct);
+ } else {
+ throw new IllegalStateException("Claimed transition isn't an opening or closing"
+ + " type: " + chg.getMode());
+ }
+ }
+ // No animation, just show it immediately.
+ startTransaction.apply();
+ finishTransaction.apply();
+ finishCallback.onTransitionFinished(wct, null /* wctCB */);
+ startNextTransition();
+ return true;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
index d92e2cc..686fbbf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
@@ -15,23 +15,22 @@
*/
package com.android.wm.shell.bubbles;
-import static android.graphics.Paint.ANTI_ALIAS_FLAG;
-import static android.graphics.Paint.DITHER_FLAG;
-import static android.graphics.Paint.FILTER_BITMAP_FLAG;
-
+import android.annotation.DrawableRes;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.PathParser;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewOutlineProvider;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.launcher3.icons.DotRenderer;
@@ -47,7 +46,7 @@
* Badge = the icon associated with the app that created this bubble, this will show work profile
* badge if appropriate.
*/
-public class BadgedImageView extends ImageView {
+public class BadgedImageView extends FrameLayout {
/** Same value as Launcher3 dot code */
public static final float WHITE_SCRIM_ALPHA = 0.54f;
@@ -74,6 +73,9 @@
private final EnumSet<SuppressionFlag> mDotSuppressionFlags =
EnumSet.of(SuppressionFlag.FLYOUT_VISIBLE);
+ private final ImageView mBubbleIcon;
+ private final ImageView mAppIcon;
+
private float mDotScale = 0f;
private float mAnimatingToDotScale = 0f;
private boolean mDotIsAnimating = false;
@@ -86,7 +88,6 @@
private DotRenderer.DrawParams mDrawParams;
private int mDotColor;
- private Paint mPaint = new Paint(ANTI_ALIAS_FLAG);
private Rect mTempBounds = new Rect();
public BadgedImageView(Context context) {
@@ -104,6 +105,17 @@
public BadgedImageView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+
+ mBubbleIcon = new ImageView(context);
+ addView(mBubbleIcon);
+ mAppIcon = new ImageView(context);
+ addView(mAppIcon);
+
+ final TypedArray ta = mContext.obtainStyledAttributes(attrs, new int[]{android.R.attr.src},
+ defStyleAttr, defStyleRes);
+ mBubbleIcon.setImageResource(ta.getResourceId(0, 0));
+ ta.recycle();
+
mDrawParams = new DotRenderer.DrawParams();
setFocusable(true);
@@ -135,7 +147,6 @@
public void showDotAndBadge(boolean onLeft) {
removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.BEHIND_STACK);
animateDotBadgePositions(onLeft);
-
}
public void hideDotAndBadge(boolean onLeft) {
@@ -149,6 +160,7 @@
*/
public void setRenderedBubble(BubbleViewProvider bubble) {
mBubble = bubble;
+ mBubbleIcon.setImageBitmap(bubble.getBubbleIcon());
if (mDotSuppressionFlags.contains(SuppressionFlag.BEHIND_STACK)) {
hideBadge();
} else {
@@ -176,6 +188,20 @@
mDotRenderer.draw(canvas, mDrawParams);
}
+ /**
+ * Set drawable resource shown as the icon
+ */
+ public void setIconImageResource(@DrawableRes int drawable) {
+ mBubbleIcon.setImageResource(drawable);
+ }
+
+ /**
+ * Get icon drawable
+ */
+ public Drawable getIconDrawable() {
+ return mBubbleIcon.getDrawable();
+ }
+
/** Adds a dot suppression flag, updating dot visibility if needed. */
void addDotSuppressionFlag(SuppressionFlag flag) {
if (mDotSuppressionFlags.add(flag)) {
@@ -279,7 +305,6 @@
showBadge();
}
-
/** Whether to draw the dot in onDraw(). */
private boolean shouldDrawDot() {
// Always render the dot if it's animating, since it could be animating out. Otherwise, show
@@ -325,29 +350,28 @@
void showBadge() {
Bitmap badge = mBubble.getAppBadge();
if (badge == null) {
- setImageBitmap(mBubble.getBubbleIcon());
+ mAppIcon.setVisibility(GONE);
return;
}
- Canvas bubbleCanvas = new Canvas();
- Bitmap noBadgeBubble = mBubble.getBubbleIcon();
- Bitmap bubble = noBadgeBubble.copy(noBadgeBubble.getConfig(), /* isMutable */ true);
- bubbleCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG));
- bubbleCanvas.setBitmap(bubble);
- final int bubbleSize = bubble.getWidth();
+ final int bubbleSize = mBubble.getBubbleIcon().getWidth();
final int badgeSize = (int) (ICON_BADGE_SCALE * bubbleSize);
- Rect dest = new Rect();
+
+ FrameLayout.LayoutParams appIconParams = (LayoutParams) mAppIcon.getLayoutParams();
+ appIconParams.height = badgeSize;
+ appIconParams.width = badgeSize;
if (mOnLeft) {
- dest.set(0, bubbleSize - badgeSize, badgeSize, bubbleSize);
+ appIconParams.gravity = Gravity.BOTTOM | Gravity.LEFT;
} else {
- dest.set(bubbleSize - badgeSize, bubbleSize - badgeSize, bubbleSize, bubbleSize);
+ appIconParams.gravity = Gravity.BOTTOM | Gravity.RIGHT;
}
- bubbleCanvas.drawBitmap(badge, null /* src */, dest, mPaint);
- bubbleCanvas.setBitmap(null);
- setImageBitmap(bubble);
+ mAppIcon.setLayoutParams(appIconParams);
+
+ mAppIcon.setImageBitmap(badge);
+ mAppIcon.setVisibility(VISIBLE);
}
void hideBadge() {
- setImageBitmap(mBubble.getBubbleIcon());
+ mAppIcon.setVisibility(GONE);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index ec59fad..f979f4d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -80,6 +80,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
@@ -136,6 +137,7 @@
private final TaskStackListenerImpl mTaskStackListener;
private final ShellTaskOrganizer mTaskOrganizer;
private final DisplayController mDisplayController;
+ private final TaskViewTransitions mTaskViewTransitions;
private final SyncTransactionQueue mSyncQueue;
// Used to post to main UI thread
@@ -212,6 +214,7 @@
DisplayController displayController,
ShellExecutor mainExecutor,
Handler mainHandler,
+ TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue) {
BubbleLogger logger = new BubbleLogger(uiEventLogger);
BubblePositioner positioner = new BubblePositioner(context, windowManager);
@@ -220,7 +223,7 @@
new BubbleDataRepository(context, launcherApps, mainExecutor),
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
logger, taskStackListener, organizer, positioner, displayController, mainExecutor,
- mainHandler, syncQueue);
+ mainHandler, taskViewTransitions, syncQueue);
}
/**
@@ -243,6 +246,7 @@
DisplayController displayController,
ShellExecutor mainExecutor,
Handler mainHandler,
+ TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue) {
mContext = context;
mLauncherApps = launcherApps;
@@ -266,6 +270,7 @@
mSavedBubbleKeysPerUser = new SparseSetArray<>();
mBubbleIconFactory = new BubbleIconFactory(context);
mDisplayController = displayController;
+ mTaskViewTransitions = taskViewTransitions;
mSyncQueue = syncQueue;
}
@@ -570,6 +575,10 @@
return mSyncQueue;
}
+ TaskViewTransitions getTaskViewTransitions() {
+ return mTaskViewTransitions;
+ }
+
/** Contains information to help position things on the screen. */
BubblePositioner getPositioner() {
return mBubblePositioner;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 519a856..cd635c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -328,6 +328,7 @@
if (prevBubble == null) {
// Create a new bubble
bubble.setSuppressFlyout(suppressFlyout);
+ bubble.markUpdatedAt(mTimeSource.currentTimeMillis());
doAdd(bubble);
trim();
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index a87aad4..af59062 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -335,7 +335,7 @@
mManageButton.setVisibility(GONE);
} else {
mTaskView = new TaskView(mContext, mController.getTaskOrganizer(),
- mController.getSyncTransactionQueue());
+ mController.getTaskViewTransitions(), mController.getSyncTransactionQueue());
mTaskView.setListener(mController.getMainExecutor(), mTaskViewListener);
mExpandedViewContainer.addView(mTaskView);
bringChildToFront(mTaskView);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index 5161092..a175929 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -66,7 +66,7 @@
updateResources()
getExpandedView()?.applyThemeAttrs()
// Apply inset and new style to fresh icon drawable.
- getIconView()?.setImageResource(R.drawable.bubble_ic_overflow_button)
+ getIconView()?.setIconImageResource(R.drawable.bubble_ic_overflow_button)
updateBtnTheme()
}
@@ -89,19 +89,19 @@
dotColor = colorAccent
val shapeColor = res.getColor(android.R.color.system_accent1_1000)
- overflowBtn?.drawable?.setTint(shapeColor)
+ overflowBtn?.iconDrawable?.setTint(shapeColor)
val iconFactory = BubbleIconFactory(context)
// Update bitmap
- val fg = InsetDrawable(overflowBtn?.drawable, overflowIconInset)
+ val fg = InsetDrawable(overflowBtn?.iconDrawable, overflowIconInset)
bitmap = iconFactory.createBadgedIconBitmap(
AdaptiveIconDrawable(ColorDrawable(colorAccent), fg)).icon
// Update dot path
dotPath = PathParser.createPathFromPathData(
res.getString(com.android.internal.R.string.config_icon_mask))
- val scale = iconFactory.normalizer.getScale(iconView!!.drawable,
+ val scale = iconFactory.normalizer.getScale(iconView!!.iconDrawable,
null /* outBounds */, null /* path */, null /* outMaskShape */)
val radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f
val matrix = Matrix()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
index eea6e3c..c4bd73b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.common;
-import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.view.SurfaceControl;
@@ -63,8 +62,6 @@
if (buffer == null || buffer.getHardwareBuffer() == null) {
return;
}
- final GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
- buffer.getHardwareBuffer());
mScreenshot = new SurfaceControl.Builder()
.setName("ScreenshotUtils screenshot")
.setFormat(PixelFormat.TRANSLUCENT)
@@ -73,7 +70,7 @@
.setBLASTLayer()
.build();
- mTransaction.setBuffer(mScreenshot, graphicBuffer);
+ mTransaction.setBuffer(mScreenshot, buffer.getHardwareBuffer());
mTransaction.setColorSpace(mScreenshot, buffer.getColorSpace());
mTransaction.reparent(mScreenshot, mSurfaceControl);
mTransaction.setLayer(mScreenshot, mLayer);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 040fffae..b8ac87f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -93,7 +93,7 @@
private final InsetsState mInsetsState = new InsetsState();
private Context mContext;
- private DividerSnapAlgorithm mDividerSnapAlgorithm;
+ @VisibleForTesting DividerSnapAlgorithm mDividerSnapAlgorithm;
private WindowContainerToken mWinToken1;
private WindowContainerToken mWinToken2;
private int mDividePosition;
@@ -294,20 +294,22 @@
mSplitLayoutHandler.onLayoutSizeChanging(this);
}
- void setDividePosition(int position) {
+ void setDividePosition(int position, boolean applyLayoutChange) {
mDividePosition = position;
updateBounds(mDividePosition);
- mSplitLayoutHandler.onLayoutSizeChanged(this);
+ if (applyLayoutChange) {
+ mSplitLayoutHandler.onLayoutSizeChanged(this);
+ }
}
- /** Sets divide position base on the ratio within root bounds. */
+ /** Updates divide position and split bounds base on the ratio within root bounds. */
public void setDivideRatio(float ratio) {
final int position = isLandscape()
? mRootBounds.left + (int) (mRootBounds.width() * ratio)
: mRootBounds.top + (int) (mRootBounds.height() * ratio);
- DividerSnapAlgorithm.SnapTarget snapTarget =
+ final DividerSnapAlgorithm.SnapTarget snapTarget =
mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(position);
- setDividePosition(snapTarget.position);
+ setDividePosition(snapTarget.position, false /* applyLayoutChange */);
}
/** Resets divider position. */
@@ -336,7 +338,7 @@
break;
default:
flingDividePosition(currentPosition, snapTarget.position,
- () -> setDividePosition(snapTarget.position));
+ () -> setDividePosition(snapTarget.position, true /* applyLayoutChange */));
break;
}
}
@@ -389,7 +391,7 @@
@Override
public void onAnimationCancel(Animator animation) {
- setDividePosition(to);
+ setDividePosition(to, true /* applyLayoutChange */);
}
});
animator.start();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
similarity index 86%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
index a703114..99dbfe0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.wm.shell.sizecompatui;
+package com.android.wm.shell.compatui;
import com.android.wm.shell.common.annotations.ExternalThread;
/**
- * Interface to engage size compat UI.
+ * Interface to engage compat UI.
*/
@ExternalThread
-public interface SizeCompatUI {
+public interface CompatUI {
/**
- * Called when the keyguard occluded state changes. Removes all size compat UIs if the
+ * Called when the keyguard occluded state changes. Removes all compat UIs if the
* keyguard is now occluded.
* @param occluded indicates if the keyguard is now occluded.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
similarity index 81%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index e06070a..e0b2387 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.sizecompatui;
+package com.android.wm.shell.compatui;
import android.annotation.Nullable;
import android.content.Context;
@@ -48,20 +48,20 @@
/**
* Controls to show/update restart-activity buttons on Tasks based on whether the foreground
- * activities are in size compatibility mode.
+ * activities are in compatibility mode.
*/
-public class SizeCompatUIController implements OnDisplaysChangedListener,
+public class CompatUIController implements OnDisplaysChangedListener,
DisplayImeController.ImePositionProcessor {
/** Callback for size compat UI interaction. */
- public interface SizeCompatUICallback {
+ public interface CompatUICallback {
/** Called when the size compat restart button appears. */
void onSizeCompatRestartButtonAppeared(int taskId);
/** Called when the size compat restart button is clicked. */
void onSizeCompatRestartButtonClicked(int taskId);
}
- private static final String TAG = "SizeCompatUIController";
+ private static final String TAG = "CompatUIController";
/** Whether the IME is shown on display id. */
private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1);
@@ -71,7 +71,7 @@
new SparseArray<>(0);
/** The showing UIs by task id. */
- private final SparseArray<SizeCompatUILayout> mActiveLayouts = new SparseArray<>(0);
+ private final SparseArray<CompatUIWindowManager> mActiveLayouts = new SparseArray<>(0);
/** Avoid creating display context frequently for non-default display. */
private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0);
@@ -82,17 +82,17 @@
private final DisplayImeController mImeController;
private final SyncTransactionQueue mSyncQueue;
private final ShellExecutor mMainExecutor;
- private final SizeCompatUIImpl mImpl = new SizeCompatUIImpl();
+ private final CompatUIImpl mImpl = new CompatUIImpl();
- private SizeCompatUICallback mCallback;
+ private CompatUICallback mCallback;
/** Only show once automatically in the process life. */
private boolean mHasShownHint;
- /** Indicates if the keyguard is currently occluded, in which case size compat UIs shouldn't
+ /** Indicates if the keyguard is currently occluded, in which case compat UIs shouldn't
* be shown. */
private boolean mKeyguardOccluded;
- public SizeCompatUIController(Context context,
+ public CompatUIController(Context context,
DisplayController displayController,
DisplayInsetsController displayInsetsController,
DisplayImeController imeController,
@@ -108,35 +108,36 @@
mImeController.addPositionProcessor(this);
}
- public SizeCompatUI asSizeCompatUI() {
+ /** Returns implementation of {@link CompatUI}. */
+ public CompatUI asCompatUI() {
return mImpl;
}
/** Sets the callback for UI interactions. */
- public void setSizeCompatUICallback(SizeCompatUICallback callback) {
+ public void setCompatUICallback(CompatUICallback callback) {
mCallback = callback;
}
/**
- * Called when the Task info changed. Creates and updates the size compat UI if there is an
+ * Called when the Task info changed. Creates and updates the compat UI if there is an
* activity in size compat, or removes the UI if there is no size compat activity.
*
* @param displayId display the task and activity are in.
* @param taskId task the activity is in.
- * @param taskConfig task config to place the size compat UI with.
+ * @param taskConfig task config to place the compat UI with.
* @param taskListener listener to handle the Task Surface placement.
*/
- public void onSizeCompatInfoChanged(int displayId, int taskId,
+ public void onCompatInfoChanged(int displayId, int taskId,
@Nullable Configuration taskConfig,
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
if (taskConfig == null || taskListener == null) {
- // Null token means the current foreground activity is not in size compatibility mode.
+ // Null token means the current foreground activity is not in compatibility mode.
removeLayout(taskId);
} else if (mActiveLayouts.contains(taskId)) {
// UI already exists, update the UI layout.
updateLayout(taskId, taskConfig, taskListener);
} else {
- // Create a new size compat UI.
+ // Create a new compat UI.
createLayout(displayId, taskId, taskConfig, taskListener);
}
}
@@ -151,7 +152,7 @@
mDisplayContextCache.remove(displayId);
removeOnInsetsChangedListener(displayId);
- // Remove all size compat UIs on the removed display.
+ // Remove all compat UIs on the removed display.
final List<Integer> toRemoveTaskIds = new ArrayList<>();
forAllLayoutsOnDisplay(displayId, layout -> toRemoveTaskIds.add(layout.getTaskId()));
for (int i = toRemoveTaskIds.size() - 1; i >= 0; i--) {
@@ -194,7 +195,7 @@
mDisplaysWithIme.remove(displayId);
}
- // Hide the size compat UIs when input method is showing.
+ // Hide the compat UIs when input method is showing.
forAllLayoutsOnDisplay(displayId,
layout -> layout.updateVisibility(showOnDisplay(displayId)));
}
@@ -202,7 +203,7 @@
@VisibleForTesting
void onKeyguardOccludedChanged(boolean occluded) {
mKeyguardOccluded = occluded;
- // Hide the size compat UIs when keyguard is occluded.
+ // Hide the compat UIs when keyguard is occluded.
forAllLayouts(layout -> layout.updateVisibility(showOnDisplay(layout.getDisplayId())));
}
@@ -222,34 +223,34 @@
return;
}
- final SizeCompatUILayout layout = createLayout(context, displayId, taskId, taskConfig,
- taskListener);
- mActiveLayouts.put(taskId, layout);
- layout.createSizeCompatButton(showOnDisplay(displayId));
+ final CompatUIWindowManager compatUIWindowManager =
+ createLayout(context, displayId, taskId, taskConfig, taskListener);
+ mActiveLayouts.put(taskId, compatUIWindowManager);
+ compatUIWindowManager.createLayout(showOnDisplay(displayId));
}
@VisibleForTesting
- SizeCompatUILayout createLayout(Context context, int displayId, int taskId,
+ CompatUIWindowManager createLayout(Context context, int displayId, int taskId,
Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
- final SizeCompatUILayout layout = new SizeCompatUILayout(mSyncQueue, mCallback, context,
- taskConfig, taskId, taskListener, mDisplayController.getDisplayLayout(displayId),
- mHasShownHint);
+ final CompatUIWindowManager compatUIWindowManager = new CompatUIWindowManager(context,
+ taskConfig, mSyncQueue, mCallback, taskId, taskListener,
+ mDisplayController.getDisplayLayout(displayId), mHasShownHint);
// Only show hint for the first time.
mHasShownHint = true;
- return layout;
+ return compatUIWindowManager;
}
private void updateLayout(int taskId, Configuration taskConfig,
ShellTaskOrganizer.TaskListener taskListener) {
- final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
+ final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
if (layout == null) {
return;
}
- layout.updateSizeCompatInfo(taskConfig, taskListener, showOnDisplay(layout.getDisplayId()));
+ layout.updateCompatInfo(taskConfig, taskListener, showOnDisplay(layout.getDisplayId()));
}
private void removeLayout(int taskId) {
- final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
+ final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
if (layout != null) {
layout.release();
mActiveLayouts.remove(taskId);
@@ -275,19 +276,19 @@
return context;
}
- private void forAllLayoutsOnDisplay(int displayId, Consumer<SizeCompatUILayout> callback) {
+ private void forAllLayoutsOnDisplay(int displayId, Consumer<CompatUIWindowManager> callback) {
forAllLayouts(layout -> layout.getDisplayId() == displayId, callback);
}
- private void forAllLayouts(Consumer<SizeCompatUILayout> callback) {
+ private void forAllLayouts(Consumer<CompatUIWindowManager> callback) {
forAllLayouts(layout -> true, callback);
}
- private void forAllLayouts(Predicate<SizeCompatUILayout> condition,
- Consumer<SizeCompatUILayout> callback) {
+ private void forAllLayouts(Predicate<CompatUIWindowManager> condition,
+ Consumer<CompatUIWindowManager> callback) {
for (int i = 0; i < mActiveLayouts.size(); i++) {
final int taskId = mActiveLayouts.keyAt(i);
- final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
+ final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
if (layout != null && condition.test(layout)) {
callback.accept(layout);
}
@@ -298,11 +299,11 @@
* The interface for calls from outside the Shell, within the host process.
*/
@ExternalThread
- private class SizeCompatUIImpl implements SizeCompatUI {
+ private class CompatUIImpl implements CompatUI {
@Override
public void onKeyguardOccludedChanged(boolean occluded) {
mMainExecutor.execute(() -> {
- SizeCompatUIController.this.onKeyguardOccludedChanged(occluded);
+ CompatUIController.this.onKeyguardOccludedChanged(occluded);
});
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
new file mode 100644
index 0000000..ea4f209
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+package com.android.wm.shell.compatui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.wm.shell.R;
+
+/**
+ * Container for compat UI controls.
+ */
+public class CompatUILayout extends LinearLayout {
+
+ private CompatUIWindowManager mWindowManager;
+
+ public CompatUILayout(Context context) {
+ this(context, null);
+ }
+
+ public CompatUILayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CompatUILayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public CompatUILayout(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ void inject(CompatUIWindowManager windowManager) {
+ mWindowManager = windowManager;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ // Need to relayout after changes like hiding / showing a hint since they affect size.
+ // Doing this directly in setSizeCompatHintVisibility can result in flaky animation.
+ mWindowManager.relayout();
+ }
+
+ void setSizeCompatHintVisibility(boolean show) {
+ final LinearLayout sizeCompatHint = findViewById(R.id.size_compat_hint);
+ int visibility = show ? View.VISIBLE : View.GONE;
+ if (sizeCompatHint.getVisibility() == visibility) {
+ return;
+ }
+ sizeCompatHint.setVisibility(visibility);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ final ImageButton restartButton = findViewById(R.id.size_compat_restart_button);
+ restartButton.setOnClickListener(view -> mWindowManager.onRestartButtonClicked());
+ restartButton.setOnLongClickListener(view -> {
+ mWindowManager.onRestartButtonLongClicked();
+ return true;
+ });
+
+ final LinearLayout sizeCompatHint = findViewById(R.id.size_compat_hint);
+ ((TextView) sizeCompatHint.findViewById(R.id.compat_mode_hint_text))
+ .setText(R.string.restart_button_description);
+ sizeCompatHint.setOnClickListener(view -> setSizeCompatHintVisibility(/* show= */ false));
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
new file mode 100644
index 0000000..997ad04
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -0,0 +1,321 @@
+/*
+ * 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.
+ */
+
+package com.android.wm.shell.compatui;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.util.Log;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+/**
+ * Holds view hierarchy of a root surface and helps to inflate and manage layout for compat
+ * controls.
+ */
+class CompatUIWindowManager extends WindowlessWindowManager {
+
+ private static final String TAG = "CompatUIWindowManager";
+
+ private final SyncTransactionQueue mSyncQueue;
+ private final CompatUIController.CompatUICallback mCallback;
+ private final int mDisplayId;
+ private final int mTaskId;
+ private final Rect mStableBounds;
+
+ private Context mContext;
+ private Configuration mTaskConfig;
+ private ShellTaskOrganizer.TaskListener mTaskListener;
+ private DisplayLayout mDisplayLayout;
+
+ @VisibleForTesting
+ boolean mShouldShowHint;
+
+ @Nullable
+ @VisibleForTesting
+ CompatUILayout mCompatUILayout;
+
+ @Nullable
+ private SurfaceControlViewHost mViewHost;
+ @Nullable
+ private SurfaceControl mLeash;
+
+ CompatUIWindowManager(Context context, Configuration taskConfig,
+ SyncTransactionQueue syncQueue, CompatUIController.CompatUICallback callback,
+ int taskId, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
+ boolean hasShownHint) {
+ super(taskConfig, null /* rootSurface */, null /* hostInputToken */);
+ mContext = context;
+ mSyncQueue = syncQueue;
+ mCallback = callback;
+ mTaskConfig = taskConfig;
+ mDisplayId = mContext.getDisplayId();
+ mTaskId = taskId;
+ mTaskListener = taskListener;
+ mDisplayLayout = displayLayout;
+ mShouldShowHint = !hasShownHint;
+ mStableBounds = new Rect();
+ mDisplayLayout.getStableBounds(mStableBounds);
+ }
+
+ @Override
+ public void setConfiguration(Configuration configuration) {
+ super.setConfiguration(configuration);
+ mContext = mContext.createConfigurationContext(configuration);
+ }
+
+ @Override
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+ // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ .setContainerLayer()
+ .setName("CompatUILeash")
+ .setHidden(false)
+ .setCallsite("CompatUIWindowManager#attachToParentSurface");
+ attachToParentSurface(builder);
+ mLeash = builder.build();
+ b.setParent(mLeash);
+ }
+
+ /** Creates the layout for compat controls. */
+ void createLayout(boolean show) {
+ if (!show || mCompatUILayout != null) {
+ // Wait until compat controls should be visible.
+ return;
+ }
+
+ initCompatUi();
+ updateSurfacePosition();
+
+ mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
+ }
+
+ /** Called when compat info changed. */
+ void updateCompatInfo(Configuration taskConfig,
+ ShellTaskOrganizer.TaskListener taskListener, boolean show) {
+ final Configuration prevTaskConfig = mTaskConfig;
+ final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
+ mTaskConfig = taskConfig;
+ mTaskListener = taskListener;
+
+ // Update configuration.
+ mContext = mContext.createConfigurationContext(taskConfig);
+ setConfiguration(taskConfig);
+
+ if (mCompatUILayout == null || prevTaskListener != taskListener) {
+ // TaskListener changed, recreate the layout for new surface parent.
+ release();
+ createLayout(show);
+ return;
+ }
+
+ if (!taskConfig.windowConfiguration.getBounds()
+ .equals(prevTaskConfig.windowConfiguration.getBounds())) {
+ // Reposition the UI surfaces.
+ updateSurfacePosition();
+ }
+
+ if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
+ // Update layout for RTL.
+ mCompatUILayout.setLayoutDirection(taskConfig.getLayoutDirection());
+ updateSurfacePosition();
+ }
+ }
+
+ /** Called when the visibility of the UI should change. */
+ void updateVisibility(boolean show) {
+ if (mCompatUILayout == null) {
+ // Layout may not have been created because it was hidden previously.
+ createLayout(show);
+ return;
+ }
+
+ // Hide compat UIs when IME is showing.
+ final int newVisibility = show ? View.VISIBLE : View.GONE;
+ if (mCompatUILayout.getVisibility() != newVisibility) {
+ mCompatUILayout.setVisibility(newVisibility);
+ }
+ }
+
+ /** Called when display layout changed. */
+ void updateDisplayLayout(DisplayLayout displayLayout) {
+ final Rect prevStableBounds = mStableBounds;
+ final Rect curStableBounds = new Rect();
+ displayLayout.getStableBounds(curStableBounds);
+ mDisplayLayout = displayLayout;
+ if (!prevStableBounds.equals(curStableBounds)) {
+ // Stable bounds changed, update UI surface positions.
+ updateSurfacePosition();
+ mStableBounds.set(curStableBounds);
+ }
+ }
+
+ /** Called when it is ready to be placed compat UI surface. */
+ void attachToParentSurface(SurfaceControl.Builder b) {
+ mTaskListener.attachChildSurfaceToTask(mTaskId, b);
+ }
+
+ /** Called when the restart button is clicked. */
+ void onRestartButtonClicked() {
+ mCallback.onSizeCompatRestartButtonClicked(mTaskId);
+ }
+
+ /** Called when the restart button is long clicked. */
+ void onRestartButtonLongClicked() {
+ if (mCompatUILayout == null) {
+ return;
+ }
+ mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true);
+ }
+
+ int getDisplayId() {
+ return mDisplayId;
+ }
+
+ int getTaskId() {
+ return mTaskId;
+ }
+
+ /** Releases the surface control and tears down the view hierarchy. */
+ void release() {
+ mCompatUILayout = null;
+
+ if (mViewHost != null) {
+ mViewHost.release();
+ mViewHost = null;
+ }
+
+ if (mLeash != null) {
+ final SurfaceControl leash = mLeash;
+ mSyncQueue.runInSync(t -> t.remove(leash));
+ mLeash = null;
+ }
+ }
+
+ void relayout() {
+ mViewHost.relayout(getWindowLayoutParams());
+ updateSurfacePosition();
+ }
+
+ @VisibleForTesting
+ void updateSurfacePosition() {
+ if (mCompatUILayout == null || mLeash == null) {
+ return;
+ }
+
+ // Use stable bounds to prevent controls from overlapping with system bars.
+ final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
+ final Rect stableBounds = new Rect();
+ mDisplayLayout.getStableBounds(stableBounds);
+ stableBounds.intersect(taskBounds);
+
+ // Position of the button in the container coordinate.
+ final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+ ? stableBounds.left - taskBounds.left
+ : stableBounds.right - taskBounds.left - mCompatUILayout.getMeasuredWidth();
+ final int positionY = stableBounds.bottom - taskBounds.top
+ - mCompatUILayout.getMeasuredHeight();
+
+ updateSurfacePosition(positionX, positionY);
+ }
+
+ private int getLayoutDirection() {
+ return mContext.getResources().getConfiguration().getLayoutDirection();
+ }
+
+ private void updateSurfacePosition(int positionX, int positionY) {
+ mSyncQueue.runInSync(t -> {
+ if (mLeash == null || !mLeash.isValid()) {
+ Log.w(TAG, "The leash has been released.");
+ return;
+ }
+ t.setPosition(mLeash, positionX, positionY);
+ // The compat UI should be the topmost child of the Task in case there can be more
+ // than one children.
+ t.setLayer(mLeash, Integer.MAX_VALUE);
+ });
+ }
+
+ /** Inflates {@link CompatUILayout} on to the root surface. */
+ private void initCompatUi() {
+ if (mViewHost != null) {
+ throw new IllegalStateException(
+ "A UI has already been created with this window manager.");
+ }
+
+ // Construction extracted into the separate methods to allow injection for tests.
+ mViewHost = createSurfaceViewHost();
+ mCompatUILayout = inflateCompatUILayout();
+ mCompatUILayout.inject(this);
+
+ mCompatUILayout.setSizeCompatHintVisibility(mShouldShowHint);
+
+ mViewHost.setView(mCompatUILayout, getWindowLayoutParams());
+
+ // Only show by default for the first time.
+ mShouldShowHint = false;
+ }
+
+ @VisibleForTesting
+ CompatUILayout inflateCompatUILayout() {
+ return (CompatUILayout) LayoutInflater.from(mContext)
+ .inflate(R.layout.compat_ui_layout, null);
+ }
+
+ @VisibleForTesting
+ SurfaceControlViewHost createSurfaceViewHost() {
+ return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+ }
+
+ /** Gets the layout params. */
+ private WindowManager.LayoutParams getWindowLayoutParams() {
+ // Measure how big the hint is since its size depends on the text size.
+ mCompatUILayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
+ // Cannot be wrap_content as this determines the actual window size
+ mCompatUILayout.getMeasuredWidth(), mCompatUILayout.getMeasuredHeight(),
+ TYPE_APPLICATION_OVERLAY,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
+ PixelFormat.TRANSLUCENT);
+ winParams.token = new Binder();
+ winParams.setTitle(CompatUILayout.class.getSimpleName() + mTaskId);
+ winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ return winParams;
+ }
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 54ce6bb..6d158d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -36,6 +36,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.TaskViewFactoryController;
+import com.android.wm.shell.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.apppairs.AppPairsController;
@@ -54,6 +55,8 @@
import com.android.wm.shell.common.annotations.ShellAnimationThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
+import com.android.wm.shell.compatui.CompatUI;
+import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
import com.android.wm.shell.draganddrop.DragAndDrop;
@@ -75,8 +78,6 @@
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.recents.RecentTasksController;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
-import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.startingsurface.StartingSurface;
@@ -173,25 +174,25 @@
@Provides
static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor,
Context context,
- SizeCompatUIController sizeCompatUI,
+ CompatUIController compatUI,
Optional<RecentTasksController> recentTasksOptional
) {
- return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI, recentTasksOptional);
+ return new ShellTaskOrganizer(mainExecutor, context, compatUI, recentTasksOptional);
}
@WMSingleton
@Provides
- static SizeCompatUI provideSizeCompatUI(SizeCompatUIController sizeCompatUIController) {
- return sizeCompatUIController.asSizeCompatUI();
+ static CompatUI provideCompatUI(CompatUIController compatUIController) {
+ return compatUIController.asCompatUI();
}
@WMSingleton
@Provides
- static SizeCompatUIController provideSizeCompatUIController(Context context,
+ static CompatUIController provideCompatUIController(Context context,
DisplayController displayController, DisplayInsetsController displayInsetsController,
DisplayImeController imeController, SyncTransactionQueue syncQueue,
@ShellMainThread ShellExecutor mainExecutor) {
- return new SizeCompatUIController(context, displayController, displayInsetsController,
+ return new CompatUIController(context, displayController, displayInsetsController,
imeController, syncQueue, mainExecutor);
}
@@ -463,6 +464,12 @@
animExecutor);
}
+ @WMSingleton
+ @Provides
+ static TaskViewTransitions provideTaskViewTransitions(Transitions transitions) {
+ return new TaskViewTransitions(transitions);
+ }
+
//
// Display areas
//
@@ -594,8 +601,10 @@
static TaskViewFactoryController provideTaskViewFactoryController(
ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor,
- SyncTransactionQueue syncQueue) {
- return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor, syncQueue);
+ SyncTransactionQueue syncQueue,
+ TaskViewTransitions taskViewTransitions) {
+ return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor, syncQueue,
+ taskViewTransitions);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index f562fd9c..dff5635 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -27,6 +27,7 @@
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.bubbles.BubbleController;
@@ -108,11 +109,13 @@
DisplayController displayController,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler,
+ TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue) {
return BubbleController.create(context, null /* synchronizer */,
floatingContentCoordinator, statusBarService, windowManager,
windowManagerShellWrapper, launcherApps, taskStackListener,
- uiEventLogger, organizer, displayController, mainExecutor, mainHandler, syncQueue);
+ uiEventLogger, organizer, displayController, mainExecutor, mainHandler,
+ taskViewTransitions, syncQueue);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 854fc60e..f0b2716 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -778,8 +778,8 @@
}
@Override
- public boolean supportSizeCompatUI() {
- // PIP doesn't support size compat.
+ public boolean supportCompatUI() {
+ // PIP doesn't support compat.
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index eb512af..101a55d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -191,6 +191,7 @@
mSystemWindows.addView(mPipMenuView,
getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
0, SHELL_ROOT_LAYER_PIP);
+ setShellRootAccessibilityWindow();
}
private void detachPipMenuView() {
@@ -546,6 +547,10 @@
mListeners.forEach(l -> l.onPipMenuStateChangeFinish(menuState));
}
mMenuState = menuState;
+ setShellRootAccessibilityWindow();
+ }
+
+ private void setShellRootAccessibilityWindow() {
switch (mMenuState) {
case MENU_STATE_NONE:
mSystemWindows.setShellRootAccessibilityWindow(0, SHELL_ROOT_LAYER_PIP, null);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index 8467cc5..92a3598 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -122,7 +122,7 @@
if (mTargetViewContainer != null) {
// init can be called multiple times, remove the old one from view hierarchy first.
- mWindowManager.removeViewImmediate(mTargetViewContainer);
+ cleanUpDismissTarget();
}
mTargetView = new DismissCircleView(mContext);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 83390a5..b165706 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -253,9 +253,6 @@
final Rect newBounds;
switch (mState) {
case STATE_PIP_MENU:
- newBounds = mPipBoundsState.getExpandedBounds();
- break;
-
case STATE_PIP:
// Let PipBoundsAlgorithm figure out what the correct bounds are at the moment.
// Internally, it will get the "default" bounds from PipBoundsState and adjust them
@@ -336,11 +333,6 @@
private void loadConfigurations() {
final Resources res = mContext.getResources();
mResizeAnimationDuration = res.getInteger(R.integer.config_pipResizeAnimationDuration);
- // "Cache" bounds for the Pip menu as "expanded" bounds in PipBoundsState. We'll refer back
- // to this value in resizePinnedStack(), when we are adjusting Pip task/window position for
- // the menu.
- mPipBoundsState.setExpandedBounds(
- Rect.unflattenFromString(res.getString(R.string.pip_menu_bounds)));
}
private DisplayInfo getDisplayInfo() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
index 6f7cd82..bda685e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.pip.tv;
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
@@ -26,7 +24,6 @@
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
-import android.widget.TextView;
import com.android.wm.shell.R;
@@ -36,12 +33,7 @@
*/
public class TvPipMenuActionButton extends RelativeLayout implements View.OnClickListener {
private final ImageView mIconImageView;
- private final ImageView mButtonImageView;
- private final TextView mDescriptionTextView;
- private Animator mTextFocusGainAnimator;
- private Animator mButtonFocusGainAnimator;
- private Animator mTextFocusLossAnimator;
- private Animator mButtonFocusLossAnimator;
+ private final View mButtonView;
private OnClickListener mOnClickListener;
public TvPipMenuActionButton(Context context) {
@@ -64,8 +56,7 @@
inflater.inflate(R.layout.tv_pip_menu_action_button, this);
mIconImageView = findViewById(R.id.icon);
- mButtonImageView = findViewById(R.id.button);
- mDescriptionTextView = findViewById(R.id.desc);
+ mButtonView = findViewById(R.id.button);
final int[] values = new int[]{android.R.attr.src, android.R.attr.text};
final TypedArray typedArray = context.obtainStyledAttributes(attrs, values, defStyleAttr,
@@ -76,43 +67,16 @@
if (textResId != 0) {
setTextAndDescription(getContext().getString(textResId));
}
-
typedArray.recycle();
}
@Override
- public void onFinishInflate() {
- super.onFinishInflate();
- mButtonImageView.setOnFocusChangeListener((v, hasFocus) -> {
- if (hasFocus) {
- startFocusGainAnimation();
- } else {
- startFocusLossAnimation();
- }
- });
-
- mTextFocusGainAnimator = AnimatorInflater.loadAnimator(getContext(),
- R.anim.tv_pip_controls_focus_gain_animation);
- mTextFocusGainAnimator.setTarget(mDescriptionTextView);
- mButtonFocusGainAnimator = AnimatorInflater.loadAnimator(getContext(),
- R.anim.tv_pip_controls_focus_gain_animation);
- mButtonFocusGainAnimator.setTarget(mButtonImageView);
-
- mTextFocusLossAnimator = AnimatorInflater.loadAnimator(getContext(),
- R.anim.tv_pip_controls_focus_loss_animation);
- mTextFocusLossAnimator.setTarget(mDescriptionTextView);
- mButtonFocusLossAnimator = AnimatorInflater.loadAnimator(getContext(),
- R.anim.tv_pip_controls_focus_loss_animation);
- mButtonFocusLossAnimator.setTarget(mButtonImageView);
- }
-
- @Override
public void setOnClickListener(OnClickListener listener) {
// We do not want to set an OnClickListener to the TvPipMenuActionButton itself, but only to
// the ImageView. So let's "cash" the listener we've been passed here and set a "proxy"
// listener to the ImageView.
mOnClickListener = listener;
- mButtonImageView.setOnClickListener(listener != null ? this : null);
+ mButtonView.setOnClickListener(listener != null ? this : null);
}
@Override
@@ -143,55 +107,16 @@
* Sets the text for description the with the given string.
*/
public void setTextAndDescription(CharSequence text) {
- mButtonImageView.setContentDescription(text);
- mDescriptionTextView.setText(text);
+ mButtonView.setContentDescription(text);
}
- private static void cancelAnimator(Animator animator) {
- if (animator.isStarted()) {
- animator.cancel();
- }
+ @Override
+ public void setEnabled(boolean enabled) {
+ mButtonView.setEnabled(enabled);
}
- /**
- * Starts the focus gain animation.
- */
- public void startFocusGainAnimation() {
- cancelAnimator(mButtonFocusLossAnimator);
- cancelAnimator(mTextFocusLossAnimator);
- mTextFocusGainAnimator.start();
- if (mButtonImageView.getAlpha() < 1f) {
- // If we had faded out the ripple drawable, run our manual focus change animation.
- // See the comment at {@link #startFocusLossAnimation()} for the reason of manual
- // animator.
- mButtonFocusGainAnimator.start();
- }
- }
-
- /**
- * Starts the focus loss animation.
- */
- public void startFocusLossAnimation() {
- cancelAnimator(mButtonFocusGainAnimator);
- cancelAnimator(mTextFocusGainAnimator);
- mTextFocusLossAnimator.start();
- if (mButtonImageView.hasFocus()) {
- // Button uses ripple that has the default animation for the focus changes.
- // However, it doesn't expose the API to fade out while it is focused, so we should
- // manually run the fade out animation when PIP controls row loses focus.
- mButtonFocusLossAnimator.start();
- }
- }
-
- /**
- * Resets to initial state.
- */
- public void reset() {
- cancelAnimator(mButtonFocusGainAnimator);
- cancelAnimator(mTextFocusGainAnimator);
- cancelAnimator(mButtonFocusLossAnimator);
- cancelAnimator(mTextFocusLossAnimator);
- mButtonImageView.setAlpha(1f);
- mDescriptionTextView.setAlpha(mButtonImageView.hasFocus() ? 1f : 0f);
+ @Override
+ public boolean isEnabled() {
+ return mButtonView.isEnabled();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index ee41b41..77bfa07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -24,6 +24,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ParceledListSlice;
+import android.graphics.Rect;
import android.os.Handler;
import android.util.Log;
import android.view.SurfaceControl;
@@ -122,19 +123,19 @@
if (DEBUG) Log.d(TAG, "showMenu()");
if (mMenuView != null) {
- mSystemWindows.updateViewLayout(mMenuView, getPipMenuLayoutParams(MENU_WINDOW_TITLE,
- mPipBoundsState.getDisplayBounds().width(),
- mPipBoundsState.getDisplayBounds().height()));
+ Rect pipBounds = mPipBoundsState.getBounds();
+ mSystemWindows.updateViewLayout(mMenuView, getPipMenuLayoutParams(
+ MENU_WINDOW_TITLE, pipBounds.width(), pipBounds.height()));
maybeUpdateMenuViewActions();
- mMenuView.show();
- // By default, SystemWindows views are above everything else.
- // Set the relative z-order so the menu is below PiP.
- if (mMenuView.getWindowSurfaceControl() != null && mLeash != null) {
+ SurfaceControl menuSurfaceControl = mSystemWindows.getViewSurface(mMenuView);
+ if (menuSurfaceControl != null) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.setRelativeLayer(mMenuView.getWindowSurfaceControl(), mLeash, -1);
+ t.setRelativeLayer(mMenuView.getWindowSurfaceControl(), mLeash, 1);
+ t.setPosition(menuSurfaceControl, pipBounds.left, pipBounds.top);
t.apply();
}
+ mMenuView.show();
}
}
@@ -181,7 +182,15 @@
private void onMediaActionsChanged(List<RemoteAction> actions) {
if (DEBUG) Log.d(TAG, "onMediaActionsChanged()");
- updateAdditionalActionsList(mMediaActions, actions);
+
+ // Hide disabled actions.
+ List<RemoteAction> enabledActions = new ArrayList<>();
+ for (RemoteAction remoteAction : actions) {
+ if (remoteAction.isEnabled()) {
+ enabledActions.add(remoteAction);
+ }
+ }
+ updateAdditionalActionsList(mMediaActions, enabledActions);
}
private void updateAdditionalActionsList(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index d6cd9ea..4327f15 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -24,9 +24,7 @@
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.content.Context;
-import android.graphics.Color;
import android.os.Handler;
-import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
@@ -55,13 +53,13 @@
private static final String TAG = "TvPipMenuView";
private static final boolean DEBUG = TvPipController.DEBUG;
- private static final float DISABLED_ACTION_ALPHA = 0.54f;
-
private final Animator mFadeInAnimation;
private final Animator mFadeOutAnimation;
- @Nullable private Listener mListener;
+ @Nullable
+ private Listener mListener;
private final LinearLayout mActionButtonsContainer;
+ private final View mMenuFrameView;
private final List<TvPipMenuActionButton> mAdditionalButtons = new ArrayList<>();
public TvPipMenuView(@NonNull Context context) {
@@ -88,11 +86,12 @@
mActionButtonsContainer.findViewById(R.id.tv_pip_menu_close_button)
.setOnClickListener(this);
+ mMenuFrameView = findViewById(R.id.tv_pip_menu_frame);
mFadeInAnimation = loadAnimator(mContext, R.anim.tv_pip_menu_fade_in_animation);
- mFadeInAnimation.setTarget(mActionButtonsContainer);
+ mFadeInAnimation.setTarget(mMenuFrameView);
mFadeOutAnimation = loadAnimator(mContext, R.anim.tv_pip_menu_fade_out_animation);
- mFadeOutAnimation.setTarget(mActionButtonsContainer);
+ mFadeOutAnimation.setTarget(mMenuFrameView);
}
void setListener(@Nullable Listener listener) {
@@ -103,7 +102,6 @@
if (DEBUG) Log.d(TAG, "show()");
mFadeInAnimation.start();
- setAlpha(1.0f);
grantWindowFocus(true);
}
@@ -111,12 +109,11 @@
if (DEBUG) Log.d(TAG, "hide()");
mFadeOutAnimation.start();
- setAlpha(0.0f);
grantWindowFocus(false);
}
boolean isVisible() {
- return getAlpha() == 1.0f;
+ return mMenuFrameView != null && mMenuFrameView.getAlpha() != 0.0f;
}
private void grantWindowFocus(boolean grantFocus) {
@@ -140,9 +137,7 @@
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
// Add buttons until we have enough to display all of the actions.
while (actionsNumber > buttonsNumber) {
- final TvPipMenuActionButton button = (TvPipMenuActionButton) layoutInflater.inflate(
- R.layout.tv_pip_menu_additional_action_button, mActionButtonsContainer,
- false);
+ TvPipMenuActionButton button = new TvPipMenuActionButton(mContext);
button.setOnClickListener(this);
mActionButtonsContainer.addView(button);
@@ -168,13 +163,8 @@
button.setVisibility(View.VISIBLE); // Ensure the button is visible.
button.setTextAndDescription(action.getContentDescription());
button.setEnabled(action.isEnabled());
- button.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
button.setTag(action);
-
- action.getIcon().loadDrawableAsync(mContext, drawable -> {
- drawable.setTint(Color.WHITE);
- button.setImageDrawable(drawable);
- }, mainHandler);
+ action.getIcon().loadDrawableAsync(mContext, button::setImageDrawable, mainHandler);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java
deleted file mode 100644
index ff6f913..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java
+++ /dev/null
@@ -1,66 +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.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-
-import androidx.annotation.Nullable;
-
-import com.android.wm.shell.R;
-
-/** Popup to show the hint about the {@link SizeCompatRestartButton}. */
-public class SizeCompatHintPopup extends FrameLayout implements View.OnClickListener {
-
- private SizeCompatUILayout mLayout;
-
- public SizeCompatHintPopup(Context context) {
- super(context);
- }
-
- public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- }
-
- public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public SizeCompatHintPopup(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- void inject(SizeCompatUILayout layout) {
- mLayout = layout;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- final LinearLayout hintPopup = findViewById(R.id.size_compat_hint_popup);
- hintPopup.setOnClickListener(this);
- }
-
- @Override
- public void onClick(View v) {
- mLayout.dismissHint();
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
deleted file mode 100644
index d75fe517..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
+++ /dev/null
@@ -1,76 +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.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageButton;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.wm.shell.R;
-
-/** Button to restart the size compat activity. */
-public class SizeCompatRestartButton extends FrameLayout implements View.OnClickListener,
- View.OnLongClickListener {
-
- private SizeCompatUILayout mLayout;
-
- public SizeCompatRestartButton(@NonNull Context context) {
- super(context);
- }
-
- public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- }
-
- public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs,
- int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs,
- int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- void inject(SizeCompatUILayout layout) {
- mLayout = layout;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- final ImageButton restartButton = findViewById(R.id.size_compat_restart_button);
- restartButton.setOnClickListener(this);
- restartButton.setOnLongClickListener(this);
- }
-
- @Override
- public void onClick(View v) {
- mLayout.onRestartButtonClicked();
- }
-
- @Override
- public boolean onLongClick(View v) {
- mLayout.onRestartButtonLongClicked();
- return true;
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
deleted file mode 100644
index c35b89a..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
+++ /dev/null
@@ -1,344 +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.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.util.Log;
-import android.view.SurfaceControl;
-import android.view.View;
-import android.view.WindowManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-/**
- * Records and handles layout of size compat UI on a task with size compat activity. Helps to
- * calculate proper bounds when configuration or UI position changes.
- */
-class SizeCompatUILayout {
- private static final String TAG = "SizeCompatUILayout";
-
- final SyncTransactionQueue mSyncQueue;
- private final SizeCompatUIController.SizeCompatUICallback mCallback;
- private Context mContext;
- private Configuration mTaskConfig;
- private final int mDisplayId;
- private final int mTaskId;
- private ShellTaskOrganizer.TaskListener mTaskListener;
- private DisplayLayout mDisplayLayout;
- private final Rect mStableBounds;
- private final int mButtonWidth;
- private final int mButtonHeight;
- private final int mPopupOffsetX;
- private final int mPopupOffsetY;
-
- @VisibleForTesting
- final SizeCompatUIWindowManager mButtonWindowManager;
- @VisibleForTesting
- @Nullable
- SizeCompatUIWindowManager mHintWindowManager;
- @VisibleForTesting
- @Nullable
- SizeCompatRestartButton mButton;
- @VisibleForTesting
- @Nullable
- SizeCompatHintPopup mHint;
- @VisibleForTesting
- boolean mShouldShowHint;
-
- SizeCompatUILayout(SyncTransactionQueue syncQueue,
- SizeCompatUIController.SizeCompatUICallback callback, Context context,
- Configuration taskConfig, int taskId, ShellTaskOrganizer.TaskListener taskListener,
- DisplayLayout displayLayout, boolean hasShownHint) {
- mSyncQueue = syncQueue;
- mCallback = callback;
- mContext = context.createConfigurationContext(taskConfig);
- mTaskConfig = taskConfig;
- mDisplayId = mContext.getDisplayId();
- mTaskId = taskId;
- mTaskListener = taskListener;
- mDisplayLayout = displayLayout;
- mShouldShowHint = !hasShownHint;
- mButtonWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this);
-
- mStableBounds = new Rect();
- mDisplayLayout.getStableBounds(mStableBounds);
-
- final Resources resources = mContext.getResources();
- mButtonWidth = resources.getDimensionPixelSize(R.dimen.size_compat_button_width);
- mButtonHeight = resources.getDimensionPixelSize(R.dimen.size_compat_button_height);
- mPopupOffsetX = (mButtonWidth / 2) - resources.getDimensionPixelSize(
- R.dimen.size_compat_hint_corner_radius) - (resources.getDimensionPixelSize(
- R.dimen.size_compat_hint_point_width) / 2);
- mPopupOffsetY = mButtonHeight;
- }
-
- /** Creates the activity restart button window. */
- void createSizeCompatButton(boolean show) {
- if (!show || mButton != null) {
- // Wait until button should be visible.
- return;
- }
- mButton = mButtonWindowManager.createSizeCompatButton();
- updateButtonSurfacePosition();
-
- if (mShouldShowHint) {
- // Only show by default for the first time.
- mShouldShowHint = false;
- createSizeCompatHint();
- }
-
- mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
- }
-
- /** Creates the restart button hint window. */
- private void createSizeCompatHint() {
- if (mHint != null) {
- // Hint already shown.
- return;
- }
- mHintWindowManager = createHintWindowManager();
- mHint = mHintWindowManager.createSizeCompatHint();
- updateHintSurfacePosition();
- }
-
- @VisibleForTesting
- SizeCompatUIWindowManager createHintWindowManager() {
- return new SizeCompatUIWindowManager(mContext, mTaskConfig, this);
- }
-
- /** Dismisses the hint window. */
- void dismissHint() {
- mHint = null;
- if (mHintWindowManager != null) {
- mHintWindowManager.release();
- mHintWindowManager = null;
- }
- }
-
- /** Releases the UI windows. */
- void release() {
- dismissHint();
- mButton = null;
- mButtonWindowManager.release();
- }
-
- /** Called when size compat info changed. */
- void updateSizeCompatInfo(Configuration taskConfig,
- ShellTaskOrganizer.TaskListener taskListener, boolean show) {
- final Configuration prevTaskConfig = mTaskConfig;
- final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
- mTaskConfig = taskConfig;
- mTaskListener = taskListener;
-
- // Update configuration.
- mContext = mContext.createConfigurationContext(taskConfig);
- mButtonWindowManager.setConfiguration(taskConfig);
- if (mHintWindowManager != null) {
- mHintWindowManager.setConfiguration(taskConfig);
- }
-
- if (mButton == null || prevTaskListener != taskListener) {
- // TaskListener changed, recreate the button for new surface parent.
- release();
- createSizeCompatButton(show);
- return;
- }
-
- if (!taskConfig.windowConfiguration.getBounds()
- .equals(prevTaskConfig.windowConfiguration.getBounds())) {
- // Reposition the UI surfaces.
- updateAllSurfacePositions();
- }
-
- if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
- // Update layout for RTL.
- mButton.setLayoutDirection(taskConfig.getLayoutDirection());
- updateButtonSurfacePosition();
- if (mHint != null) {
- mHint.setLayoutDirection(taskConfig.getLayoutDirection());
- updateHintSurfacePosition();
- }
- }
- }
-
- /** Called when display layout changed. */
- void updateDisplayLayout(DisplayLayout displayLayout) {
- final Rect prevStableBounds = mStableBounds;
- final Rect curStableBounds = new Rect();
- displayLayout.getStableBounds(curStableBounds);
- mDisplayLayout = displayLayout;
- if (!prevStableBounds.equals(curStableBounds)) {
- // Stable bounds changed, update UI surface positions.
- updateAllSurfacePositions();
- mStableBounds.set(curStableBounds);
- }
- }
-
- /** Called when the visibility of the UI should change. */
- void updateVisibility(boolean show) {
- if (mButton == null) {
- // Button may not have been created because it was hidden previously.
- createSizeCompatButton(show);
- return;
- }
-
- // Hide size compat UIs when IME is showing.
- final int newVisibility = show ? View.VISIBLE : View.GONE;
- if (mButton.getVisibility() != newVisibility) {
- mButton.setVisibility(newVisibility);
- }
- if (mHint != null && mHint.getVisibility() != newVisibility) {
- mHint.setVisibility(newVisibility);
- }
- }
-
- /** Gets the layout params for restart button. */
- WindowManager.LayoutParams getButtonWindowLayoutParams() {
- final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
- // Cannot be wrap_content as this determines the actual window size
- mButtonWidth, mButtonHeight,
- TYPE_APPLICATION_OVERLAY,
- FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
- PixelFormat.TRANSLUCENT);
- winParams.token = new Binder();
- winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + getTaskId());
- winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
- return winParams;
- }
-
- /** Gets the layout params for hint popup. */
- WindowManager.LayoutParams getHintWindowLayoutParams(SizeCompatHintPopup hint) {
- final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
- // Cannot be wrap_content as this determines the actual window size
- hint.getMeasuredWidth(), hint.getMeasuredHeight(),
- TYPE_APPLICATION_OVERLAY,
- FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
- PixelFormat.TRANSLUCENT);
- winParams.token = new Binder();
- winParams.setTitle(SizeCompatHintPopup.class.getSimpleName() + getTaskId());
- winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
- winParams.windowAnimations = android.R.style.Animation_InputMethod;
- return winParams;
- }
-
- /** Called when it is ready to be placed size compat UI surface. */
- void attachToParentSurface(SurfaceControl.Builder b) {
- mTaskListener.attachChildSurfaceToTask(mTaskId, b);
- }
-
- /** Called when the restart button is clicked. */
- void onRestartButtonClicked() {
- mCallback.onSizeCompatRestartButtonClicked(mTaskId);
- }
-
- /** Called when the restart button is long clicked. */
- void onRestartButtonLongClicked() {
- createSizeCompatHint();
- }
-
- private void updateAllSurfacePositions() {
- updateButtonSurfacePosition();
- updateHintSurfacePosition();
- }
-
- @VisibleForTesting
- void updateButtonSurfacePosition() {
- if (mButton == null || mButtonWindowManager.getSurfaceControl() == null) {
- return;
- }
- final SurfaceControl leash = mButtonWindowManager.getSurfaceControl();
-
- // Use stable bounds to prevent the button from overlapping with system bars.
- final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
- final Rect stableBounds = new Rect();
- mDisplayLayout.getStableBounds(stableBounds);
- stableBounds.intersect(taskBounds);
-
- // Position of the button in the container coordinate.
- final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
- ? stableBounds.left - taskBounds.left
- : stableBounds.right - taskBounds.left - mButtonWidth;
- final int positionY = stableBounds.bottom - taskBounds.top - mButtonHeight;
-
- updateSurfacePosition(leash, positionX, positionY);
- }
-
- @VisibleForTesting
- void updateHintSurfacePosition() {
- if (mHint == null || mHintWindowManager == null
- || mHintWindowManager.getSurfaceControl() == null) {
- return;
- }
- final SurfaceControl leash = mHintWindowManager.getSurfaceControl();
-
- // Use stable bounds to prevent the hint from overlapping with system bars.
- final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
- final Rect stableBounds = new Rect();
- mDisplayLayout.getStableBounds(stableBounds);
- stableBounds.intersect(taskBounds);
-
- // Position of the hint in the container coordinate.
- final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
- ? stableBounds.left - taskBounds.left + mPopupOffsetX
- : stableBounds.right - taskBounds.left - mPopupOffsetX - mHint.getMeasuredWidth();
- final int positionY =
- stableBounds.bottom - taskBounds.top - mPopupOffsetY - mHint.getMeasuredHeight();
-
- updateSurfacePosition(leash, positionX, positionY);
- }
-
- private void updateSurfacePosition(SurfaceControl leash, int positionX, int positionY) {
- mSyncQueue.runInSync(t -> {
- if (!leash.isValid()) {
- Log.w(TAG, "The leash has been released.");
- return;
- }
- t.setPosition(leash, positionX, positionY);
- // The size compat UI should be the topmost child of the Task in case there can be more
- // than one children.
- t.setLayer(leash, Integer.MAX_VALUE);
- });
- }
-
- int getDisplayId() {
- return mDisplayId;
- }
-
- int getTaskId() {
- return mTaskId;
- }
-
- private int getLayoutDirection() {
- return mContext.getResources().getConfiguration().getLayoutDirection();
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
deleted file mode 100644
index 82f69c3..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
+++ /dev/null
@@ -1,127 +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.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.view.IWindow;
-import android.view.LayoutInflater;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.SurfaceSession;
-import android.view.View;
-import android.view.WindowlessWindowManager;
-
-import com.android.wm.shell.R;
-
-/**
- * Holds view hierarchy of a root surface and helps to inflate {@link SizeCompatRestartButton} or
- * {@link SizeCompatHintPopup}.
- */
-class SizeCompatUIWindowManager extends WindowlessWindowManager {
-
- private Context mContext;
- private final SizeCompatUILayout mLayout;
-
- @Nullable
- private SurfaceControlViewHost mViewHost;
- @Nullable
- private SurfaceControl mLeash;
-
- SizeCompatUIWindowManager(Context context, Configuration config, SizeCompatUILayout layout) {
- super(config, null /* rootSurface */, null /* hostInputToken */);
- mContext = context;
- mLayout = layout;
- }
-
- @Override
- public void setConfiguration(Configuration configuration) {
- super.setConfiguration(configuration);
- mContext = mContext.createConfigurationContext(configuration);
- }
-
- @Override
- protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
- // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
- final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
- .setContainerLayer()
- .setName("SizeCompatUILeash")
- .setHidden(false)
- .setCallsite("SizeCompatUIWindowManager#attachToParentSurface");
- mLayout.attachToParentSurface(builder);
- mLeash = builder.build();
- b.setParent(mLeash);
- }
-
- /** Inflates {@link SizeCompatRestartButton} on to the root surface. */
- SizeCompatRestartButton createSizeCompatButton() {
- if (mViewHost != null) {
- throw new IllegalStateException(
- "A UI has already been created with this window manager.");
- }
-
- mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
-
- final SizeCompatRestartButton button = (SizeCompatRestartButton)
- LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null);
- button.inject(mLayout);
- mViewHost.setView(button, mLayout.getButtonWindowLayoutParams());
- return button;
- }
-
- /** Inflates {@link SizeCompatHintPopup} on to the root surface. */
- SizeCompatHintPopup createSizeCompatHint() {
- if (mViewHost != null) {
- throw new IllegalStateException(
- "A UI has already been created with this window manager.");
- }
-
- mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
-
- final SizeCompatHintPopup hint = (SizeCompatHintPopup)
- LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null);
- // Measure how big the hint is.
- hint.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- hint.inject(mLayout);
- mViewHost.setView(hint, mLayout.getHintWindowLayoutParams(hint));
- return hint;
- }
-
- /** Releases the surface control and tears down the view hierarchy. */
- void release() {
- if (mViewHost != null) {
- mViewHost.release();
- mViewHost = null;
- }
-
- if (mLeash != null) {
- final SurfaceControl leash = mLeash;
- mLayout.mSyncQueue.runInSync(t -> t.remove(leash));
- mLeash = null;
- }
- }
-
- /**
- * Gets {@link SurfaceControl} of the surface holding size compat UI view. @return {@code null}
- * if not feasible.
- */
- @Nullable
- SurfaceControl getSurfaceControl() {
- return mLeash;
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 05552aa..cf4e56e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -102,6 +102,7 @@
static final int EXIT_REASON_ROOT_TASK_VANISHED = 6;
static final int EXIT_REASON_SCREEN_LOCKED = 7;
static final int EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP = 8;
+ static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
@IntDef(value = {
EXIT_REASON_UNKNOWN,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
@@ -112,6 +113,7 @@
EXIT_REASON_ROOT_TASK_VANISHED,
EXIT_REASON_SCREEN_LOCKED,
EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP,
+ EXIT_REASON_CHILD_TASK_ENTER_PIP,
})
@Retention(RetentionPolicy.SOURCE)
@interface ExitReason{}
@@ -406,6 +408,8 @@
return "APP_FINISHED";
case EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW:
return "APP_DOES_NOT_SUPPORT_MULTIWINDOW";
+ case EXIT_REASON_CHILD_TASK_ENTER_PIP:
+ return "CHILD_TASK_ENTER_PIP";
default:
return "unknown reason, reason int = " + exitReason;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index d1feee4..cdaa54c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -35,6 +35,7 @@
import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
@@ -341,12 +342,12 @@
sideOptions = sideOptions != null ? sideOptions : new Bundle();
setSideStagePosition(sidePosition, wct);
+ mSplitLayout.setDivideRatio(splitRatio);
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
mSideStage.setBounds(getSideStageBounds(), wct);
- mSplitLayout.setDivideRatio(splitRatio);
// Make sure the launch options will put tasks in the corresponding split roots
addActivityOptions(mainOptions, mMainStage);
addActivityOptions(sideOptions, mSideStage);
@@ -629,8 +630,12 @@
});
mShouldUpdateRecents = false;
- mSideStage.removeAllTasks(wct, childrenToTop == mSideStage);
- mMainStage.deactivate(wct, childrenToTop == mMainStage);
+ // When the exit split-screen is caused by one of the task enters auto pip,
+ // we want the tasks to be put to bottom instead of top, otherwise it will end up
+ // a fullscreen plus a pinned task instead of pinned only at the end of the transition.
+ final boolean fromEnteringPip = exitReason == EXIT_REASON_CHILD_TASK_ENTER_PIP;
+ mSideStage.removeAllTasks(wct, !fromEnteringPip && childrenToTop == mSideStage);
+ mMainStage.deactivate(wct, !fromEnteringPip && childrenToTop == mMainStage);
mTaskOrganizer.applyTransaction(wct);
mSyncQueue.runInSync(t -> t
.setWindowCrop(mMainStage.mRootLeash, null)
@@ -660,6 +665,8 @@
case EXIT_REASON_DRAG_DIVIDER:
// Either of the split apps have finished
case EXIT_REASON_APP_FINISHED:
+ // One of the children enters PiP
+ case EXIT_REASON_CHILD_TASK_ENTER_PIP:
return true;
default:
return false;
@@ -749,6 +756,11 @@
}
}
+ private void onStageChildTaskEnterPip(StageListenerImpl stageListener, int taskId) {
+ exitSplitScreen(stageListener == mMainStageListener ? mMainStage : mSideStage,
+ EXIT_REASON_CHILD_TASK_ENTER_PIP);
+ }
+
private void updateRecentTasksSplitPair() {
if (!mShouldUpdateRecents) {
return;
@@ -1443,6 +1455,11 @@
}
@Override
+ public void onChildTaskEnterPip(int taskId) {
+ StageCoordinator.this.onStageChildTaskEnterPip(this, taskId);
+ }
+
+ @Override
public void onRootTaskVanished() {
reset();
StageCoordinator.this.onStageRootTaskVanished(this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index cd10b9f..2c853c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
@@ -73,6 +74,8 @@
void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);
+ void onChildTaskEnterPip(int taskId);
+
void onRootTaskVanished();
void onNoLongerSupportMultiWindow();
@@ -256,6 +259,9 @@
mChildrenTaskInfo.remove(taskId);
mChildrenLeashes.remove(taskId);
mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible);
+ if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
+ mCallbacks.onChildTaskEnterPip(taskId);
+ }
if (ENABLE_SHELL_TRANSITIONS) {
// Status is managed/synchronized by the transition lifecycle.
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 6643ca1..4ecc0b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -380,9 +380,7 @@
}
private void drawSizeMatchSnapshot() {
- GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
- mSnapshot.getHardwareBuffer());
- mTransaction.setBuffer(mSurfaceControl, graphicBuffer)
+ mTransaction.setBuffer(mSurfaceControl, mSnapshot.getHardwareBuffer())
.setColorSpace(mSurfaceControl, mSnapshot.getColorSpace())
.apply();
}
@@ -428,20 +426,20 @@
// Scale the mismatch dimensions to fill the task bounds
mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL);
mTransaction.setMatrix(childSurfaceControl, mSnapshotMatrix, mTmpFloat9);
- GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
- mSnapshot.getHardwareBuffer());
mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace());
- mTransaction.setBuffer(childSurfaceControl, graphicBuffer);
+ mTransaction.setBuffer(childSurfaceControl, mSnapshot.getHardwareBuffer());
if (aspectRatioMismatch) {
GraphicBuffer background = GraphicBuffer.create(mFrame.width(), mFrame.height(),
PixelFormat.RGBA_8888,
GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
| GraphicBuffer.USAGE_SW_WRITE_RARELY);
+ // TODO: Support this on HardwareBuffer
final Canvas c = background.lockCanvas();
drawBackgroundAndBars(c, frame);
background.unlockCanvasAndPost(c);
- mTransaction.setBuffer(mSurfaceControl, background);
+ mTransaction.setBuffer(mSurfaceControl,
+ HardwareBuffer.createFromGraphicBuffer(background));
}
mTransaction.apply();
childSurfaceControl.release();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index a0d9d03..e404724 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -346,6 +346,15 @@
}
if (change.getMode() == TRANSIT_CHANGE) {
+ // If task is child task, only set position in parent.
+ if (isTask && change.getParent() != null
+ && info.getChange(change.getParent()).getTaskInfo() != null) {
+ final Point positionInParent = change.getTaskInfo().positionInParent;
+ startTransaction.setPosition(change.getLeash(),
+ positionInParent.x, positionInParent.y);
+ continue;
+ }
+
// No default animation for this, so just update bounds/position.
startTransaction.setPosition(change.getLeash(),
change.getEndAbsBounds().left - change.getEndRelOffset().x,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 1fcbf14..a3b98a8f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -56,7 +56,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.sizecompatui.SizeCompatUIController;
+import com.android.wm.shell.compatui.CompatUIController;
import org.junit.Before;
import org.junit.Test;
@@ -82,7 +82,7 @@
@Mock
private Context mContext;
@Mock
- private SizeCompatUIController mSizeCompatUI;
+ private CompatUIController mCompatUI;
ShellTaskOrganizer mOrganizer;
private final SyncTransactionQueue mSyncTransactionQueue = mock(SyncTransactionQueue.class);
@@ -132,7 +132,7 @@
.when(mTaskOrganizerController).registerTaskOrganizer(any());
} catch (RemoteException e) {}
mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext,
- mSizeCompatUI, Optional.empty()));
+ mCompatUI, Optional.empty()));
}
@Test
@@ -334,34 +334,34 @@
mOrganizer.onTaskAppeared(taskInfo1, null);
// sizeCompatActivity is null if top activity is not in size compat.
- verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
null /* taskConfig */, null /* taskListener */);
// sizeCompatActivity is non-null if top activity is in size compat.
- clearInvocations(mSizeCompatUI);
+ clearInvocations(mCompatUI);
final RunningTaskInfo taskInfo2 =
createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
taskInfo2.displayId = taskInfo1.displayId;
taskInfo2.topActivityInSizeCompat = true;
taskInfo2.isVisible = true;
mOrganizer.onTaskInfoChanged(taskInfo2);
- verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
taskInfo1.configuration, taskListener);
// Not show size compat UI if task is not visible.
- clearInvocations(mSizeCompatUI);
+ clearInvocations(mCompatUI);
final RunningTaskInfo taskInfo3 =
createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
taskInfo3.displayId = taskInfo1.displayId;
taskInfo3.topActivityInSizeCompat = true;
taskInfo3.isVisible = false;
mOrganizer.onTaskInfoChanged(taskInfo3);
- verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
null /* taskConfig */, null /* taskListener */);
- clearInvocations(mSizeCompatUI);
+ clearInvocations(mCompatUI);
mOrganizer.onTaskVanished(taskInfo1);
- verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
null /* taskConfig */, null /* taskListener */);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
index 1cbad15..03df92f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
@@ -21,6 +21,8 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -43,12 +45,14 @@
import android.view.SurfaceHolder;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.HandlerExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SyncTransactionQueue.TransactionRunnable;
+import com.android.wm.shell.transition.Transitions;
import org.junit.After;
import org.junit.Before;
@@ -75,6 +79,8 @@
HandlerExecutor mExecutor;
@Mock
SyncTransactionQueue mSyncQueue;
+ @Mock
+ TaskViewTransitions mTaskViewTransitions;
SurfaceSession mSession;
SurfaceControl mLeash;
@@ -110,7 +116,7 @@
return null;
}).when(mSyncQueue).runInSync(any());
- mTaskView = new TaskView(mContext, mOrganizer, mSyncQueue);
+ mTaskView = new TaskView(mContext, mOrganizer, mTaskViewTransitions, mSyncQueue);
mTaskView.setListener(mExecutor, mViewListener);
}
@@ -123,7 +129,7 @@
@Test
public void testSetPendingListener_throwsException() {
- TaskView taskView = new TaskView(mContext, mOrganizer, mSyncQueue);
+ TaskView taskView = new TaskView(mContext, mOrganizer, mTaskViewTransitions, mSyncQueue);
taskView.setListener(mExecutor, mViewListener);
try {
taskView.setListener(mExecutor, mViewListener);
@@ -144,7 +150,8 @@
}
@Test
- public void testOnTaskAppeared_noSurface() {
+ public void testOnTaskAppeared_noSurface_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
@@ -154,7 +161,8 @@
}
@Test
- public void testOnTaskAppeared_withSurface() {
+ public void testOnTaskAppeared_withSurface_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
@@ -163,7 +171,8 @@
}
@Test
- public void testSurfaceCreated_noTask() {
+ public void testSurfaceCreated_noTask_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
verify(mViewListener).onInitialized();
@@ -172,7 +181,8 @@
}
@Test
- public void testSurfaceCreated_withTask() {
+ public void testSurfaceCreated_withTask_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
@@ -181,7 +191,8 @@
}
@Test
- public void testSurfaceDestroyed_noTask() {
+ public void testSurfaceDestroyed_noTask_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
SurfaceHolder sh = mock(SurfaceHolder.class);
mTaskView.surfaceCreated(sh);
mTaskView.surfaceDestroyed(sh);
@@ -190,7 +201,8 @@
}
@Test
- public void testSurfaceDestroyed_withTask() {
+ public void testSurfaceDestroyed_withTask_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
SurfaceHolder sh = mock(SurfaceHolder.class);
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
mTaskView.surfaceCreated(sh);
@@ -201,7 +213,8 @@
}
@Test
- public void testOnReleased() {
+ public void testOnReleased_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
mTaskView.release();
@@ -211,7 +224,8 @@
}
@Test
- public void testOnTaskVanished() {
+ public void testOnTaskVanished_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
mTaskView.onTaskVanished(mTaskInfo);
@@ -220,7 +234,8 @@
}
@Test
- public void testOnBackPressedOnTaskRoot() {
+ public void testOnBackPressedOnTaskRoot_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
mTaskView.onBackPressedOnTaskRoot(mTaskInfo);
@@ -228,17 +243,158 @@
}
@Test
- public void testSetOnBackPressedOnTaskRoot() {
+ public void testSetOnBackPressedOnTaskRoot_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
}
@Test
- public void testUnsetOnBackPressedOnTaskRoot() {
+ public void testUnsetOnBackPressedOnTaskRoot_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
mTaskView.onTaskVanished(mTaskInfo);
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(false));
}
+
+ @Test
+ public void testOnNewTask_noSurface() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+
+ verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
+ verify(mViewListener, never()).onInitialized();
+ // If there's no surface the task should be made invisible
+ verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(false));
+ }
+
+ @Test
+ public void testSurfaceCreated_noTask() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+ verify(mTaskViewTransitions, never()).setTaskViewVisible(any(), anyBoolean());
+
+ verify(mViewListener).onInitialized();
+ // No task, no visibility change
+ verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void testOnNewTask_withSurface() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+
+ verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
+ verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void testSurfaceCreated_withTask() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+
+ verify(mViewListener).onInitialized();
+ verify(mTaskViewTransitions).setTaskViewVisible(eq(mTaskView), eq(true));
+
+ mTaskView.prepareOpenAnimation(false /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+
+ verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(true));
+ }
+
+ @Test
+ public void testSurfaceDestroyed_noTask() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ SurfaceHolder sh = mock(SurfaceHolder.class);
+ mTaskView.surfaceCreated(sh);
+ mTaskView.surfaceDestroyed(sh);
+
+ verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void testSurfaceDestroyed_withTask() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ SurfaceHolder sh = mock(SurfaceHolder.class);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskView.surfaceCreated(sh);
+ reset(mViewListener);
+ mTaskView.surfaceDestroyed(sh);
+
+ verify(mTaskViewTransitions).setTaskViewVisible(eq(mTaskView), eq(false));
+
+ mTaskView.prepareHideAnimation(new SurfaceControl.Transaction());
+
+ verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(false));
+ }
+
+ @Test
+ public void testOnReleased() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+ mTaskView.release();
+
+ verify(mOrganizer).removeListener(eq(mTaskView));
+ verify(mViewListener).onReleased();
+ verify(mTaskViewTransitions).removeTaskView(eq(mTaskView));
+ }
+
+ @Test
+ public void testOnTaskVanished() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+ mTaskView.prepareCloseAnimation();
+
+ verify(mViewListener).onTaskRemovalStarted(eq(mTaskInfo.taskId));
+ }
+
+ @Test
+ public void testOnBackPressedOnTaskRoot() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskView.onBackPressedOnTaskRoot(mTaskInfo);
+
+ verify(mViewListener).onBackPressedOnTaskRoot(eq(mTaskInfo.taskId));
+ }
+
+ @Test
+ public void testSetOnBackPressedOnTaskRoot() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
+ }
+
+ @Test
+ public void testUnsetOnBackPressedOnTaskRoot() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
+
+ mTaskView.prepareCloseAnimation();
+ verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(false));
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index bc701d0..8bc1223 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -39,6 +39,7 @@
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.Log;
import android.util.Pair;
import android.view.WindowManager;
@@ -913,6 +914,31 @@
assertSelectionChangedTo(mBubbleA2);
}
+ /**
+ * - have a maxed out bubble stack & all of the bubbles have been recently accessed
+ * - bubble a notification that was posted before any of those bubbles were accessed
+ * => that bubble should be added
+ *
+ */
+ @Test
+ public void test_addOldNotifWithNewerBubbles() {
+ sendUpdatedEntryAtTime(mEntryA1, 2000);
+ sendUpdatedEntryAtTime(mEntryA2, 3000);
+ sendUpdatedEntryAtTime(mEntryA3, 4000);
+ sendUpdatedEntryAtTime(mEntryB1, 5000);
+ sendUpdatedEntryAtTime(mEntryB2, 6000);
+
+ mBubbleData.setListener(mListener);
+ sendUpdatedEntryAtTime(mEntryB3, 1000 /* postTime */, 7000 /* currentTime */);
+ verifyUpdateReceived();
+
+ // B3 is in the stack
+ assertThat(mBubbleData.getBubbleInStackWithKey(mBubbleB3.getKey())).isNotNull();
+ // A1 is the oldest so it's in the overflow
+ assertThat(mBubbleData.getOverflowBubbleWithKey(mEntryA1.getKey())).isNotNull();
+ assertOrderChangedTo(mBubbleB3, mBubbleB2, mBubbleB1, mBubbleA3, mBubbleA2);
+ }
+
private void verifyUpdateReceived() {
verify(mListener).applyUpdate(mUpdateCaptor.capture());
reset(mListener);
@@ -1014,6 +1040,12 @@
}
private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime) {
+ setCurrentTime(postTime);
+ sendUpdatedEntryAtTime(entry, postTime, true /* isTextChanged */);
+ }
+
+ private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime, long currentTime) {
+ setCurrentTime(currentTime);
sendUpdatedEntryAtTime(entry, postTime, true /* isTextChanged */);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 453050f..83d5f04 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -24,6 +24,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -101,14 +102,21 @@
@Test
public void testSetDividePosition() {
- mSplitLayout.setDividePosition(anyInt());
+ mSplitLayout.setDividePosition(100, false /* applyLayoutChange */);
+ assertThat(mSplitLayout.getDividePosition()).isEqualTo(100);
+ verify(mSplitLayoutHandler, never()).onLayoutSizeChanged(any(SplitLayout.class));
+
+ mSplitLayout.setDividePosition(200, true /* applyLayoutChange */);
+ assertThat(mSplitLayout.getDividePosition()).isEqualTo(200);
verify(mSplitLayoutHandler).onLayoutSizeChanged(any(SplitLayout.class));
}
@Test
public void testSetDivideRatio() {
+ mSplitLayout.setDividePosition(200, false /* applyLayoutChange */);
mSplitLayout.setDivideRatio(0.5f);
- verify(mSplitLayoutHandler).onLayoutSizeChanged(any(SplitLayout.class));
+ assertThat(mSplitLayout.getDividePosition()).isEqualTo(
+ mSplitLayout.mDividerSnapAlgorithm.getMiddleTarget().position);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
similarity index 83%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 877b192..f622edb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.sizecompatui;
+package com.android.wm.shell.compatui;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
@@ -56,18 +56,18 @@
import org.mockito.MockitoAnnotations;
/**
- * Tests for {@link SizeCompatUIController}.
+ * Tests for {@link CompatUIController}.
*
* Build/Install/Run:
- * atest WMShellUnitTests:SizeCompatUIControllerTest
+ * atest WMShellUnitTests:CompatUIControllerTest
*/
@RunWith(AndroidTestingRunner.class)
@SmallTest
-public class SizeCompatUIControllerTest extends ShellTestCase {
+public class CompatUIControllerTest extends ShellTestCase {
private static final int DISPLAY_ID = 0;
private static final int TASK_ID = 12;
- private SizeCompatUIController mController;
+ private CompatUIController mController;
private @Mock DisplayController mMockDisplayController;
private @Mock DisplayInsetsController mMockDisplayInsetsController;
private @Mock DisplayLayout mMockDisplayLayout;
@@ -75,7 +75,7 @@
private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
private @Mock SyncTransactionQueue mMockSyncQueue;
private @Mock ShellExecutor mMockExecutor;
- private @Mock SizeCompatUILayout mMockLayout;
+ private @Mock CompatUIWindowManager mMockLayout;
@Captor
ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
@@ -87,10 +87,10 @@
doReturn(mMockDisplayLayout).when(mMockDisplayController).getDisplayLayout(anyInt());
doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId();
doReturn(TASK_ID).when(mMockLayout).getTaskId();
- mController = new SizeCompatUIController(mContext, mMockDisplayController,
+ mController = new CompatUIController(mContext, mMockDisplayController,
mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor) {
@Override
- SizeCompatUILayout createLayout(Context context, int displayId, int taskId,
+ CompatUIWindowManager createLayout(Context context, int displayId, int taskId,
Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
return mMockLayout;
}
@@ -105,24 +105,24 @@
}
@Test
- public void testOnSizeCompatInfoChanged() {
+ public void testOnCompatInfoChanged() {
final Configuration taskConfig = new Configuration();
// Verify that the restart button is added with non-null size compat info.
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
verify(mController).createLayout(any(), eq(DISPLAY_ID), eq(TASK_ID), eq(taskConfig),
eq(mMockTaskListener));
// Verify that the restart button is updated with non-null new size compat info.
final Configuration newTaskConfig = new Configuration();
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig, mMockTaskListener);
- verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener,
+ verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
true /* show */);
// Verify that the restart button is removed with null size compat info.
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, null, mMockTaskListener);
+ mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, null, mMockTaskListener);
verify(mMockLayout).release();
}
@@ -140,7 +140,7 @@
public void testOnDisplayRemoved() {
mController.onDisplayAdded(DISPLAY_ID);
final Configuration taskConfig = new Configuration();
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
+ mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
mMockTaskListener);
mController.onDisplayRemoved(DISPLAY_ID + 1);
@@ -158,7 +158,7 @@
@Test
public void testOnDisplayConfigurationChanged() {
final Configuration taskConfig = new Configuration();
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
+ mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
mMockTaskListener);
final Configuration newTaskConfig = new Configuration();
@@ -175,7 +175,7 @@
public void testInsetsChanged() {
mController.onDisplayAdded(DISPLAY_ID);
final Configuration taskConfig = new Configuration();
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
+ mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
mMockTaskListener);
InsetsState insetsState = new InsetsState();
InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
@@ -197,7 +197,7 @@
@Test
public void testChangeButtonVisibilityOnImeShowHide() {
final Configuration taskConfig = new Configuration();
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
// Verify that the restart button is hidden after IME is showing.
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
@@ -205,9 +205,9 @@
verify(mMockLayout).updateVisibility(false);
// Verify button remains hidden while IME is showing.
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
- verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener,
+ verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
false /* show */);
// Verify button is shown after IME is hidden.
@@ -219,7 +219,7 @@
@Test
public void testChangeButtonVisibilityOnKeyguardOccludedChanged() {
final Configuration taskConfig = new Configuration();
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
// Verify that the restart button is hidden after keyguard becomes occluded.
mController.onKeyguardOccludedChanged(true);
@@ -227,9 +227,9 @@
verify(mMockLayout).updateVisibility(false);
// Verify button remains hidden while keyguard is occluded.
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
- verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener,
+ verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
false /* show */);
// Verify button is shown after keyguard becomes not occluded.
@@ -241,7 +241,7 @@
@Test
public void testButtonRemainsHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
final Configuration taskConfig = new Configuration();
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
mController.onKeyguardOccludedChanged(true);
@@ -264,7 +264,7 @@
@Test
public void testButtonRemainsHiddenOnImeHideWhenKeyguardIsOccluded() {
final Configuration taskConfig = new Configuration();
- mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
mController.onKeyguardOccludedChanged(true);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
new file mode 100644
index 0000000..2c3987b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+package com.android.wm.shell.compatui;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Configuration;
+import android.testing.AndroidTestingRunner;
+import android.view.LayoutInflater;
+import android.view.SurfaceControlViewHost;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link CompatUILayout}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:CompatUILayoutTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class CompatUILayoutTest extends ShellTestCase {
+
+ private static final int TASK_ID = 1;
+
+ @Mock private SyncTransactionQueue mSyncTransactionQueue;
+ @Mock private CompatUIController.CompatUICallback mCallback;
+ @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
+ @Mock private SurfaceControlViewHost mViewHost;
+
+ private CompatUIWindowManager mWindowManager;
+ private CompatUILayout mCompatUILayout;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
+ mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
+ false /* hasShownHint */);
+
+ mCompatUILayout = (CompatUILayout)
+ LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, null);
+ mCompatUILayout.inject(mWindowManager);
+
+ spyOn(mWindowManager);
+ spyOn(mCompatUILayout);
+ doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
+ }
+
+ @Test
+ public void testOnClickForRestartButton() {
+ final ImageButton button = mCompatUILayout.findViewById(R.id.size_compat_restart_button);
+ button.performClick();
+
+ verify(mWindowManager).onRestartButtonClicked();
+ doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
+ verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
+ }
+
+ @Test
+ public void testOnLongClickForRestartButton() {
+ doNothing().when(mWindowManager).onRestartButtonLongClicked();
+
+ final ImageButton button = mCompatUILayout.findViewById(R.id.size_compat_restart_button);
+ button.performLongClick();
+
+ verify(mWindowManager).onRestartButtonLongClicked();
+ }
+
+ @Test
+ public void testOnClickForSizeCompatHint() {
+ mWindowManager.createLayout(true /* show */);
+ final LinearLayout sizeCompatHint = mCompatUILayout.findViewById(R.id.size_compat_hint);
+ sizeCompatHint.performClick();
+
+ verify(mCompatUILayout).setSizeCompatHintVisibility(/* show= */ false);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
new file mode 100644
index 0000000..d5dcf2e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -0,0 +1,253 @@
+/*
+ * 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.
+ */
+
+package com.android.wm.shell.compatui;
+
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.view.DisplayInfo;
+import android.view.InsetsSource;
+import android.view.InsetsState;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link CompatUIWindowManager}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:CompatUIWindowManagerTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class CompatUIWindowManagerTest extends ShellTestCase {
+
+ private static final int TASK_ID = 1;
+
+ @Mock private SyncTransactionQueue mSyncTransactionQueue;
+ @Mock private CompatUIController.CompatUICallback mCallback;
+ @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
+ @Mock private CompatUILayout mCompatUILayout;
+ @Mock private SurfaceControlViewHost mViewHost;
+ private Configuration mTaskConfig;
+
+ private CompatUIWindowManager mWindowManager;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mTaskConfig = new Configuration();
+
+ mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
+ mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
+ false /* hasShownHint */);
+
+ spyOn(mWindowManager);
+ doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
+ doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
+ }
+
+ @Test
+ public void testCreateSizeCompatButton() {
+ // Not create layout if show is false.
+ mWindowManager.createLayout(false /* show */);
+
+ verify(mWindowManager, never()).inflateCompatUILayout();
+
+ // Not create hint popup.
+ mWindowManager.mShouldShowHint = false;
+ mWindowManager.createLayout(true /* show */);
+
+ verify(mWindowManager).inflateCompatUILayout();
+ verify(mCompatUILayout).setSizeCompatHintVisibility(false /* show */);
+
+ // Create hint popup.
+ mWindowManager.release();
+ mWindowManager.mShouldShowHint = true;
+ mWindowManager.createLayout(true /* show */);
+
+ verify(mWindowManager, times(2)).inflateCompatUILayout();
+ assertNotNull(mCompatUILayout);
+ verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
+ assertFalse(mWindowManager.mShouldShowHint);
+ }
+
+ @Test
+ public void testRelease() {
+ mWindowManager.createLayout(true /* show */);
+
+ verify(mWindowManager).inflateCompatUILayout();
+
+ mWindowManager.release();
+
+ verify(mViewHost).release();
+ }
+
+ @Test
+ public void testUpdateCompatInfo() {
+ mWindowManager.createLayout(true /* show */);
+
+ // No diff
+ clearInvocations(mWindowManager);
+ mWindowManager.updateCompatInfo(mTaskConfig, mTaskListener, true /* show */);
+
+ verify(mWindowManager, never()).updateSurfacePosition();
+ verify(mWindowManager, never()).release();
+ verify(mWindowManager, never()).createLayout(anyBoolean());
+
+ // Change task listener, recreate button.
+ clearInvocations(mWindowManager);
+ final ShellTaskOrganizer.TaskListener newTaskListener = mock(
+ ShellTaskOrganizer.TaskListener.class);
+ mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener,
+ true /* show */);
+
+ verify(mWindowManager).release();
+ verify(mWindowManager).createLayout(anyBoolean());
+
+ // Change task bounds, update position.
+ clearInvocations(mWindowManager);
+ final Configuration newTaskConfiguration = new Configuration();
+ newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
+ mWindowManager.updateCompatInfo(newTaskConfiguration, newTaskListener,
+ true /* show */);
+
+ verify(mWindowManager).updateSurfacePosition();
+ }
+
+ @Test
+ public void testUpdateDisplayLayout() {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = 1000;
+ displayInfo.logicalHeight = 2000;
+ final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo,
+ mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
+
+ mWindowManager.updateDisplayLayout(displayLayout1);
+ verify(mWindowManager).updateSurfacePosition();
+
+ // No update if the display bounds is the same.
+ clearInvocations(mWindowManager);
+ final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo,
+ mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
+ mWindowManager.updateDisplayLayout(displayLayout2);
+ verify(mWindowManager, never()).updateSurfacePosition();
+ }
+
+ @Test
+ public void testUpdateDisplayLayoutInsets() {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = 1000;
+ displayInfo.logicalHeight = 2000;
+ final DisplayLayout displayLayout = new DisplayLayout(displayInfo,
+ mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false);
+
+ mWindowManager.updateDisplayLayout(displayLayout);
+ verify(mWindowManager).updateSurfacePosition();
+
+ // Update if the insets change on the existing display layout
+ clearInvocations(mWindowManager);
+ InsetsState insetsState = new InsetsState();
+ InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
+ insetsSource.setFrame(0, 0, 1000, 1000);
+ insetsState.addSource(insetsSource);
+ displayLayout.setInsets(mContext.getResources(), insetsState);
+ mWindowManager.updateDisplayLayout(displayLayout);
+ verify(mWindowManager).updateSurfacePosition();
+ }
+
+ @Test
+ public void testUpdateVisibility() {
+ // Create button if it is not created.
+ mWindowManager.mCompatUILayout = null;
+ mWindowManager.updateVisibility(true /* show */);
+
+ verify(mWindowManager).createLayout(true /* show */);
+
+ // Hide button.
+ clearInvocations(mWindowManager);
+ doReturn(View.VISIBLE).when(mCompatUILayout).getVisibility();
+ mWindowManager.updateVisibility(false /* show */);
+
+ verify(mWindowManager, never()).createLayout(anyBoolean());
+ verify(mCompatUILayout).setVisibility(View.GONE);
+
+ // Show button.
+ doReturn(View.GONE).when(mCompatUILayout).getVisibility();
+ mWindowManager.updateVisibility(true /* show */);
+
+ verify(mWindowManager, never()).createLayout(anyBoolean());
+ verify(mCompatUILayout).setVisibility(View.VISIBLE);
+ }
+
+ @Test
+ public void testAttachToParentSurface() {
+ final SurfaceControl.Builder b = new SurfaceControl.Builder();
+ mWindowManager.attachToParentSurface(b);
+
+ verify(mTaskListener).attachChildSurfaceToTask(TASK_ID, b);
+ }
+
+ @Test
+ public void testOnRestartButtonClicked() {
+ mWindowManager.onRestartButtonClicked();
+
+ verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
+ }
+
+ @Test
+ public void testOnRestartButtonLongClicked_showHint() {
+ // Not create hint popup.
+ mWindowManager.mShouldShowHint = false;
+ mWindowManager.createLayout(true /* show */);
+
+ verify(mWindowManager).inflateCompatUILayout();
+ verify(mCompatUILayout).setSizeCompatHintVisibility(false /* show */);
+
+ mWindowManager.onRestartButtonLongClicked();
+
+ verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
+ }
+
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java
deleted file mode 100644
index 3a14a33..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java
+++ /dev/null
@@ -1,85 +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.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.verify;
-
-import android.content.res.Configuration;
-import android.testing.AndroidTestingRunner;
-import android.view.LayoutInflater;
-import android.widget.LinearLayout;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link SizeCompatHintPopup}.
- *
- * Build/Install/Run:
- * atest WMShellUnitTests:SizeCompatHintPopupTest
- */
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class SizeCompatHintPopupTest extends ShellTestCase {
-
- @Mock private SyncTransactionQueue mSyncTransactionQueue;
- @Mock private SizeCompatUIController.SizeCompatUICallback mCallback;
- @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
- @Mock private DisplayLayout mDisplayLayout;
-
- private SizeCompatUILayout mLayout;
- private SizeCompatHintPopup mHint;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- final int taskId = 1;
- mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext,
- new Configuration(), taskId, mTaskListener, mDisplayLayout,
- false /* hasShownHint */);
- mHint = (SizeCompatHintPopup)
- LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null);
- mHint.inject(mLayout);
-
- spyOn(mLayout);
- }
-
- @Test
- public void testOnClick() {
- doNothing().when(mLayout).dismissHint();
-
- final LinearLayout hintPopup = mHint.findViewById(R.id.size_compat_hint_popup);
- hintPopup.performClick();
-
- verify(mLayout).dismissHint();
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java
deleted file mode 100644
index a20a5e9..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java
+++ /dev/null
@@ -1,95 +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.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.verify;
-
-import android.content.res.Configuration;
-import android.testing.AndroidTestingRunner;
-import android.view.LayoutInflater;
-import android.widget.ImageButton;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link SizeCompatRestartButton}.
- *
- * Build/Install/Run:
- * atest WMShellUnitTests:SizeCompatRestartButtonTest
- */
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class SizeCompatRestartButtonTest extends ShellTestCase {
-
- private static final int TASK_ID = 1;
-
- @Mock private SyncTransactionQueue mSyncTransactionQueue;
- @Mock private SizeCompatUIController.SizeCompatUICallback mCallback;
- @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
- @Mock private DisplayLayout mDisplayLayout;
-
- private SizeCompatUILayout mLayout;
- private SizeCompatRestartButton mButton;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext,
- new Configuration(), TASK_ID, mTaskListener, mDisplayLayout,
- false /* hasShownHint */);
- mButton = (SizeCompatRestartButton)
- LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null);
- mButton.inject(mLayout);
-
- spyOn(mLayout);
- }
-
- @Test
- public void testOnClick() {
- final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button);
- button.performClick();
-
- verify(mLayout).onRestartButtonClicked();
- verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
- }
-
- @Test
- public void testOnLongClick() {
- doNothing().when(mLayout).onRestartButtonLongClicked();
-
- final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button);
- button.performLongClick();
-
- verify(mLayout).onRestartButtonLongClicked();
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
deleted file mode 100644
index eb9305b..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
+++ /dev/null
@@ -1,284 +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.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.testing.AndroidTestingRunner;
-import android.view.DisplayInfo;
-import android.view.InsetsSource;
-import android.view.InsetsState;
-import android.view.SurfaceControl;
-import android.view.View;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link SizeCompatUILayout}.
- *
- * Build/Install/Run:
- * atest WMShellUnitTests:SizeCompatUILayoutTest
- */
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class SizeCompatUILayoutTest extends ShellTestCase {
-
- private static final int TASK_ID = 1;
-
- @Mock private SyncTransactionQueue mSyncTransactionQueue;
- @Mock private SizeCompatUIController.SizeCompatUICallback mCallback;
- @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
- @Mock private DisplayLayout mDisplayLayout;
- @Mock private SizeCompatRestartButton mButton;
- @Mock private SizeCompatHintPopup mHint;
- private Configuration mTaskConfig;
-
- private SizeCompatUILayout mLayout;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mTaskConfig = new Configuration();
-
- mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext,
- new Configuration(), TASK_ID, mTaskListener, new DisplayLayout(),
- false /* hasShownHint */);
-
- spyOn(mLayout);
- spyOn(mLayout.mButtonWindowManager);
- doReturn(mButton).when(mLayout.mButtonWindowManager).createSizeCompatButton();
-
- final SizeCompatUIWindowManager hintWindowManager = mLayout.createHintWindowManager();
- spyOn(hintWindowManager);
- doReturn(mHint).when(hintWindowManager).createSizeCompatHint();
- doReturn(hintWindowManager).when(mLayout).createHintWindowManager();
- }
-
- @Test
- public void testCreateSizeCompatButton() {
- // Not create button if show is false.
- mLayout.createSizeCompatButton(false /* show */);
-
- verify(mLayout.mButtonWindowManager, never()).createSizeCompatButton();
- assertNull(mLayout.mButton);
- assertNull(mLayout.mHintWindowManager);
- assertNull(mLayout.mHint);
-
- // Not create hint popup.
- mLayout.mShouldShowHint = false;
- mLayout.createSizeCompatButton(true /* show */);
-
- verify(mLayout.mButtonWindowManager).createSizeCompatButton();
- assertNotNull(mLayout.mButton);
- assertNull(mLayout.mHintWindowManager);
- assertNull(mLayout.mHint);
-
- // Create hint popup.
- mLayout.release();
- mLayout.mShouldShowHint = true;
- mLayout.createSizeCompatButton(true /* show */);
-
- verify(mLayout.mButtonWindowManager, times(2)).createSizeCompatButton();
- assertNotNull(mLayout.mButton);
- assertNotNull(mLayout.mHintWindowManager);
- verify(mLayout.mHintWindowManager).createSizeCompatHint();
- assertNotNull(mLayout.mHint);
- assertFalse(mLayout.mShouldShowHint);
- }
-
- @Test
- public void testRelease() {
- mLayout.createSizeCompatButton(true /* show */);
- final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager;
-
- mLayout.release();
-
- assertNull(mLayout.mButton);
- assertNull(mLayout.mHint);
- verify(hintWindowManager).release();
- assertNull(mLayout.mHintWindowManager);
- verify(mLayout.mButtonWindowManager).release();
- }
-
- @Test
- public void testUpdateSizeCompatInfo() {
- mLayout.createSizeCompatButton(true /* show */);
-
- // No diff
- clearInvocations(mLayout);
- mLayout.updateSizeCompatInfo(mTaskConfig, mTaskListener, true /* show */);
-
- verify(mLayout, never()).updateButtonSurfacePosition();
- verify(mLayout, never()).release();
- verify(mLayout, never()).createSizeCompatButton(anyBoolean());
-
- // Change task listener, recreate button.
- clearInvocations(mLayout);
- final ShellTaskOrganizer.TaskListener newTaskListener = mock(
- ShellTaskOrganizer.TaskListener.class);
- mLayout.updateSizeCompatInfo(mTaskConfig, newTaskListener,
- true /* show */);
-
- verify(mLayout).release();
- verify(mLayout).createSizeCompatButton(anyBoolean());
-
- // Change task bounds, update position.
- clearInvocations(mLayout);
- final Configuration newTaskConfiguration = new Configuration();
- newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
- mLayout.updateSizeCompatInfo(newTaskConfiguration, newTaskListener,
- true /* show */);
-
- verify(mLayout).updateButtonSurfacePosition();
- verify(mLayout).updateHintSurfacePosition();
- }
-
- @Test
- public void testUpdateDisplayLayout() {
- final DisplayInfo displayInfo = new DisplayInfo();
- displayInfo.logicalWidth = 1000;
- displayInfo.logicalHeight = 2000;
- final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo,
- mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
-
- mLayout.updateDisplayLayout(displayLayout1);
- verify(mLayout).updateButtonSurfacePosition();
- verify(mLayout).updateHintSurfacePosition();
-
- // No update if the display bounds is the same.
- clearInvocations(mLayout);
- final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo,
- mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
- mLayout.updateDisplayLayout(displayLayout2);
- verify(mLayout, never()).updateButtonSurfacePosition();
- verify(mLayout, never()).updateHintSurfacePosition();
- }
-
- @Test
- public void testUpdateDisplayLayoutInsets() {
- final DisplayInfo displayInfo = new DisplayInfo();
- displayInfo.logicalWidth = 1000;
- displayInfo.logicalHeight = 2000;
- final DisplayLayout displayLayout = new DisplayLayout(displayInfo,
- mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false);
-
- mLayout.updateDisplayLayout(displayLayout);
- verify(mLayout).updateButtonSurfacePosition();
- verify(mLayout).updateHintSurfacePosition();
-
- // Update if the insets change on the existing display layout
- clearInvocations(mLayout);
- InsetsState insetsState = new InsetsState();
- InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
- insetsSource.setFrame(0, 0, 1000, 1000);
- insetsState.addSource(insetsSource);
- displayLayout.setInsets(mContext.getResources(), insetsState);
- mLayout.updateDisplayLayout(displayLayout);
- verify(mLayout).updateButtonSurfacePosition();
- verify(mLayout).updateHintSurfacePosition();
- }
-
- @Test
- public void testUpdateVisibility() {
- // Create button if it is not created.
- mLayout.mButton = null;
- mLayout.updateVisibility(true /* show */);
-
- verify(mLayout).createSizeCompatButton(true /* show */);
-
- // Hide button.
- clearInvocations(mLayout);
- doReturn(View.VISIBLE).when(mButton).getVisibility();
- mLayout.updateVisibility(false /* show */);
-
- verify(mLayout, never()).createSizeCompatButton(anyBoolean());
- verify(mButton).setVisibility(View.GONE);
-
- // Show button.
- doReturn(View.GONE).when(mButton).getVisibility();
- mLayout.updateVisibility(true /* show */);
-
- verify(mLayout, never()).createSizeCompatButton(anyBoolean());
- verify(mButton).setVisibility(View.VISIBLE);
- }
-
- @Test
- public void testAttachToParentSurface() {
- final SurfaceControl.Builder b = new SurfaceControl.Builder();
- mLayout.attachToParentSurface(b);
-
- verify(mTaskListener).attachChildSurfaceToTask(TASK_ID, b);
- }
-
- @Test
- public void testOnRestartButtonClicked() {
- mLayout.onRestartButtonClicked();
-
- verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
- }
-
- @Test
- public void testOnRestartButtonLongClicked_showHint() {
- mLayout.dismissHint();
-
- assertNull(mLayout.mHint);
-
- mLayout.onRestartButtonLongClicked();
-
- assertNotNull(mLayout.mHint);
- }
-
- @Test
- public void testDismissHint() {
- mLayout.onRestartButtonLongClicked();
- final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager;
- assertNotNull(mLayout.mHint);
- assertNotNull(hintWindowManager);
-
- mLayout.dismissHint();
-
- assertNull(mLayout.mHint);
- assertNull(mLayout.mHintWindowManager);
- verify(hintWindowManager).release();
- }
-}
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 22904a0..136fc6c 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -902,6 +902,27 @@
return log_stream.str();
}
+base::expected<uint32_t, NullOrIOError> AssetManager2::GetParentThemeResourceId(uint32_t resid)
+const {
+ auto entry = FindEntry(resid, 0u /* density_override */,
+ false /* stop_at_first_match */,
+ false /* ignore_configuration */);
+ if (!entry.has_value()) {
+ return base::unexpected(entry.error());
+ }
+
+ auto entry_map = std::get_if<incfs::verified_map_ptr<ResTable_map_entry>>(&entry->entry);
+ if (entry_map == nullptr) {
+ // Not a bag, nothing to do.
+ return base::unexpected(std::nullopt);
+ }
+
+ auto map = *entry_map;
+ const uint32_t parent_resid = dtohl(map->parent.ident);
+
+ return parent_resid;
+}
+
base::expected<AssetManager2::ResourceName, NullOrIOError> AssetManager2::GetResourceName(
uint32_t resid) const {
auto result = FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */,
diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp
index 2c3567a..2c005fd 100644
--- a/libs/androidfw/LocaleDataTables.cpp
+++ b/libs/androidfw/LocaleDataTables.cpp
@@ -34,206 +34,209 @@
/* 30 */ {'H', 'a', 'n', 't'},
/* 31 */ {'H', 'e', 'b', 'r'},
/* 32 */ {'H', 'l', 'u', 'w'},
- /* 33 */ {'H', 'm', 'n', 'g'},
- /* 34 */ {'H', 'm', 'n', 'p'},
- /* 35 */ {'I', 't', 'a', 'l'},
- /* 36 */ {'J', 'p', 'a', 'n'},
- /* 37 */ {'K', 'a', 'l', 'i'},
- /* 38 */ {'K', 'a', 'n', 'a'},
- /* 39 */ {'K', 'h', 'a', 'r'},
- /* 40 */ {'K', 'h', 'm', 'r'},
- /* 41 */ {'K', 'i', 't', 's'},
- /* 42 */ {'K', 'n', 'd', 'a'},
- /* 43 */ {'K', 'o', 'r', 'e'},
- /* 44 */ {'L', 'a', 'n', 'a'},
- /* 45 */ {'L', 'a', 'o', 'o'},
- /* 46 */ {'L', 'a', 't', 'n'},
- /* 47 */ {'L', 'e', 'p', 'c'},
- /* 48 */ {'L', 'i', 'n', 'a'},
- /* 49 */ {'L', 'i', 's', 'u'},
- /* 50 */ {'L', 'y', 'c', 'i'},
- /* 51 */ {'L', 'y', 'd', 'i'},
- /* 52 */ {'M', 'a', 'n', 'd'},
- /* 53 */ {'M', 'a', 'n', 'i'},
- /* 54 */ {'M', 'e', 'd', 'f'},
- /* 55 */ {'M', 'e', 'r', 'c'},
- /* 56 */ {'M', 'l', 'y', 'm'},
- /* 57 */ {'M', 'o', 'n', 'g'},
- /* 58 */ {'M', 'r', 'o', 'o'},
- /* 59 */ {'M', 'y', 'm', 'r'},
- /* 60 */ {'N', 'a', 'r', 'b'},
- /* 61 */ {'N', 'k', 'o', 'o'},
- /* 62 */ {'N', 's', 'h', 'u'},
- /* 63 */ {'O', 'g', 'a', 'm'},
- /* 64 */ {'O', 'l', 'c', 'k'},
- /* 65 */ {'O', 'r', 'k', 'h'},
- /* 66 */ {'O', 'r', 'y', 'a'},
- /* 67 */ {'O', 's', 'g', 'e'},
+ /* 33 */ {'H', 'm', 'n', 'p'},
+ /* 34 */ {'I', 't', 'a', 'l'},
+ /* 35 */ {'J', 'p', 'a', 'n'},
+ /* 36 */ {'K', 'a', 'l', 'i'},
+ /* 37 */ {'K', 'a', 'n', 'a'},
+ /* 38 */ {'K', 'h', 'a', 'r'},
+ /* 39 */ {'K', 'h', 'm', 'r'},
+ /* 40 */ {'K', 'i', 't', 's'},
+ /* 41 */ {'K', 'n', 'd', 'a'},
+ /* 42 */ {'K', 'o', 'r', 'e'},
+ /* 43 */ {'L', 'a', 'n', 'a'},
+ /* 44 */ {'L', 'a', 'o', 'o'},
+ /* 45 */ {'L', 'a', 't', 'n'},
+ /* 46 */ {'L', 'e', 'p', 'c'},
+ /* 47 */ {'L', 'i', 'n', 'a'},
+ /* 48 */ {'L', 'i', 's', 'u'},
+ /* 49 */ {'L', 'y', 'c', 'i'},
+ /* 50 */ {'L', 'y', 'd', 'i'},
+ /* 51 */ {'M', 'a', 'n', 'd'},
+ /* 52 */ {'M', 'a', 'n', 'i'},
+ /* 53 */ {'M', 'e', 'd', 'f'},
+ /* 54 */ {'M', 'e', 'r', 'c'},
+ /* 55 */ {'M', 'l', 'y', 'm'},
+ /* 56 */ {'M', 'o', 'n', 'g'},
+ /* 57 */ {'M', 'r', 'o', 'o'},
+ /* 58 */ {'M', 'y', 'm', 'r'},
+ /* 59 */ {'N', 'a', 'r', 'b'},
+ /* 60 */ {'N', 'k', 'o', 'o'},
+ /* 61 */ {'N', 's', 'h', 'u'},
+ /* 62 */ {'O', 'g', 'a', 'm'},
+ /* 63 */ {'O', 'l', 'c', 'k'},
+ /* 64 */ {'O', 'r', 'k', 'h'},
+ /* 65 */ {'O', 'r', 'y', 'a'},
+ /* 66 */ {'O', 's', 'g', 'e'},
+ /* 67 */ {'O', 'u', 'g', 'r'},
/* 68 */ {'P', 'a', 'u', 'c'},
/* 69 */ {'P', 'h', 'l', 'i'},
/* 70 */ {'P', 'h', 'n', 'x'},
/* 71 */ {'P', 'l', 'r', 'd'},
/* 72 */ {'P', 'r', 't', 'i'},
- /* 73 */ {'R', 'u', 'n', 'r'},
- /* 74 */ {'S', 'a', 'm', 'r'},
- /* 75 */ {'S', 'a', 'r', 'b'},
- /* 76 */ {'S', 'a', 'u', 'r'},
- /* 77 */ {'S', 'g', 'n', 'w'},
- /* 78 */ {'S', 'i', 'n', 'h'},
- /* 79 */ {'S', 'o', 'g', 'd'},
- /* 80 */ {'S', 'o', 'r', 'a'},
- /* 81 */ {'S', 'o', 'y', 'o'},
- /* 82 */ {'S', 'y', 'r', 'c'},
- /* 83 */ {'T', 'a', 'l', 'e'},
- /* 84 */ {'T', 'a', 'l', 'u'},
- /* 85 */ {'T', 'a', 'm', 'l'},
- /* 86 */ {'T', 'a', 'n', 'g'},
- /* 87 */ {'T', 'a', 'v', 't'},
- /* 88 */ {'T', 'e', 'l', 'u'},
- /* 89 */ {'T', 'f', 'n', 'g'},
- /* 90 */ {'T', 'h', 'a', 'a'},
- /* 91 */ {'T', 'h', 'a', 'i'},
- /* 92 */ {'T', 'i', 'b', 't'},
- /* 93 */ {'U', 'g', 'a', 'r'},
- /* 94 */ {'V', 'a', 'i', 'i'},
- /* 95 */ {'W', 'c', 'h', 'o'},
- /* 96 */ {'X', 'p', 'e', 'o'},
- /* 97 */ {'X', 's', 'u', 'x'},
- /* 98 */ {'Y', 'i', 'i', 'i'},
- /* 99 */ {'~', '~', '~', 'A'},
- /* 100 */ {'~', '~', '~', 'B'},
+ /* 73 */ {'R', 'o', 'h', 'g'},
+ /* 74 */ {'R', 'u', 'n', 'r'},
+ /* 75 */ {'S', 'a', 'm', 'r'},
+ /* 76 */ {'S', 'a', 'r', 'b'},
+ /* 77 */ {'S', 'a', 'u', 'r'},
+ /* 78 */ {'S', 'g', 'n', 'w'},
+ /* 79 */ {'S', 'i', 'n', 'h'},
+ /* 80 */ {'S', 'o', 'g', 'd'},
+ /* 81 */ {'S', 'o', 'r', 'a'},
+ /* 82 */ {'S', 'o', 'y', 'o'},
+ /* 83 */ {'S', 'y', 'r', 'c'},
+ /* 84 */ {'T', 'a', 'l', 'e'},
+ /* 85 */ {'T', 'a', 'l', 'u'},
+ /* 86 */ {'T', 'a', 'm', 'l'},
+ /* 87 */ {'T', 'a', 'n', 'g'},
+ /* 88 */ {'T', 'a', 'v', 't'},
+ /* 89 */ {'T', 'e', 'l', 'u'},
+ /* 90 */ {'T', 'f', 'n', 'g'},
+ /* 91 */ {'T', 'h', 'a', 'a'},
+ /* 92 */ {'T', 'h', 'a', 'i'},
+ /* 93 */ {'T', 'i', 'b', 't'},
+ /* 94 */ {'T', 'n', 's', 'a'},
+ /* 95 */ {'T', 'o', 't', 'o'},
+ /* 96 */ {'U', 'g', 'a', 'r'},
+ /* 97 */ {'V', 'a', 'i', 'i'},
+ /* 98 */ {'W', 'c', 'h', 'o'},
+ /* 99 */ {'X', 'p', 'e', 'o'},
+ /* 100 */ {'X', 's', 'u', 'x'},
+ /* 101 */ {'Y', 'i', 'i', 'i'},
+ /* 102 */ {'~', '~', '~', 'A'},
+ /* 103 */ {'~', '~', '~', 'B'},
};
const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({
- {0x61610000u, 46u}, // aa -> Latn
- {0xA0000000u, 46u}, // aai -> Latn
- {0xA8000000u, 46u}, // aak -> Latn
- {0xD0000000u, 46u}, // aau -> Latn
+ {0x61610000u, 45u}, // aa -> Latn
+ {0xA0000000u, 45u}, // aai -> Latn
+ {0xA8000000u, 45u}, // aak -> Latn
+ {0xD0000000u, 45u}, // aau -> Latn
{0x61620000u, 18u}, // ab -> Cyrl
- {0xA0200000u, 46u}, // abi -> Latn
+ {0xA0200000u, 45u}, // abi -> Latn
{0xC0200000u, 18u}, // abq -> Cyrl
- {0xC4200000u, 46u}, // abr -> Latn
- {0xCC200000u, 46u}, // abt -> Latn
- {0xE0200000u, 46u}, // aby -> Latn
- {0x8C400000u, 46u}, // acd -> Latn
- {0x90400000u, 46u}, // ace -> Latn
- {0x9C400000u, 46u}, // ach -> Latn
- {0x80600000u, 46u}, // ada -> Latn
- {0x90600000u, 46u}, // ade -> Latn
- {0xA4600000u, 46u}, // adj -> Latn
- {0xBC600000u, 92u}, // adp -> Tibt
+ {0xC4200000u, 45u}, // abr -> Latn
+ {0xCC200000u, 45u}, // abt -> Latn
+ {0xE0200000u, 45u}, // aby -> Latn
+ {0x8C400000u, 45u}, // acd -> Latn
+ {0x90400000u, 45u}, // ace -> Latn
+ {0x9C400000u, 45u}, // ach -> Latn
+ {0x80600000u, 45u}, // ada -> Latn
+ {0x90600000u, 45u}, // ade -> Latn
+ {0xA4600000u, 45u}, // adj -> Latn
+ {0xBC600000u, 93u}, // adp -> Tibt
{0xE0600000u, 18u}, // ady -> Cyrl
- {0xE4600000u, 46u}, // adz -> Latn
+ {0xE4600000u, 45u}, // adz -> Latn
{0x61650000u, 5u}, // ae -> Avst
{0x84800000u, 2u}, // aeb -> Arab
- {0xE0800000u, 46u}, // aey -> Latn
- {0x61660000u, 46u}, // af -> Latn
- {0x88C00000u, 46u}, // agc -> Latn
- {0x8CC00000u, 46u}, // agd -> Latn
- {0x98C00000u, 46u}, // agg -> Latn
- {0xB0C00000u, 46u}, // agm -> Latn
- {0xB8C00000u, 46u}, // ago -> Latn
- {0xC0C00000u, 46u}, // agq -> Latn
- {0x80E00000u, 46u}, // aha -> Latn
- {0xACE00000u, 46u}, // ahl -> Latn
+ {0xE0800000u, 45u}, // aey -> Latn
+ {0x61660000u, 45u}, // af -> Latn
+ {0x88C00000u, 45u}, // agc -> Latn
+ {0x8CC00000u, 45u}, // agd -> Latn
+ {0x98C00000u, 45u}, // agg -> Latn
+ {0xB0C00000u, 45u}, // agm -> Latn
+ {0xB8C00000u, 45u}, // ago -> Latn
+ {0xC0C00000u, 45u}, // agq -> Latn
+ {0x80E00000u, 45u}, // aha -> Latn
+ {0xACE00000u, 45u}, // ahl -> Latn
{0xB8E00000u, 1u}, // aho -> Ahom
- {0x99200000u, 46u}, // ajg -> Latn
- {0x616B0000u, 46u}, // ak -> Latn
- {0xA9400000u, 97u}, // akk -> Xsux
- {0x81600000u, 46u}, // ala -> Latn
- {0xA1600000u, 46u}, // ali -> Latn
- {0xB5600000u, 46u}, // aln -> Latn
+ {0x99200000u, 45u}, // ajg -> Latn
+ {0x616B0000u, 45u}, // ak -> Latn
+ {0xA9400000u, 100u}, // akk -> Xsux
+ {0x81600000u, 45u}, // ala -> Latn
+ {0xA1600000u, 45u}, // ali -> Latn
+ {0xB5600000u, 45u}, // aln -> Latn
{0xCD600000u, 18u}, // alt -> Cyrl
{0x616D0000u, 21u}, // am -> Ethi
- {0xB1800000u, 46u}, // amm -> Latn
- {0xB5800000u, 46u}, // amn -> Latn
- {0xB9800000u, 46u}, // amo -> Latn
- {0xBD800000u, 46u}, // amp -> Latn
- {0x616E0000u, 46u}, // an -> Latn
- {0x89A00000u, 46u}, // anc -> Latn
- {0xA9A00000u, 46u}, // ank -> Latn
- {0xB5A00000u, 46u}, // ann -> Latn
- {0xE1A00000u, 46u}, // any -> Latn
- {0xA5C00000u, 46u}, // aoj -> Latn
- {0xB1C00000u, 46u}, // aom -> Latn
- {0xE5C00000u, 46u}, // aoz -> Latn
+ {0xB1800000u, 45u}, // amm -> Latn
+ {0xB5800000u, 45u}, // amn -> Latn
+ {0xB9800000u, 45u}, // amo -> Latn
+ {0xBD800000u, 45u}, // amp -> Latn
+ {0x616E0000u, 45u}, // an -> Latn
+ {0x89A00000u, 45u}, // anc -> Latn
+ {0xA9A00000u, 45u}, // ank -> Latn
+ {0xB5A00000u, 45u}, // ann -> Latn
+ {0xE1A00000u, 45u}, // any -> Latn
+ {0xA5C00000u, 45u}, // aoj -> Latn
+ {0xB1C00000u, 45u}, // aom -> Latn
+ {0xE5C00000u, 45u}, // aoz -> Latn
{0x89E00000u, 2u}, // apc -> Arab
{0x8DE00000u, 2u}, // apd -> Arab
- {0x91E00000u, 46u}, // ape -> Latn
- {0xC5E00000u, 46u}, // apr -> Latn
- {0xC9E00000u, 46u}, // aps -> Latn
- {0xE5E00000u, 46u}, // apz -> Latn
+ {0x91E00000u, 45u}, // ape -> Latn
+ {0xC5E00000u, 45u}, // apr -> Latn
+ {0xC9E00000u, 45u}, // aps -> Latn
+ {0xE5E00000u, 45u}, // apz -> Latn
{0x61720000u, 2u}, // ar -> Arab
- {0x61725842u, 100u}, // ar-XB -> ~~~B
+ {0x61725842u, 103u}, // ar-XB -> ~~~B
{0x8A200000u, 3u}, // arc -> Armi
- {0x9E200000u, 46u}, // arh -> Latn
- {0xB6200000u, 46u}, // arn -> Latn
- {0xBA200000u, 46u}, // aro -> Latn
+ {0x9E200000u, 45u}, // arh -> Latn
+ {0xB6200000u, 45u}, // arn -> Latn
+ {0xBA200000u, 45u}, // aro -> Latn
{0xC2200000u, 2u}, // arq -> Arab
{0xCA200000u, 2u}, // ars -> Arab
{0xE2200000u, 2u}, // ary -> Arab
{0xE6200000u, 2u}, // arz -> Arab
{0x61730000u, 8u}, // as -> Beng
- {0x82400000u, 46u}, // asa -> Latn
- {0x92400000u, 77u}, // ase -> Sgnw
- {0x9A400000u, 46u}, // asg -> Latn
- {0xBA400000u, 46u}, // aso -> Latn
- {0xCE400000u, 46u}, // ast -> Latn
- {0x82600000u, 46u}, // ata -> Latn
- {0x9A600000u, 46u}, // atg -> Latn
- {0xA6600000u, 46u}, // atj -> Latn
- {0xE2800000u, 46u}, // auy -> Latn
+ {0x82400000u, 45u}, // asa -> Latn
+ {0x92400000u, 78u}, // ase -> Sgnw
+ {0x9A400000u, 45u}, // asg -> Latn
+ {0xBA400000u, 45u}, // aso -> Latn
+ {0xCE400000u, 45u}, // ast -> Latn
+ {0x82600000u, 45u}, // ata -> Latn
+ {0x9A600000u, 45u}, // atg -> Latn
+ {0xA6600000u, 45u}, // atj -> Latn
+ {0xE2800000u, 45u}, // auy -> Latn
{0x61760000u, 18u}, // av -> Cyrl
{0xAEA00000u, 2u}, // avl -> Arab
- {0xB6A00000u, 46u}, // avn -> Latn
- {0xCEA00000u, 46u}, // avt -> Latn
- {0xD2A00000u, 46u}, // avu -> Latn
+ {0xB6A00000u, 45u}, // avn -> Latn
+ {0xCEA00000u, 45u}, // avt -> Latn
+ {0xD2A00000u, 45u}, // avu -> Latn
{0x82C00000u, 19u}, // awa -> Deva
- {0x86C00000u, 46u}, // awb -> Latn
- {0xBAC00000u, 46u}, // awo -> Latn
- {0xDEC00000u, 46u}, // awx -> Latn
- {0x61790000u, 46u}, // ay -> Latn
- {0x87000000u, 46u}, // ayb -> Latn
- {0x617A0000u, 46u}, // az -> Latn
+ {0x86C00000u, 45u}, // awb -> Latn
+ {0xBAC00000u, 45u}, // awo -> Latn
+ {0xDEC00000u, 45u}, // awx -> Latn
+ {0x61790000u, 45u}, // ay -> Latn
+ {0x87000000u, 45u}, // ayb -> Latn
+ {0x617A0000u, 45u}, // az -> Latn
{0x617A4951u, 2u}, // az-IQ -> Arab
{0x617A4952u, 2u}, // az-IR -> Arab
{0x617A5255u, 18u}, // az-RU -> Cyrl
{0x62610000u, 18u}, // ba -> Cyrl
{0xAC010000u, 2u}, // bal -> Arab
- {0xB4010000u, 46u}, // ban -> Latn
+ {0xB4010000u, 45u}, // ban -> Latn
{0xBC010000u, 19u}, // bap -> Deva
- {0xC4010000u, 46u}, // bar -> Latn
- {0xC8010000u, 46u}, // bas -> Latn
- {0xD4010000u, 46u}, // bav -> Latn
+ {0xC4010000u, 45u}, // bar -> Latn
+ {0xC8010000u, 45u}, // bas -> Latn
+ {0xD4010000u, 45u}, // bav -> Latn
{0xDC010000u, 6u}, // bax -> Bamu
- {0x80210000u, 46u}, // bba -> Latn
- {0x84210000u, 46u}, // bbb -> Latn
- {0x88210000u, 46u}, // bbc -> Latn
- {0x8C210000u, 46u}, // bbd -> Latn
- {0xA4210000u, 46u}, // bbj -> Latn
- {0xBC210000u, 46u}, // bbp -> Latn
- {0xC4210000u, 46u}, // bbr -> Latn
- {0x94410000u, 46u}, // bcf -> Latn
- {0x9C410000u, 46u}, // bch -> Latn
- {0xA0410000u, 46u}, // bci -> Latn
- {0xB0410000u, 46u}, // bcm -> Latn
- {0xB4410000u, 46u}, // bcn -> Latn
- {0xB8410000u, 46u}, // bco -> Latn
+ {0x80210000u, 45u}, // bba -> Latn
+ {0x84210000u, 45u}, // bbb -> Latn
+ {0x88210000u, 45u}, // bbc -> Latn
+ {0x8C210000u, 45u}, // bbd -> Latn
+ {0xA4210000u, 45u}, // bbj -> Latn
+ {0xBC210000u, 45u}, // bbp -> Latn
+ {0xC4210000u, 45u}, // bbr -> Latn
+ {0x94410000u, 45u}, // bcf -> Latn
+ {0x9C410000u, 45u}, // bch -> Latn
+ {0xA0410000u, 45u}, // bci -> Latn
+ {0xB0410000u, 45u}, // bcm -> Latn
+ {0xB4410000u, 45u}, // bcn -> Latn
+ {0xB8410000u, 45u}, // bco -> Latn
{0xC0410000u, 21u}, // bcq -> Ethi
- {0xD0410000u, 46u}, // bcu -> Latn
- {0x8C610000u, 46u}, // bdd -> Latn
+ {0xD0410000u, 45u}, // bcu -> Latn
+ {0x8C610000u, 45u}, // bdd -> Latn
{0x62650000u, 18u}, // be -> Cyrl
- {0x94810000u, 46u}, // bef -> Latn
- {0x9C810000u, 46u}, // beh -> Latn
+ {0x94810000u, 45u}, // bef -> Latn
+ {0x9C810000u, 45u}, // beh -> Latn
{0xA4810000u, 2u}, // bej -> Arab
- {0xB0810000u, 46u}, // bem -> Latn
- {0xCC810000u, 46u}, // bet -> Latn
- {0xD8810000u, 46u}, // bew -> Latn
- {0xDC810000u, 46u}, // bex -> Latn
- {0xE4810000u, 46u}, // bez -> Latn
- {0x8CA10000u, 46u}, // bfd -> Latn
- {0xC0A10000u, 85u}, // bfq -> Taml
+ {0xB0810000u, 45u}, // bem -> Latn
+ {0xCC810000u, 45u}, // bet -> Latn
+ {0xD8810000u, 45u}, // bew -> Latn
+ {0xDC810000u, 45u}, // bex -> Latn
+ {0xE4810000u, 45u}, // bez -> Latn
+ {0x8CA10000u, 45u}, // bfd -> Latn
+ {0xC0A10000u, 86u}, // bfq -> Taml
{0xCCA10000u, 2u}, // bft -> Arab
{0xE0A10000u, 19u}, // bfy -> Deva
{0x62670000u, 18u}, // bg -> Cyrl
@@ -241,1235 +244,1239 @@
{0xB4C10000u, 2u}, // bgn -> Arab
{0xDCC10000u, 26u}, // bgx -> Grek
{0x84E10000u, 19u}, // bhb -> Deva
- {0x98E10000u, 46u}, // bhg -> Latn
+ {0x98E10000u, 45u}, // bhg -> Latn
{0xA0E10000u, 19u}, // bhi -> Deva
- {0xACE10000u, 46u}, // bhl -> Latn
+ {0xACE10000u, 45u}, // bhl -> Latn
{0xB8E10000u, 19u}, // bho -> Deva
- {0xE0E10000u, 46u}, // bhy -> Latn
- {0x62690000u, 46u}, // bi -> Latn
- {0x85010000u, 46u}, // bib -> Latn
- {0x99010000u, 46u}, // big -> Latn
- {0xA9010000u, 46u}, // bik -> Latn
- {0xB1010000u, 46u}, // bim -> Latn
- {0xB5010000u, 46u}, // bin -> Latn
- {0xB9010000u, 46u}, // bio -> Latn
- {0xC1010000u, 46u}, // biq -> Latn
- {0x9D210000u, 46u}, // bjh -> Latn
+ {0xE0E10000u, 45u}, // bhy -> Latn
+ {0x62690000u, 45u}, // bi -> Latn
+ {0x85010000u, 45u}, // bib -> Latn
+ {0x99010000u, 45u}, // big -> Latn
+ {0xA9010000u, 45u}, // bik -> Latn
+ {0xB1010000u, 45u}, // bim -> Latn
+ {0xB5010000u, 45u}, // bin -> Latn
+ {0xB9010000u, 45u}, // bio -> Latn
+ {0xC1010000u, 45u}, // biq -> Latn
+ {0x9D210000u, 45u}, // bjh -> Latn
{0xA1210000u, 21u}, // bji -> Ethi
{0xA5210000u, 19u}, // bjj -> Deva
- {0xB5210000u, 46u}, // bjn -> Latn
- {0xB9210000u, 46u}, // bjo -> Latn
- {0xC5210000u, 46u}, // bjr -> Latn
- {0xCD210000u, 46u}, // bjt -> Latn
- {0xE5210000u, 46u}, // bjz -> Latn
- {0x89410000u, 46u}, // bkc -> Latn
- {0xB1410000u, 46u}, // bkm -> Latn
- {0xC1410000u, 46u}, // bkq -> Latn
- {0xD1410000u, 46u}, // bku -> Latn
- {0xD5410000u, 46u}, // bkv -> Latn
- {0xCD610000u, 87u}, // blt -> Tavt
- {0x626D0000u, 46u}, // bm -> Latn
- {0x9D810000u, 46u}, // bmh -> Latn
- {0xA9810000u, 46u}, // bmk -> Latn
- {0xC1810000u, 46u}, // bmq -> Latn
- {0xD1810000u, 46u}, // bmu -> Latn
+ {0xB5210000u, 45u}, // bjn -> Latn
+ {0xB9210000u, 45u}, // bjo -> Latn
+ {0xC5210000u, 45u}, // bjr -> Latn
+ {0xCD210000u, 45u}, // bjt -> Latn
+ {0xE5210000u, 45u}, // bjz -> Latn
+ {0x89410000u, 45u}, // bkc -> Latn
+ {0xB1410000u, 45u}, // bkm -> Latn
+ {0xC1410000u, 45u}, // bkq -> Latn
+ {0xD1410000u, 45u}, // bku -> Latn
+ {0xD5410000u, 45u}, // bkv -> Latn
+ {0x99610000u, 45u}, // blg -> Latn
+ {0xCD610000u, 88u}, // blt -> Tavt
+ {0x626D0000u, 45u}, // bm -> Latn
+ {0x9D810000u, 45u}, // bmh -> Latn
+ {0xA9810000u, 45u}, // bmk -> Latn
+ {0xC1810000u, 45u}, // bmq -> Latn
+ {0xD1810000u, 45u}, // bmu -> Latn
{0x626E0000u, 8u}, // bn -> Beng
- {0x99A10000u, 46u}, // bng -> Latn
- {0xB1A10000u, 46u}, // bnm -> Latn
- {0xBDA10000u, 46u}, // bnp -> Latn
- {0x626F0000u, 92u}, // bo -> Tibt
- {0xA5C10000u, 46u}, // boj -> Latn
- {0xB1C10000u, 46u}, // bom -> Latn
- {0xB5C10000u, 46u}, // bon -> Latn
+ {0x99A10000u, 45u}, // bng -> Latn
+ {0xB1A10000u, 45u}, // bnm -> Latn
+ {0xBDA10000u, 45u}, // bnp -> Latn
+ {0x626F0000u, 93u}, // bo -> Tibt
+ {0xA5C10000u, 45u}, // boj -> Latn
+ {0xB1C10000u, 45u}, // bom -> Latn
+ {0xB5C10000u, 45u}, // bon -> Latn
{0xE1E10000u, 8u}, // bpy -> Beng
- {0x8A010000u, 46u}, // bqc -> Latn
+ {0x8A010000u, 45u}, // bqc -> Latn
{0xA2010000u, 2u}, // bqi -> Arab
- {0xBE010000u, 46u}, // bqp -> Latn
- {0xD6010000u, 46u}, // bqv -> Latn
- {0x62720000u, 46u}, // br -> Latn
+ {0xBE010000u, 45u}, // bqp -> Latn
+ {0xD6010000u, 45u}, // bqv -> Latn
+ {0x62720000u, 45u}, // br -> Latn
{0x82210000u, 19u}, // bra -> Deva
{0x9E210000u, 2u}, // brh -> Arab
{0xDE210000u, 19u}, // brx -> Deva
- {0xE6210000u, 46u}, // brz -> Latn
- {0x62730000u, 46u}, // bs -> Latn
- {0xA6410000u, 46u}, // bsj -> Latn
+ {0xE6210000u, 45u}, // brz -> Latn
+ {0x62730000u, 45u}, // bs -> Latn
+ {0xA6410000u, 45u}, // bsj -> Latn
{0xC2410000u, 7u}, // bsq -> Bass
- {0xCA410000u, 46u}, // bss -> Latn
+ {0xCA410000u, 45u}, // bss -> Latn
{0xCE410000u, 21u}, // bst -> Ethi
- {0xBA610000u, 46u}, // bto -> Latn
- {0xCE610000u, 46u}, // btt -> Latn
+ {0xBA610000u, 45u}, // bto -> Latn
+ {0xCE610000u, 45u}, // btt -> Latn
{0xD6610000u, 19u}, // btv -> Deva
{0x82810000u, 18u}, // bua -> Cyrl
- {0x8A810000u, 46u}, // buc -> Latn
- {0x8E810000u, 46u}, // bud -> Latn
- {0x9A810000u, 46u}, // bug -> Latn
- {0xAA810000u, 46u}, // buk -> Latn
- {0xB2810000u, 46u}, // bum -> Latn
- {0xBA810000u, 46u}, // buo -> Latn
- {0xCA810000u, 46u}, // bus -> Latn
- {0xD2810000u, 46u}, // buu -> Latn
- {0x86A10000u, 46u}, // bvb -> Latn
- {0x8EC10000u, 46u}, // bwd -> Latn
- {0xC6C10000u, 46u}, // bwr -> Latn
- {0x9EE10000u, 46u}, // bxh -> Latn
- {0x93010000u, 46u}, // bye -> Latn
+ {0x8A810000u, 45u}, // buc -> Latn
+ {0x8E810000u, 45u}, // bud -> Latn
+ {0x9A810000u, 45u}, // bug -> Latn
+ {0xAA810000u, 45u}, // buk -> Latn
+ {0xB2810000u, 45u}, // bum -> Latn
+ {0xBA810000u, 45u}, // buo -> Latn
+ {0xCA810000u, 45u}, // bus -> Latn
+ {0xD2810000u, 45u}, // buu -> Latn
+ {0x86A10000u, 45u}, // bvb -> Latn
+ {0x8EC10000u, 45u}, // bwd -> Latn
+ {0xC6C10000u, 45u}, // bwr -> Latn
+ {0x9EE10000u, 45u}, // bxh -> Latn
+ {0x93010000u, 45u}, // bye -> Latn
{0xB7010000u, 21u}, // byn -> Ethi
- {0xC7010000u, 46u}, // byr -> Latn
- {0xCB010000u, 46u}, // bys -> Latn
- {0xD7010000u, 46u}, // byv -> Latn
- {0xDF010000u, 46u}, // byx -> Latn
- {0x83210000u, 46u}, // bza -> Latn
- {0x93210000u, 46u}, // bze -> Latn
- {0x97210000u, 46u}, // bzf -> Latn
- {0x9F210000u, 46u}, // bzh -> Latn
- {0xDB210000u, 46u}, // bzw -> Latn
- {0x63610000u, 46u}, // ca -> Latn
- {0x8C020000u, 46u}, // cad -> Latn
- {0xB4020000u, 46u}, // can -> Latn
- {0xA4220000u, 46u}, // cbj -> Latn
- {0x9C420000u, 46u}, // cch -> Latn
+ {0xC7010000u, 45u}, // byr -> Latn
+ {0xCB010000u, 45u}, // bys -> Latn
+ {0xD7010000u, 45u}, // byv -> Latn
+ {0xDF010000u, 45u}, // byx -> Latn
+ {0x83210000u, 45u}, // bza -> Latn
+ {0x93210000u, 45u}, // bze -> Latn
+ {0x97210000u, 45u}, // bzf -> Latn
+ {0x9F210000u, 45u}, // bzh -> Latn
+ {0xDB210000u, 45u}, // bzw -> Latn
+ {0x63610000u, 45u}, // ca -> Latn
+ {0x8C020000u, 45u}, // cad -> Latn
+ {0xB4020000u, 45u}, // can -> Latn
+ {0xA4220000u, 45u}, // cbj -> Latn
+ {0x9C420000u, 45u}, // cch -> Latn
{0xBC420000u, 10u}, // ccp -> Cakm
{0x63650000u, 18u}, // ce -> Cyrl
- {0x84820000u, 46u}, // ceb -> Latn
- {0x80A20000u, 46u}, // cfa -> Latn
- {0x98C20000u, 46u}, // cgg -> Latn
- {0x63680000u, 46u}, // ch -> Latn
- {0xA8E20000u, 46u}, // chk -> Latn
+ {0x84820000u, 45u}, // ceb -> Latn
+ {0x80A20000u, 45u}, // cfa -> Latn
+ {0x98C20000u, 45u}, // cgg -> Latn
+ {0x63680000u, 45u}, // ch -> Latn
+ {0xA8E20000u, 45u}, // chk -> Latn
{0xB0E20000u, 18u}, // chm -> Cyrl
- {0xB8E20000u, 46u}, // cho -> Latn
- {0xBCE20000u, 46u}, // chp -> Latn
+ {0xB8E20000u, 45u}, // cho -> Latn
+ {0xBCE20000u, 45u}, // chp -> Latn
{0xC4E20000u, 14u}, // chr -> Cher
- {0x89020000u, 46u}, // cic -> Latn
+ {0x89020000u, 45u}, // cic -> Latn
{0x81220000u, 2u}, // cja -> Arab
{0xB1220000u, 13u}, // cjm -> Cham
- {0xD5220000u, 46u}, // cjv -> Latn
+ {0xD5220000u, 45u}, // cjv -> Latn
{0x85420000u, 2u}, // ckb -> Arab
- {0xAD420000u, 46u}, // ckl -> Latn
- {0xB9420000u, 46u}, // cko -> Latn
- {0xE1420000u, 46u}, // cky -> Latn
- {0x81620000u, 46u}, // cla -> Latn
- {0x91820000u, 46u}, // cme -> Latn
- {0x99820000u, 81u}, // cmg -> Soyo
- {0x636F0000u, 46u}, // co -> Latn
+ {0xAD420000u, 45u}, // ckl -> Latn
+ {0xB9420000u, 45u}, // cko -> Latn
+ {0xE1420000u, 45u}, // cky -> Latn
+ {0x81620000u, 45u}, // cla -> Latn
+ {0x91820000u, 45u}, // cme -> Latn
+ {0x99820000u, 82u}, // cmg -> Soyo
+ {0x636F0000u, 45u}, // co -> Latn
{0xBDC20000u, 16u}, // cop -> Copt
- {0xC9E20000u, 46u}, // cps -> Latn
+ {0xC9E20000u, 45u}, // cps -> Latn
{0x63720000u, 11u}, // cr -> Cans
{0x9E220000u, 18u}, // crh -> Cyrl
{0xA6220000u, 11u}, // crj -> Cans
{0xAA220000u, 11u}, // crk -> Cans
{0xAE220000u, 11u}, // crl -> Cans
{0xB2220000u, 11u}, // crm -> Cans
- {0xCA220000u, 46u}, // crs -> Latn
- {0x63730000u, 46u}, // cs -> Latn
- {0x86420000u, 46u}, // csb -> Latn
+ {0xCA220000u, 45u}, // crs -> Latn
+ {0x63730000u, 45u}, // cs -> Latn
+ {0x86420000u, 45u}, // csb -> Latn
{0xDA420000u, 11u}, // csw -> Cans
{0x8E620000u, 68u}, // ctd -> Pauc
{0x63750000u, 18u}, // cu -> Cyrl
{0x63760000u, 18u}, // cv -> Cyrl
- {0x63790000u, 46u}, // cy -> Latn
- {0x64610000u, 46u}, // da -> Latn
- {0x8C030000u, 46u}, // dad -> Latn
- {0x94030000u, 46u}, // daf -> Latn
- {0x98030000u, 46u}, // dag -> Latn
- {0x9C030000u, 46u}, // dah -> Latn
- {0xA8030000u, 46u}, // dak -> Latn
+ {0x63790000u, 45u}, // cy -> Latn
+ {0x64610000u, 45u}, // da -> Latn
+ {0x8C030000u, 45u}, // dad -> Latn
+ {0x94030000u, 45u}, // daf -> Latn
+ {0x98030000u, 45u}, // dag -> Latn
+ {0x9C030000u, 45u}, // dah -> Latn
+ {0xA8030000u, 45u}, // dak -> Latn
{0xC4030000u, 18u}, // dar -> Cyrl
- {0xD4030000u, 46u}, // dav -> Latn
- {0x8C230000u, 46u}, // dbd -> Latn
- {0xC0230000u, 46u}, // dbq -> Latn
+ {0xD4030000u, 45u}, // dav -> Latn
+ {0x8C230000u, 45u}, // dbd -> Latn
+ {0xC0230000u, 45u}, // dbq -> Latn
{0x88430000u, 2u}, // dcc -> Arab
- {0xB4630000u, 46u}, // ddn -> Latn
- {0x64650000u, 46u}, // de -> Latn
- {0x8C830000u, 46u}, // ded -> Latn
- {0xB4830000u, 46u}, // den -> Latn
- {0x80C30000u, 46u}, // dga -> Latn
- {0x9CC30000u, 46u}, // dgh -> Latn
- {0xA0C30000u, 46u}, // dgi -> Latn
+ {0xB4630000u, 45u}, // ddn -> Latn
+ {0x64650000u, 45u}, // de -> Latn
+ {0x8C830000u, 45u}, // ded -> Latn
+ {0xB4830000u, 45u}, // den -> Latn
+ {0x80C30000u, 45u}, // dga -> Latn
+ {0x9CC30000u, 45u}, // dgh -> Latn
+ {0xA0C30000u, 45u}, // dgi -> Latn
{0xACC30000u, 2u}, // dgl -> Arab
- {0xC4C30000u, 46u}, // dgr -> Latn
- {0xE4C30000u, 46u}, // dgz -> Latn
- {0x81030000u, 46u}, // dia -> Latn
- {0x91230000u, 46u}, // dje -> Latn
- {0x95830000u, 54u}, // dmf -> Medf
- {0xA5A30000u, 46u}, // dnj -> Latn
- {0x85C30000u, 46u}, // dob -> Latn
+ {0xC4C30000u, 45u}, // dgr -> Latn
+ {0xE4C30000u, 45u}, // dgz -> Latn
+ {0x81030000u, 45u}, // dia -> Latn
+ {0x91230000u, 45u}, // dje -> Latn
+ {0x95830000u, 53u}, // dmf -> Medf
+ {0xA5A30000u, 45u}, // dnj -> Latn
+ {0x85C30000u, 45u}, // dob -> Latn
{0xA1C30000u, 19u}, // doi -> Deva
- {0xBDC30000u, 46u}, // dop -> Latn
- {0xD9C30000u, 46u}, // dow -> Latn
- {0x9E230000u, 57u}, // drh -> Mong
- {0xA2230000u, 46u}, // dri -> Latn
+ {0xBDC30000u, 45u}, // dop -> Latn
+ {0xD9C30000u, 45u}, // dow -> Latn
+ {0x9E230000u, 56u}, // drh -> Mong
+ {0xA2230000u, 45u}, // dri -> Latn
{0xCA230000u, 21u}, // drs -> Ethi
- {0x86430000u, 46u}, // dsb -> Latn
- {0xB2630000u, 46u}, // dtm -> Latn
- {0xBE630000u, 46u}, // dtp -> Latn
- {0xCA630000u, 46u}, // dts -> Latn
+ {0x86430000u, 45u}, // dsb -> Latn
+ {0xB2630000u, 45u}, // dtm -> Latn
+ {0xBE630000u, 45u}, // dtp -> Latn
+ {0xCA630000u, 45u}, // dts -> Latn
{0xE2630000u, 19u}, // dty -> Deva
- {0x82830000u, 46u}, // dua -> Latn
- {0x8A830000u, 46u}, // duc -> Latn
- {0x8E830000u, 46u}, // dud -> Latn
- {0x9A830000u, 46u}, // dug -> Latn
- {0x64760000u, 90u}, // dv -> Thaa
- {0x82A30000u, 46u}, // dva -> Latn
- {0xDAC30000u, 46u}, // dww -> Latn
- {0xBB030000u, 46u}, // dyo -> Latn
- {0xD3030000u, 46u}, // dyu -> Latn
- {0x647A0000u, 92u}, // dz -> Tibt
- {0x9B230000u, 46u}, // dzg -> Latn
- {0xD0240000u, 46u}, // ebu -> Latn
- {0x65650000u, 46u}, // ee -> Latn
- {0xA0A40000u, 46u}, // efi -> Latn
- {0xACC40000u, 46u}, // egl -> Latn
+ {0x82830000u, 45u}, // dua -> Latn
+ {0x8A830000u, 45u}, // duc -> Latn
+ {0x8E830000u, 45u}, // dud -> Latn
+ {0x9A830000u, 45u}, // dug -> Latn
+ {0x64760000u, 91u}, // dv -> Thaa
+ {0x82A30000u, 45u}, // dva -> Latn
+ {0xDAC30000u, 45u}, // dww -> Latn
+ {0xBB030000u, 45u}, // dyo -> Latn
+ {0xD3030000u, 45u}, // dyu -> Latn
+ {0x647A0000u, 93u}, // dz -> Tibt
+ {0x9B230000u, 45u}, // dzg -> Latn
+ {0xD0240000u, 45u}, // ebu -> Latn
+ {0x65650000u, 45u}, // ee -> Latn
+ {0xA0A40000u, 45u}, // efi -> Latn
+ {0xACC40000u, 45u}, // egl -> Latn
{0xE0C40000u, 20u}, // egy -> Egyp
- {0x81440000u, 46u}, // eka -> Latn
- {0xE1440000u, 37u}, // eky -> Kali
+ {0x81440000u, 45u}, // eka -> Latn
+ {0xE1440000u, 36u}, // eky -> Kali
{0x656C0000u, 26u}, // el -> Grek
- {0x81840000u, 46u}, // ema -> Latn
- {0xA1840000u, 46u}, // emi -> Latn
- {0x656E0000u, 46u}, // en -> Latn
- {0x656E5841u, 99u}, // en-XA -> ~~~A
- {0xB5A40000u, 46u}, // enn -> Latn
- {0xC1A40000u, 46u}, // enq -> Latn
- {0x656F0000u, 46u}, // eo -> Latn
- {0xA2240000u, 46u}, // eri -> Latn
- {0x65730000u, 46u}, // es -> Latn
+ {0x81840000u, 45u}, // ema -> Latn
+ {0xA1840000u, 45u}, // emi -> Latn
+ {0x656E0000u, 45u}, // en -> Latn
+ {0x656E5841u, 102u}, // en-XA -> ~~~A
+ {0xB5A40000u, 45u}, // enn -> Latn
+ {0xC1A40000u, 45u}, // enq -> Latn
+ {0x656F0000u, 45u}, // eo -> Latn
+ {0xA2240000u, 45u}, // eri -> Latn
+ {0x65730000u, 45u}, // es -> Latn
{0x9A440000u, 24u}, // esg -> Gonm
- {0xD2440000u, 46u}, // esu -> Latn
- {0x65740000u, 46u}, // et -> Latn
- {0xC6640000u, 46u}, // etr -> Latn
- {0xCE640000u, 35u}, // ett -> Ital
- {0xD2640000u, 46u}, // etu -> Latn
- {0xDE640000u, 46u}, // etx -> Latn
- {0x65750000u, 46u}, // eu -> Latn
- {0xBAC40000u, 46u}, // ewo -> Latn
- {0xCEE40000u, 46u}, // ext -> Latn
- {0x83240000u, 46u}, // eza -> Latn
+ {0xD2440000u, 45u}, // esu -> Latn
+ {0x65740000u, 45u}, // et -> Latn
+ {0xC6640000u, 45u}, // etr -> Latn
+ {0xCE640000u, 34u}, // ett -> Ital
+ {0xD2640000u, 45u}, // etu -> Latn
+ {0xDE640000u, 45u}, // etx -> Latn
+ {0x65750000u, 45u}, // eu -> Latn
+ {0xBAC40000u, 45u}, // ewo -> Latn
+ {0xCEE40000u, 45u}, // ext -> Latn
+ {0x83240000u, 45u}, // eza -> Latn
{0x66610000u, 2u}, // fa -> Arab
- {0x80050000u, 46u}, // faa -> Latn
- {0x84050000u, 46u}, // fab -> Latn
- {0x98050000u, 46u}, // fag -> Latn
- {0xA0050000u, 46u}, // fai -> Latn
- {0xB4050000u, 46u}, // fan -> Latn
- {0x66660000u, 46u}, // ff -> Latn
- {0xA0A50000u, 46u}, // ffi -> Latn
- {0xB0A50000u, 46u}, // ffm -> Latn
- {0x66690000u, 46u}, // fi -> Latn
+ {0x80050000u, 45u}, // faa -> Latn
+ {0x84050000u, 45u}, // fab -> Latn
+ {0x98050000u, 45u}, // fag -> Latn
+ {0xA0050000u, 45u}, // fai -> Latn
+ {0xB4050000u, 45u}, // fan -> Latn
+ {0x66660000u, 45u}, // ff -> Latn
+ {0xA0A50000u, 45u}, // ffi -> Latn
+ {0xB0A50000u, 45u}, // ffm -> Latn
+ {0x66690000u, 45u}, // fi -> Latn
{0x81050000u, 2u}, // fia -> Arab
- {0xAD050000u, 46u}, // fil -> Latn
- {0xCD050000u, 46u}, // fit -> Latn
- {0x666A0000u, 46u}, // fj -> Latn
- {0xC5650000u, 46u}, // flr -> Latn
- {0xBD850000u, 46u}, // fmp -> Latn
- {0x666F0000u, 46u}, // fo -> Latn
- {0x8DC50000u, 46u}, // fod -> Latn
- {0xB5C50000u, 46u}, // fon -> Latn
- {0xC5C50000u, 46u}, // for -> Latn
- {0x91E50000u, 46u}, // fpe -> Latn
- {0xCA050000u, 46u}, // fqs -> Latn
- {0x66720000u, 46u}, // fr -> Latn
- {0x8A250000u, 46u}, // frc -> Latn
- {0xBE250000u, 46u}, // frp -> Latn
- {0xC6250000u, 46u}, // frr -> Latn
- {0xCA250000u, 46u}, // frs -> Latn
+ {0xAD050000u, 45u}, // fil -> Latn
+ {0xCD050000u, 45u}, // fit -> Latn
+ {0x666A0000u, 45u}, // fj -> Latn
+ {0xC5650000u, 45u}, // flr -> Latn
+ {0xBD850000u, 45u}, // fmp -> Latn
+ {0x666F0000u, 45u}, // fo -> Latn
+ {0x8DC50000u, 45u}, // fod -> Latn
+ {0xB5C50000u, 45u}, // fon -> Latn
+ {0xC5C50000u, 45u}, // for -> Latn
+ {0x91E50000u, 45u}, // fpe -> Latn
+ {0xCA050000u, 45u}, // fqs -> Latn
+ {0x66720000u, 45u}, // fr -> Latn
+ {0x8A250000u, 45u}, // frc -> Latn
+ {0xBE250000u, 45u}, // frp -> Latn
+ {0xC6250000u, 45u}, // frr -> Latn
+ {0xCA250000u, 45u}, // frs -> Latn
{0x86850000u, 2u}, // fub -> Arab
- {0x8E850000u, 46u}, // fud -> Latn
- {0x92850000u, 46u}, // fue -> Latn
- {0x96850000u, 46u}, // fuf -> Latn
- {0x9E850000u, 46u}, // fuh -> Latn
- {0xC2850000u, 46u}, // fuq -> Latn
- {0xC6850000u, 46u}, // fur -> Latn
- {0xD6850000u, 46u}, // fuv -> Latn
- {0xE2850000u, 46u}, // fuy -> Latn
- {0xC6A50000u, 46u}, // fvr -> Latn
- {0x66790000u, 46u}, // fy -> Latn
- {0x67610000u, 46u}, // ga -> Latn
- {0x80060000u, 46u}, // gaa -> Latn
- {0x94060000u, 46u}, // gaf -> Latn
- {0x98060000u, 46u}, // gag -> Latn
- {0x9C060000u, 46u}, // gah -> Latn
- {0xA4060000u, 46u}, // gaj -> Latn
- {0xB0060000u, 46u}, // gam -> Latn
+ {0x8E850000u, 45u}, // fud -> Latn
+ {0x92850000u, 45u}, // fue -> Latn
+ {0x96850000u, 45u}, // fuf -> Latn
+ {0x9E850000u, 45u}, // fuh -> Latn
+ {0xC2850000u, 45u}, // fuq -> Latn
+ {0xC6850000u, 45u}, // fur -> Latn
+ {0xD6850000u, 45u}, // fuv -> Latn
+ {0xE2850000u, 45u}, // fuy -> Latn
+ {0xC6A50000u, 45u}, // fvr -> Latn
+ {0x66790000u, 45u}, // fy -> Latn
+ {0x67610000u, 45u}, // ga -> Latn
+ {0x80060000u, 45u}, // gaa -> Latn
+ {0x94060000u, 45u}, // gaf -> Latn
+ {0x98060000u, 45u}, // gag -> Latn
+ {0x9C060000u, 45u}, // gah -> Latn
+ {0xA4060000u, 45u}, // gaj -> Latn
+ {0xB0060000u, 45u}, // gam -> Latn
{0xB4060000u, 29u}, // gan -> Hans
- {0xD8060000u, 46u}, // gaw -> Latn
- {0xE0060000u, 46u}, // gay -> Latn
- {0x80260000u, 46u}, // gba -> Latn
- {0x94260000u, 46u}, // gbf -> Latn
+ {0xD8060000u, 45u}, // gaw -> Latn
+ {0xE0060000u, 45u}, // gay -> Latn
+ {0x80260000u, 45u}, // gba -> Latn
+ {0x94260000u, 45u}, // gbf -> Latn
{0xB0260000u, 19u}, // gbm -> Deva
- {0xE0260000u, 46u}, // gby -> Latn
+ {0xE0260000u, 45u}, // gby -> Latn
{0xE4260000u, 2u}, // gbz -> Arab
- {0xC4460000u, 46u}, // gcr -> Latn
- {0x67640000u, 46u}, // gd -> Latn
- {0x90660000u, 46u}, // gde -> Latn
- {0xB4660000u, 46u}, // gdn -> Latn
- {0xC4660000u, 46u}, // gdr -> Latn
- {0x84860000u, 46u}, // geb -> Latn
- {0xA4860000u, 46u}, // gej -> Latn
- {0xAC860000u, 46u}, // gel -> Latn
+ {0xC4460000u, 45u}, // gcr -> Latn
+ {0x67640000u, 45u}, // gd -> Latn
+ {0x90660000u, 45u}, // gde -> Latn
+ {0xB4660000u, 45u}, // gdn -> Latn
+ {0xC4660000u, 45u}, // gdr -> Latn
+ {0x84860000u, 45u}, // geb -> Latn
+ {0xA4860000u, 45u}, // gej -> Latn
+ {0xAC860000u, 45u}, // gel -> Latn
{0xE4860000u, 21u}, // gez -> Ethi
- {0xA8A60000u, 46u}, // gfk -> Latn
+ {0xA8A60000u, 45u}, // gfk -> Latn
{0xB4C60000u, 19u}, // ggn -> Deva
- {0xC8E60000u, 46u}, // ghs -> Latn
- {0xAD060000u, 46u}, // gil -> Latn
- {0xB1060000u, 46u}, // gim -> Latn
+ {0xC8E60000u, 45u}, // ghs -> Latn
+ {0xAD060000u, 45u}, // gil -> Latn
+ {0xB1060000u, 45u}, // gim -> Latn
{0xA9260000u, 2u}, // gjk -> Arab
- {0xB5260000u, 46u}, // gjn -> Latn
+ {0xB5260000u, 45u}, // gjn -> Latn
{0xD1260000u, 2u}, // gju -> Arab
- {0xB5460000u, 46u}, // gkn -> Latn
- {0xBD460000u, 46u}, // gkp -> Latn
- {0x676C0000u, 46u}, // gl -> Latn
+ {0xB5460000u, 45u}, // gkn -> Latn
+ {0xBD460000u, 45u}, // gkp -> Latn
+ {0x676C0000u, 45u}, // gl -> Latn
{0xA9660000u, 2u}, // glk -> Arab
- {0xB1860000u, 46u}, // gmm -> Latn
+ {0xB1860000u, 45u}, // gmm -> Latn
{0xD5860000u, 21u}, // gmv -> Ethi
- {0x676E0000u, 46u}, // gn -> Latn
- {0x8DA60000u, 46u}, // gnd -> Latn
- {0x99A60000u, 46u}, // gng -> Latn
- {0x8DC60000u, 46u}, // god -> Latn
+ {0x676E0000u, 45u}, // gn -> Latn
+ {0x8DA60000u, 45u}, // gnd -> Latn
+ {0x99A60000u, 45u}, // gng -> Latn
+ {0x8DC60000u, 45u}, // god -> Latn
{0x95C60000u, 21u}, // gof -> Ethi
- {0xA1C60000u, 46u}, // goi -> Latn
+ {0xA1C60000u, 45u}, // goi -> Latn
{0xB1C60000u, 19u}, // gom -> Deva
- {0xB5C60000u, 88u}, // gon -> Telu
- {0xC5C60000u, 46u}, // gor -> Latn
- {0xC9C60000u, 46u}, // gos -> Latn
+ {0xB5C60000u, 89u}, // gon -> Telu
+ {0xC5C60000u, 45u}, // gor -> Latn
+ {0xC9C60000u, 45u}, // gos -> Latn
{0xCDC60000u, 25u}, // got -> Goth
- {0x86260000u, 46u}, // grb -> Latn
+ {0x86260000u, 45u}, // grb -> Latn
{0x8A260000u, 17u}, // grc -> Cprt
{0xCE260000u, 8u}, // grt -> Beng
- {0xDA260000u, 46u}, // grw -> Latn
- {0xDA460000u, 46u}, // gsw -> Latn
+ {0xDA260000u, 45u}, // grw -> Latn
+ {0xDA460000u, 45u}, // gsw -> Latn
{0x67750000u, 27u}, // gu -> Gujr
- {0x86860000u, 46u}, // gub -> Latn
- {0x8A860000u, 46u}, // guc -> Latn
- {0x8E860000u, 46u}, // gud -> Latn
- {0xC6860000u, 46u}, // gur -> Latn
- {0xDA860000u, 46u}, // guw -> Latn
- {0xDE860000u, 46u}, // gux -> Latn
- {0xE6860000u, 46u}, // guz -> Latn
- {0x67760000u, 46u}, // gv -> Latn
- {0x96A60000u, 46u}, // gvf -> Latn
+ {0x86860000u, 45u}, // gub -> Latn
+ {0x8A860000u, 45u}, // guc -> Latn
+ {0x8E860000u, 45u}, // gud -> Latn
+ {0xC6860000u, 45u}, // gur -> Latn
+ {0xDA860000u, 45u}, // guw -> Latn
+ {0xDE860000u, 45u}, // gux -> Latn
+ {0xE6860000u, 45u}, // guz -> Latn
+ {0x67760000u, 45u}, // gv -> Latn
+ {0x96A60000u, 45u}, // gvf -> Latn
{0xC6A60000u, 19u}, // gvr -> Deva
- {0xCAA60000u, 46u}, // gvs -> Latn
+ {0xCAA60000u, 45u}, // gvs -> Latn
{0x8AC60000u, 2u}, // gwc -> Arab
- {0xA2C60000u, 46u}, // gwi -> Latn
+ {0xA2C60000u, 45u}, // gwi -> Latn
{0xCEC60000u, 2u}, // gwt -> Arab
- {0xA3060000u, 46u}, // gyi -> Latn
- {0x68610000u, 46u}, // ha -> Latn
+ {0xA3060000u, 45u}, // gyi -> Latn
+ {0x68610000u, 45u}, // ha -> Latn
{0x6861434Du, 2u}, // ha-CM -> Arab
{0x68615344u, 2u}, // ha-SD -> Arab
- {0x98070000u, 46u}, // hag -> Latn
+ {0x98070000u, 45u}, // hag -> Latn
{0xA8070000u, 29u}, // hak -> Hans
- {0xB0070000u, 46u}, // ham -> Latn
- {0xD8070000u, 46u}, // haw -> Latn
+ {0xB0070000u, 45u}, // ham -> Latn
+ {0xD8070000u, 45u}, // haw -> Latn
{0xE4070000u, 2u}, // haz -> Arab
- {0x84270000u, 46u}, // hbb -> Latn
+ {0x84270000u, 45u}, // hbb -> Latn
{0xE0670000u, 21u}, // hdy -> Ethi
{0x68650000u, 31u}, // he -> Hebr
- {0xE0E70000u, 46u}, // hhy -> Latn
+ {0xE0E70000u, 45u}, // hhy -> Latn
{0x68690000u, 19u}, // hi -> Deva
- {0x81070000u, 46u}, // hia -> Latn
- {0x95070000u, 46u}, // hif -> Latn
- {0x99070000u, 46u}, // hig -> Latn
- {0x9D070000u, 46u}, // hih -> Latn
- {0xAD070000u, 46u}, // hil -> Latn
- {0x81670000u, 46u}, // hla -> Latn
+ {0x81070000u, 45u}, // hia -> Latn
+ {0x95070000u, 45u}, // hif -> Latn
+ {0x99070000u, 45u}, // hig -> Latn
+ {0x9D070000u, 45u}, // hih -> Latn
+ {0xAD070000u, 45u}, // hil -> Latn
+ {0x81670000u, 45u}, // hla -> Latn
{0xD1670000u, 32u}, // hlu -> Hluw
{0x8D870000u, 71u}, // hmd -> Plrd
- {0xCD870000u, 46u}, // hmt -> Latn
+ {0xCD870000u, 45u}, // hmt -> Latn
{0x8DA70000u, 2u}, // hnd -> Arab
{0x91A70000u, 19u}, // hne -> Deva
- {0xA5A70000u, 33u}, // hnj -> Hmng
- {0xB5A70000u, 46u}, // hnn -> Latn
+ {0xA5A70000u, 33u}, // hnj -> Hmnp
+ {0xB5A70000u, 45u}, // hnn -> Latn
{0xB9A70000u, 2u}, // hno -> Arab
- {0x686F0000u, 46u}, // ho -> Latn
+ {0x686F0000u, 45u}, // ho -> Latn
{0x89C70000u, 19u}, // hoc -> Deva
{0xA5C70000u, 19u}, // hoj -> Deva
- {0xCDC70000u, 46u}, // hot -> Latn
- {0x68720000u, 46u}, // hr -> Latn
- {0x86470000u, 46u}, // hsb -> Latn
+ {0xCDC70000u, 45u}, // hot -> Latn
+ {0x68720000u, 45u}, // hr -> Latn
+ {0x86470000u, 45u}, // hsb -> Latn
{0xB6470000u, 29u}, // hsn -> Hans
- {0x68740000u, 46u}, // ht -> Latn
- {0x68750000u, 46u}, // hu -> Latn
- {0xA2870000u, 46u}, // hui -> Latn
+ {0x68740000u, 45u}, // ht -> Latn
+ {0x68750000u, 45u}, // hu -> Latn
+ {0xA2870000u, 45u}, // hui -> Latn
{0x68790000u, 4u}, // hy -> Armn
- {0x687A0000u, 46u}, // hz -> Latn
- {0x69610000u, 46u}, // ia -> Latn
- {0xB4080000u, 46u}, // ian -> Latn
- {0xC4080000u, 46u}, // iar -> Latn
- {0x80280000u, 46u}, // iba -> Latn
- {0x84280000u, 46u}, // ibb -> Latn
- {0xE0280000u, 46u}, // iby -> Latn
- {0x80480000u, 46u}, // ica -> Latn
- {0x9C480000u, 46u}, // ich -> Latn
- {0x69640000u, 46u}, // id -> Latn
- {0x8C680000u, 46u}, // idd -> Latn
- {0xA0680000u, 46u}, // idi -> Latn
- {0xD0680000u, 46u}, // idu -> Latn
- {0x90A80000u, 46u}, // ife -> Latn
- {0x69670000u, 46u}, // ig -> Latn
- {0x84C80000u, 46u}, // igb -> Latn
- {0x90C80000u, 46u}, // ige -> Latn
- {0x69690000u, 98u}, // ii -> Yiii
- {0xA5280000u, 46u}, // ijj -> Latn
- {0x696B0000u, 46u}, // ik -> Latn
- {0xA9480000u, 46u}, // ikk -> Latn
- {0xCD480000u, 46u}, // ikt -> Latn
- {0xD9480000u, 46u}, // ikw -> Latn
- {0xDD480000u, 46u}, // ikx -> Latn
- {0xB9680000u, 46u}, // ilo -> Latn
- {0xB9880000u, 46u}, // imo -> Latn
- {0x696E0000u, 46u}, // in -> Latn
+ {0x687A0000u, 45u}, // hz -> Latn
+ {0x69610000u, 45u}, // ia -> Latn
+ {0xB4080000u, 45u}, // ian -> Latn
+ {0xC4080000u, 45u}, // iar -> Latn
+ {0x80280000u, 45u}, // iba -> Latn
+ {0x84280000u, 45u}, // ibb -> Latn
+ {0xE0280000u, 45u}, // iby -> Latn
+ {0x80480000u, 45u}, // ica -> Latn
+ {0x9C480000u, 45u}, // ich -> Latn
+ {0x69640000u, 45u}, // id -> Latn
+ {0x8C680000u, 45u}, // idd -> Latn
+ {0xA0680000u, 45u}, // idi -> Latn
+ {0xD0680000u, 45u}, // idu -> Latn
+ {0x90A80000u, 45u}, // ife -> Latn
+ {0x69670000u, 45u}, // ig -> Latn
+ {0x84C80000u, 45u}, // igb -> Latn
+ {0x90C80000u, 45u}, // ige -> Latn
+ {0x69690000u, 101u}, // ii -> Yiii
+ {0xA5280000u, 45u}, // ijj -> Latn
+ {0x696B0000u, 45u}, // ik -> Latn
+ {0xA9480000u, 45u}, // ikk -> Latn
+ {0xCD480000u, 45u}, // ikt -> Latn
+ {0xD9480000u, 45u}, // ikw -> Latn
+ {0xDD480000u, 45u}, // ikx -> Latn
+ {0xB9680000u, 45u}, // ilo -> Latn
+ {0xB9880000u, 45u}, // imo -> Latn
+ {0x696E0000u, 45u}, // in -> Latn
{0x9DA80000u, 18u}, // inh -> Cyrl
- {0x696F0000u, 46u}, // io -> Latn
- {0xD1C80000u, 46u}, // iou -> Latn
- {0xA2280000u, 46u}, // iri -> Latn
- {0x69730000u, 46u}, // is -> Latn
- {0x69740000u, 46u}, // it -> Latn
+ {0x696F0000u, 45u}, // io -> Latn
+ {0xD1C80000u, 45u}, // iou -> Latn
+ {0xA2280000u, 45u}, // iri -> Latn
+ {0x69730000u, 45u}, // is -> Latn
+ {0x69740000u, 45u}, // it -> Latn
{0x69750000u, 11u}, // iu -> Cans
{0x69770000u, 31u}, // iw -> Hebr
- {0xB2C80000u, 46u}, // iwm -> Latn
- {0xCAC80000u, 46u}, // iws -> Latn
- {0x9F280000u, 46u}, // izh -> Latn
- {0xA3280000u, 46u}, // izi -> Latn
- {0x6A610000u, 36u}, // ja -> Jpan
- {0x84090000u, 46u}, // jab -> Latn
- {0xB0090000u, 46u}, // jam -> Latn
- {0xC4090000u, 46u}, // jar -> Latn
- {0xB8290000u, 46u}, // jbo -> Latn
- {0xD0290000u, 46u}, // jbu -> Latn
- {0xB4890000u, 46u}, // jen -> Latn
- {0xA8C90000u, 46u}, // jgk -> Latn
- {0xB8C90000u, 46u}, // jgo -> Latn
+ {0xB2C80000u, 45u}, // iwm -> Latn
+ {0xCAC80000u, 45u}, // iws -> Latn
+ {0x9F280000u, 45u}, // izh -> Latn
+ {0xA3280000u, 45u}, // izi -> Latn
+ {0x6A610000u, 35u}, // ja -> Jpan
+ {0x84090000u, 45u}, // jab -> Latn
+ {0xB0090000u, 45u}, // jam -> Latn
+ {0xC4090000u, 45u}, // jar -> Latn
+ {0xB8290000u, 45u}, // jbo -> Latn
+ {0xD0290000u, 45u}, // jbu -> Latn
+ {0xB4890000u, 45u}, // jen -> Latn
+ {0xA8C90000u, 45u}, // jgk -> Latn
+ {0xB8C90000u, 45u}, // jgo -> Latn
{0x6A690000u, 31u}, // ji -> Hebr
- {0x85090000u, 46u}, // jib -> Latn
- {0x89890000u, 46u}, // jmc -> Latn
+ {0x85090000u, 45u}, // jib -> Latn
+ {0x89890000u, 45u}, // jmc -> Latn
{0xAD890000u, 19u}, // jml -> Deva
- {0x82290000u, 46u}, // jra -> Latn
- {0xCE890000u, 46u}, // jut -> Latn
- {0x6A760000u, 46u}, // jv -> Latn
- {0x6A770000u, 46u}, // jw -> Latn
+ {0x82290000u, 45u}, // jra -> Latn
+ {0xCE890000u, 45u}, // jut -> Latn
+ {0x6A760000u, 45u}, // jv -> Latn
+ {0x6A770000u, 45u}, // jw -> Latn
{0x6B610000u, 22u}, // ka -> Geor
{0x800A0000u, 18u}, // kaa -> Cyrl
- {0x840A0000u, 46u}, // kab -> Latn
- {0x880A0000u, 46u}, // kac -> Latn
- {0x8C0A0000u, 46u}, // kad -> Latn
- {0xA00A0000u, 46u}, // kai -> Latn
- {0xA40A0000u, 46u}, // kaj -> Latn
- {0xB00A0000u, 46u}, // kam -> Latn
- {0xB80A0000u, 46u}, // kao -> Latn
+ {0x840A0000u, 45u}, // kab -> Latn
+ {0x880A0000u, 45u}, // kac -> Latn
+ {0x8C0A0000u, 45u}, // kad -> Latn
+ {0xA00A0000u, 45u}, // kai -> Latn
+ {0xA40A0000u, 45u}, // kaj -> Latn
+ {0xB00A0000u, 45u}, // kam -> Latn
+ {0xB80A0000u, 45u}, // kao -> Latn
{0x8C2A0000u, 18u}, // kbd -> Cyrl
- {0xB02A0000u, 46u}, // kbm -> Latn
- {0xBC2A0000u, 46u}, // kbp -> Latn
- {0xC02A0000u, 46u}, // kbq -> Latn
- {0xDC2A0000u, 46u}, // kbx -> Latn
+ {0xB02A0000u, 45u}, // kbm -> Latn
+ {0xBC2A0000u, 45u}, // kbp -> Latn
+ {0xC02A0000u, 45u}, // kbq -> Latn
+ {0xDC2A0000u, 45u}, // kbx -> Latn
{0xE02A0000u, 2u}, // kby -> Arab
- {0x984A0000u, 46u}, // kcg -> Latn
- {0xA84A0000u, 46u}, // kck -> Latn
- {0xAC4A0000u, 46u}, // kcl -> Latn
- {0xCC4A0000u, 46u}, // kct -> Latn
- {0x906A0000u, 46u}, // kde -> Latn
- {0x9C6A0000u, 2u}, // kdh -> Arab
- {0xAC6A0000u, 46u}, // kdl -> Latn
- {0xCC6A0000u, 91u}, // kdt -> Thai
- {0x808A0000u, 46u}, // kea -> Latn
- {0xB48A0000u, 46u}, // ken -> Latn
- {0xE48A0000u, 46u}, // kez -> Latn
- {0xB8AA0000u, 46u}, // kfo -> Latn
+ {0x984A0000u, 45u}, // kcg -> Latn
+ {0xA84A0000u, 45u}, // kck -> Latn
+ {0xAC4A0000u, 45u}, // kcl -> Latn
+ {0xCC4A0000u, 45u}, // kct -> Latn
+ {0x906A0000u, 45u}, // kde -> Latn
+ {0x9C6A0000u, 45u}, // kdh -> Latn
+ {0xAC6A0000u, 45u}, // kdl -> Latn
+ {0xCC6A0000u, 92u}, // kdt -> Thai
+ {0x808A0000u, 45u}, // kea -> Latn
+ {0xB48A0000u, 45u}, // ken -> Latn
+ {0xE48A0000u, 45u}, // kez -> Latn
+ {0xB8AA0000u, 45u}, // kfo -> Latn
{0xC4AA0000u, 19u}, // kfr -> Deva
{0xE0AA0000u, 19u}, // kfy -> Deva
- {0x6B670000u, 46u}, // kg -> Latn
- {0x90CA0000u, 46u}, // kge -> Latn
- {0x94CA0000u, 46u}, // kgf -> Latn
- {0xBCCA0000u, 46u}, // kgp -> Latn
- {0x80EA0000u, 46u}, // kha -> Latn
- {0x84EA0000u, 84u}, // khb -> Talu
+ {0x6B670000u, 45u}, // kg -> Latn
+ {0x90CA0000u, 45u}, // kge -> Latn
+ {0x94CA0000u, 45u}, // kgf -> Latn
+ {0xBCCA0000u, 45u}, // kgp -> Latn
+ {0x80EA0000u, 45u}, // kha -> Latn
+ {0x84EA0000u, 85u}, // khb -> Talu
{0xB4EA0000u, 19u}, // khn -> Deva
- {0xC0EA0000u, 46u}, // khq -> Latn
- {0xC8EA0000u, 46u}, // khs -> Latn
- {0xCCEA0000u, 59u}, // kht -> Mymr
+ {0xC0EA0000u, 45u}, // khq -> Latn
+ {0xC8EA0000u, 45u}, // khs -> Latn
+ {0xCCEA0000u, 58u}, // kht -> Mymr
{0xD8EA0000u, 2u}, // khw -> Arab
- {0xE4EA0000u, 46u}, // khz -> Latn
- {0x6B690000u, 46u}, // ki -> Latn
- {0xA50A0000u, 46u}, // kij -> Latn
- {0xD10A0000u, 46u}, // kiu -> Latn
- {0xD90A0000u, 46u}, // kiw -> Latn
- {0x6B6A0000u, 46u}, // kj -> Latn
- {0x8D2A0000u, 46u}, // kjd -> Latn
- {0x992A0000u, 45u}, // kjg -> Laoo
- {0xC92A0000u, 46u}, // kjs -> Latn
- {0xE12A0000u, 46u}, // kjy -> Latn
+ {0xE4EA0000u, 45u}, // khz -> Latn
+ {0x6B690000u, 45u}, // ki -> Latn
+ {0xA50A0000u, 45u}, // kij -> Latn
+ {0xD10A0000u, 45u}, // kiu -> Latn
+ {0xD90A0000u, 45u}, // kiw -> Latn
+ {0x6B6A0000u, 45u}, // kj -> Latn
+ {0x8D2A0000u, 45u}, // kjd -> Latn
+ {0x992A0000u, 44u}, // kjg -> Laoo
+ {0xC92A0000u, 45u}, // kjs -> Latn
+ {0xE12A0000u, 45u}, // kjy -> Latn
{0x6B6B0000u, 18u}, // kk -> Cyrl
{0x6B6B4146u, 2u}, // kk-AF -> Arab
{0x6B6B434Eu, 2u}, // kk-CN -> Arab
{0x6B6B4952u, 2u}, // kk-IR -> Arab
{0x6B6B4D4Eu, 2u}, // kk-MN -> Arab
- {0x894A0000u, 46u}, // kkc -> Latn
- {0xA54A0000u, 46u}, // kkj -> Latn
- {0x6B6C0000u, 46u}, // kl -> Latn
- {0xB56A0000u, 46u}, // kln -> Latn
- {0xC16A0000u, 46u}, // klq -> Latn
- {0xCD6A0000u, 46u}, // klt -> Latn
- {0xDD6A0000u, 46u}, // klx -> Latn
- {0x6B6D0000u, 40u}, // km -> Khmr
- {0x858A0000u, 46u}, // kmb -> Latn
- {0x9D8A0000u, 46u}, // kmh -> Latn
- {0xB98A0000u, 46u}, // kmo -> Latn
- {0xC98A0000u, 46u}, // kms -> Latn
- {0xD18A0000u, 46u}, // kmu -> Latn
- {0xD98A0000u, 46u}, // kmw -> Latn
- {0x6B6E0000u, 42u}, // kn -> Knda
- {0x95AA0000u, 46u}, // knf -> Latn
- {0xBDAA0000u, 46u}, // knp -> Latn
- {0x6B6F0000u, 43u}, // ko -> Kore
+ {0x894A0000u, 45u}, // kkc -> Latn
+ {0xA54A0000u, 45u}, // kkj -> Latn
+ {0x6B6C0000u, 45u}, // kl -> Latn
+ {0xB56A0000u, 45u}, // kln -> Latn
+ {0xC16A0000u, 45u}, // klq -> Latn
+ {0xCD6A0000u, 45u}, // klt -> Latn
+ {0xDD6A0000u, 45u}, // klx -> Latn
+ {0x6B6D0000u, 39u}, // km -> Khmr
+ {0x858A0000u, 45u}, // kmb -> Latn
+ {0x9D8A0000u, 45u}, // kmh -> Latn
+ {0xB98A0000u, 45u}, // kmo -> Latn
+ {0xC98A0000u, 45u}, // kms -> Latn
+ {0xD18A0000u, 45u}, // kmu -> Latn
+ {0xD98A0000u, 45u}, // kmw -> Latn
+ {0x6B6E0000u, 41u}, // kn -> Knda
+ {0x95AA0000u, 45u}, // knf -> Latn
+ {0xBDAA0000u, 45u}, // knp -> Latn
+ {0x6B6F0000u, 42u}, // ko -> Kore
{0xA1CA0000u, 18u}, // koi -> Cyrl
{0xA9CA0000u, 19u}, // kok -> Deva
- {0xADCA0000u, 46u}, // kol -> Latn
- {0xC9CA0000u, 46u}, // kos -> Latn
- {0xE5CA0000u, 46u}, // koz -> Latn
- {0x91EA0000u, 46u}, // kpe -> Latn
- {0x95EA0000u, 46u}, // kpf -> Latn
- {0xB9EA0000u, 46u}, // kpo -> Latn
- {0xC5EA0000u, 46u}, // kpr -> Latn
- {0xDDEA0000u, 46u}, // kpx -> Latn
- {0x860A0000u, 46u}, // kqb -> Latn
- {0x960A0000u, 46u}, // kqf -> Latn
- {0xCA0A0000u, 46u}, // kqs -> Latn
+ {0xADCA0000u, 45u}, // kol -> Latn
+ {0xC9CA0000u, 45u}, // kos -> Latn
+ {0xE5CA0000u, 45u}, // koz -> Latn
+ {0x91EA0000u, 45u}, // kpe -> Latn
+ {0x95EA0000u, 45u}, // kpf -> Latn
+ {0xB9EA0000u, 45u}, // kpo -> Latn
+ {0xC5EA0000u, 45u}, // kpr -> Latn
+ {0xDDEA0000u, 45u}, // kpx -> Latn
+ {0x860A0000u, 45u}, // kqb -> Latn
+ {0x960A0000u, 45u}, // kqf -> Latn
+ {0xCA0A0000u, 45u}, // kqs -> Latn
{0xE20A0000u, 21u}, // kqy -> Ethi
- {0x6B720000u, 46u}, // kr -> Latn
+ {0x6B720000u, 45u}, // kr -> Latn
{0x8A2A0000u, 18u}, // krc -> Cyrl
- {0xA22A0000u, 46u}, // kri -> Latn
- {0xA62A0000u, 46u}, // krj -> Latn
- {0xAE2A0000u, 46u}, // krl -> Latn
- {0xCA2A0000u, 46u}, // krs -> Latn
+ {0xA22A0000u, 45u}, // kri -> Latn
+ {0xA62A0000u, 45u}, // krj -> Latn
+ {0xAE2A0000u, 45u}, // krl -> Latn
+ {0xCA2A0000u, 45u}, // krs -> Latn
{0xD22A0000u, 19u}, // kru -> Deva
{0x6B730000u, 2u}, // ks -> Arab
- {0x864A0000u, 46u}, // ksb -> Latn
- {0x8E4A0000u, 46u}, // ksd -> Latn
- {0x964A0000u, 46u}, // ksf -> Latn
- {0x9E4A0000u, 46u}, // ksh -> Latn
- {0xA64A0000u, 46u}, // ksj -> Latn
- {0xC64A0000u, 46u}, // ksr -> Latn
+ {0x864A0000u, 45u}, // ksb -> Latn
+ {0x8E4A0000u, 45u}, // ksd -> Latn
+ {0x964A0000u, 45u}, // ksf -> Latn
+ {0x9E4A0000u, 45u}, // ksh -> Latn
+ {0xA64A0000u, 45u}, // ksj -> Latn
+ {0xC64A0000u, 45u}, // ksr -> Latn
{0x866A0000u, 21u}, // ktb -> Ethi
- {0xB26A0000u, 46u}, // ktm -> Latn
- {0xBA6A0000u, 46u}, // kto -> Latn
- {0xC66A0000u, 46u}, // ktr -> Latn
- {0x6B750000u, 46u}, // ku -> Latn
+ {0xB26A0000u, 45u}, // ktm -> Latn
+ {0xBA6A0000u, 45u}, // kto -> Latn
+ {0xC66A0000u, 45u}, // ktr -> Latn
+ {0x6B750000u, 45u}, // ku -> Latn
{0x6B754952u, 2u}, // ku-IR -> Arab
{0x6B754C42u, 2u}, // ku-LB -> Arab
- {0x868A0000u, 46u}, // kub -> Latn
- {0x8E8A0000u, 46u}, // kud -> Latn
- {0x928A0000u, 46u}, // kue -> Latn
- {0xA68A0000u, 46u}, // kuj -> Latn
+ {0x868A0000u, 45u}, // kub -> Latn
+ {0x8E8A0000u, 45u}, // kud -> Latn
+ {0x928A0000u, 45u}, // kue -> Latn
+ {0xA68A0000u, 45u}, // kuj -> Latn
{0xB28A0000u, 18u}, // kum -> Cyrl
- {0xB68A0000u, 46u}, // kun -> Latn
- {0xBE8A0000u, 46u}, // kup -> Latn
- {0xCA8A0000u, 46u}, // kus -> Latn
+ {0xB68A0000u, 45u}, // kun -> Latn
+ {0xBE8A0000u, 45u}, // kup -> Latn
+ {0xCA8A0000u, 45u}, // kus -> Latn
{0x6B760000u, 18u}, // kv -> Cyrl
- {0x9AAA0000u, 46u}, // kvg -> Latn
- {0xC6AA0000u, 46u}, // kvr -> Latn
+ {0x9AAA0000u, 45u}, // kvg -> Latn
+ {0xC6AA0000u, 45u}, // kvr -> Latn
{0xDEAA0000u, 2u}, // kvx -> Arab
- {0x6B770000u, 46u}, // kw -> Latn
- {0xA6CA0000u, 46u}, // kwj -> Latn
- {0xBACA0000u, 46u}, // kwo -> Latn
- {0xC2CA0000u, 46u}, // kwq -> Latn
- {0x82EA0000u, 46u}, // kxa -> Latn
+ {0x6B770000u, 45u}, // kw -> Latn
+ {0xA6CA0000u, 45u}, // kwj -> Latn
+ {0xBACA0000u, 45u}, // kwo -> Latn
+ {0xC2CA0000u, 45u}, // kwq -> Latn
+ {0x82EA0000u, 45u}, // kxa -> Latn
{0x8AEA0000u, 21u}, // kxc -> Ethi
- {0x92EA0000u, 46u}, // kxe -> Latn
+ {0x92EA0000u, 45u}, // kxe -> Latn
{0xAEEA0000u, 19u}, // kxl -> Deva
- {0xB2EA0000u, 91u}, // kxm -> Thai
+ {0xB2EA0000u, 92u}, // kxm -> Thai
{0xBEEA0000u, 2u}, // kxp -> Arab
- {0xDAEA0000u, 46u}, // kxw -> Latn
- {0xE6EA0000u, 46u}, // kxz -> Latn
+ {0xDAEA0000u, 45u}, // kxw -> Latn
+ {0xE6EA0000u, 45u}, // kxz -> Latn
{0x6B790000u, 18u}, // ky -> Cyrl
{0x6B79434Eu, 2u}, // ky-CN -> Arab
- {0x6B795452u, 46u}, // ky-TR -> Latn
- {0x930A0000u, 46u}, // kye -> Latn
- {0xDF0A0000u, 46u}, // kyx -> Latn
+ {0x6B795452u, 45u}, // ky-TR -> Latn
+ {0x930A0000u, 45u}, // kye -> Latn
+ {0xDF0A0000u, 45u}, // kyx -> Latn
{0x9F2A0000u, 2u}, // kzh -> Arab
- {0xA72A0000u, 46u}, // kzj -> Latn
- {0xC72A0000u, 46u}, // kzr -> Latn
- {0xCF2A0000u, 46u}, // kzt -> Latn
- {0x6C610000u, 46u}, // la -> Latn
- {0x840B0000u, 48u}, // lab -> Lina
+ {0xA72A0000u, 45u}, // kzj -> Latn
+ {0xC72A0000u, 45u}, // kzr -> Latn
+ {0xCF2A0000u, 45u}, // kzt -> Latn
+ {0x6C610000u, 45u}, // la -> Latn
+ {0x840B0000u, 47u}, // lab -> Lina
{0x8C0B0000u, 31u}, // lad -> Hebr
- {0x980B0000u, 46u}, // lag -> Latn
+ {0x980B0000u, 45u}, // lag -> Latn
{0x9C0B0000u, 2u}, // lah -> Arab
- {0xA40B0000u, 46u}, // laj -> Latn
- {0xC80B0000u, 46u}, // las -> Latn
- {0x6C620000u, 46u}, // lb -> Latn
+ {0xA40B0000u, 45u}, // laj -> Latn
+ {0xC80B0000u, 45u}, // las -> Latn
+ {0x6C620000u, 45u}, // lb -> Latn
{0x902B0000u, 18u}, // lbe -> Cyrl
- {0xD02B0000u, 46u}, // lbu -> Latn
- {0xD82B0000u, 46u}, // lbw -> Latn
- {0xB04B0000u, 46u}, // lcm -> Latn
- {0xBC4B0000u, 91u}, // lcp -> Thai
- {0x846B0000u, 46u}, // ldb -> Latn
- {0x8C8B0000u, 46u}, // led -> Latn
- {0x908B0000u, 46u}, // lee -> Latn
- {0xB08B0000u, 46u}, // lem -> Latn
- {0xBC8B0000u, 47u}, // lep -> Lepc
- {0xC08B0000u, 46u}, // leq -> Latn
- {0xD08B0000u, 46u}, // leu -> Latn
+ {0xD02B0000u, 45u}, // lbu -> Latn
+ {0xD82B0000u, 45u}, // lbw -> Latn
+ {0xB04B0000u, 45u}, // lcm -> Latn
+ {0xBC4B0000u, 92u}, // lcp -> Thai
+ {0x846B0000u, 45u}, // ldb -> Latn
+ {0x8C8B0000u, 45u}, // led -> Latn
+ {0x908B0000u, 45u}, // lee -> Latn
+ {0xB08B0000u, 45u}, // lem -> Latn
+ {0xBC8B0000u, 46u}, // lep -> Lepc
+ {0xC08B0000u, 45u}, // leq -> Latn
+ {0xD08B0000u, 45u}, // leu -> Latn
{0xE48B0000u, 18u}, // lez -> Cyrl
- {0x6C670000u, 46u}, // lg -> Latn
- {0x98CB0000u, 46u}, // lgg -> Latn
- {0x6C690000u, 46u}, // li -> Latn
- {0x810B0000u, 46u}, // lia -> Latn
- {0x8D0B0000u, 46u}, // lid -> Latn
+ {0x6C670000u, 45u}, // lg -> Latn
+ {0x98CB0000u, 45u}, // lgg -> Latn
+ {0x6C690000u, 45u}, // li -> Latn
+ {0x810B0000u, 45u}, // lia -> Latn
+ {0x8D0B0000u, 45u}, // lid -> Latn
{0x950B0000u, 19u}, // lif -> Deva
- {0x990B0000u, 46u}, // lig -> Latn
- {0x9D0B0000u, 46u}, // lih -> Latn
- {0xA50B0000u, 46u}, // lij -> Latn
- {0xC90B0000u, 49u}, // lis -> Lisu
- {0xBD2B0000u, 46u}, // ljp -> Latn
+ {0x990B0000u, 45u}, // lig -> Latn
+ {0x9D0B0000u, 45u}, // lih -> Latn
+ {0xA50B0000u, 45u}, // lij -> Latn
+ {0xC90B0000u, 48u}, // lis -> Lisu
+ {0xBD2B0000u, 45u}, // ljp -> Latn
{0xA14B0000u, 2u}, // lki -> Arab
- {0xCD4B0000u, 46u}, // lkt -> Latn
- {0x916B0000u, 46u}, // lle -> Latn
- {0xB56B0000u, 46u}, // lln -> Latn
- {0xB58B0000u, 88u}, // lmn -> Telu
- {0xB98B0000u, 46u}, // lmo -> Latn
- {0xBD8B0000u, 46u}, // lmp -> Latn
- {0x6C6E0000u, 46u}, // ln -> Latn
- {0xC9AB0000u, 46u}, // lns -> Latn
- {0xD1AB0000u, 46u}, // lnu -> Latn
- {0x6C6F0000u, 45u}, // lo -> Laoo
- {0xA5CB0000u, 46u}, // loj -> Latn
- {0xA9CB0000u, 46u}, // lok -> Latn
- {0xADCB0000u, 46u}, // lol -> Latn
- {0xC5CB0000u, 46u}, // lor -> Latn
- {0xC9CB0000u, 46u}, // los -> Latn
- {0xE5CB0000u, 46u}, // loz -> Latn
+ {0xCD4B0000u, 45u}, // lkt -> Latn
+ {0x916B0000u, 45u}, // lle -> Latn
+ {0xB56B0000u, 45u}, // lln -> Latn
+ {0xB58B0000u, 89u}, // lmn -> Telu
+ {0xB98B0000u, 45u}, // lmo -> Latn
+ {0xBD8B0000u, 45u}, // lmp -> Latn
+ {0x6C6E0000u, 45u}, // ln -> Latn
+ {0xC9AB0000u, 45u}, // lns -> Latn
+ {0xD1AB0000u, 45u}, // lnu -> Latn
+ {0x6C6F0000u, 44u}, // lo -> Laoo
+ {0xA5CB0000u, 45u}, // loj -> Latn
+ {0xA9CB0000u, 45u}, // lok -> Latn
+ {0xADCB0000u, 45u}, // lol -> Latn
+ {0xC5CB0000u, 45u}, // lor -> Latn
+ {0xC9CB0000u, 45u}, // los -> Latn
+ {0xE5CB0000u, 45u}, // loz -> Latn
{0x8A2B0000u, 2u}, // lrc -> Arab
- {0x6C740000u, 46u}, // lt -> Latn
- {0x9A6B0000u, 46u}, // ltg -> Latn
- {0x6C750000u, 46u}, // lu -> Latn
- {0x828B0000u, 46u}, // lua -> Latn
- {0xBA8B0000u, 46u}, // luo -> Latn
- {0xE28B0000u, 46u}, // luy -> Latn
+ {0x6C740000u, 45u}, // lt -> Latn
+ {0x9A6B0000u, 45u}, // ltg -> Latn
+ {0x6C750000u, 45u}, // lu -> Latn
+ {0x828B0000u, 45u}, // lua -> Latn
+ {0xBA8B0000u, 45u}, // luo -> Latn
+ {0xE28B0000u, 45u}, // luy -> Latn
{0xE68B0000u, 2u}, // luz -> Arab
- {0x6C760000u, 46u}, // lv -> Latn
- {0xAECB0000u, 91u}, // lwl -> Thai
+ {0x6C760000u, 45u}, // lv -> Latn
+ {0xAECB0000u, 92u}, // lwl -> Thai
{0x9F2B0000u, 29u}, // lzh -> Hans
- {0xE72B0000u, 46u}, // lzz -> Latn
- {0x8C0C0000u, 46u}, // mad -> Latn
- {0x940C0000u, 46u}, // maf -> Latn
+ {0xE72B0000u, 45u}, // lzz -> Latn
+ {0x8C0C0000u, 45u}, // mad -> Latn
+ {0x940C0000u, 45u}, // maf -> Latn
{0x980C0000u, 19u}, // mag -> Deva
{0xA00C0000u, 19u}, // mai -> Deva
- {0xA80C0000u, 46u}, // mak -> Latn
- {0xB40C0000u, 46u}, // man -> Latn
- {0xB40C474Eu, 61u}, // man-GN -> Nkoo
- {0xC80C0000u, 46u}, // mas -> Latn
- {0xD80C0000u, 46u}, // maw -> Latn
- {0xE40C0000u, 46u}, // maz -> Latn
- {0x9C2C0000u, 46u}, // mbh -> Latn
- {0xB82C0000u, 46u}, // mbo -> Latn
- {0xC02C0000u, 46u}, // mbq -> Latn
- {0xD02C0000u, 46u}, // mbu -> Latn
- {0xD82C0000u, 46u}, // mbw -> Latn
- {0xA04C0000u, 46u}, // mci -> Latn
- {0xBC4C0000u, 46u}, // mcp -> Latn
- {0xC04C0000u, 46u}, // mcq -> Latn
- {0xC44C0000u, 46u}, // mcr -> Latn
- {0xD04C0000u, 46u}, // mcu -> Latn
- {0x806C0000u, 46u}, // mda -> Latn
+ {0xA80C0000u, 45u}, // mak -> Latn
+ {0xB40C0000u, 45u}, // man -> Latn
+ {0xB40C474Eu, 60u}, // man-GN -> Nkoo
+ {0xC80C0000u, 45u}, // mas -> Latn
+ {0xD80C0000u, 45u}, // maw -> Latn
+ {0xE40C0000u, 45u}, // maz -> Latn
+ {0x9C2C0000u, 45u}, // mbh -> Latn
+ {0xB82C0000u, 45u}, // mbo -> Latn
+ {0xC02C0000u, 45u}, // mbq -> Latn
+ {0xD02C0000u, 45u}, // mbu -> Latn
+ {0xD82C0000u, 45u}, // mbw -> Latn
+ {0xA04C0000u, 45u}, // mci -> Latn
+ {0xBC4C0000u, 45u}, // mcp -> Latn
+ {0xC04C0000u, 45u}, // mcq -> Latn
+ {0xC44C0000u, 45u}, // mcr -> Latn
+ {0xD04C0000u, 45u}, // mcu -> Latn
+ {0x806C0000u, 45u}, // mda -> Latn
{0x906C0000u, 2u}, // mde -> Arab
{0x946C0000u, 18u}, // mdf -> Cyrl
- {0x9C6C0000u, 46u}, // mdh -> Latn
- {0xA46C0000u, 46u}, // mdj -> Latn
- {0xC46C0000u, 46u}, // mdr -> Latn
+ {0x9C6C0000u, 45u}, // mdh -> Latn
+ {0xA46C0000u, 45u}, // mdj -> Latn
+ {0xC46C0000u, 45u}, // mdr -> Latn
{0xDC6C0000u, 21u}, // mdx -> Ethi
- {0x8C8C0000u, 46u}, // med -> Latn
- {0x908C0000u, 46u}, // mee -> Latn
- {0xA88C0000u, 46u}, // mek -> Latn
- {0xB48C0000u, 46u}, // men -> Latn
- {0xC48C0000u, 46u}, // mer -> Latn
- {0xCC8C0000u, 46u}, // met -> Latn
- {0xD08C0000u, 46u}, // meu -> Latn
+ {0x8C8C0000u, 45u}, // med -> Latn
+ {0x908C0000u, 45u}, // mee -> Latn
+ {0xA88C0000u, 45u}, // mek -> Latn
+ {0xB48C0000u, 45u}, // men -> Latn
+ {0xC48C0000u, 45u}, // mer -> Latn
+ {0xCC8C0000u, 45u}, // met -> Latn
+ {0xD08C0000u, 45u}, // meu -> Latn
{0x80AC0000u, 2u}, // mfa -> Arab
- {0x90AC0000u, 46u}, // mfe -> Latn
- {0xB4AC0000u, 46u}, // mfn -> Latn
- {0xB8AC0000u, 46u}, // mfo -> Latn
- {0xC0AC0000u, 46u}, // mfq -> Latn
- {0x6D670000u, 46u}, // mg -> Latn
- {0x9CCC0000u, 46u}, // mgh -> Latn
- {0xACCC0000u, 46u}, // mgl -> Latn
- {0xB8CC0000u, 46u}, // mgo -> Latn
+ {0x90AC0000u, 45u}, // mfe -> Latn
+ {0xB4AC0000u, 45u}, // mfn -> Latn
+ {0xB8AC0000u, 45u}, // mfo -> Latn
+ {0xC0AC0000u, 45u}, // mfq -> Latn
+ {0x6D670000u, 45u}, // mg -> Latn
+ {0x9CCC0000u, 45u}, // mgh -> Latn
+ {0xACCC0000u, 45u}, // mgl -> Latn
+ {0xB8CC0000u, 45u}, // mgo -> Latn
{0xBCCC0000u, 19u}, // mgp -> Deva
- {0xE0CC0000u, 46u}, // mgy -> Latn
- {0x6D680000u, 46u}, // mh -> Latn
- {0xA0EC0000u, 46u}, // mhi -> Latn
- {0xACEC0000u, 46u}, // mhl -> Latn
- {0x6D690000u, 46u}, // mi -> Latn
- {0x950C0000u, 46u}, // mif -> Latn
- {0xB50C0000u, 46u}, // min -> Latn
- {0xD90C0000u, 46u}, // miw -> Latn
+ {0xE0CC0000u, 45u}, // mgy -> Latn
+ {0x6D680000u, 45u}, // mh -> Latn
+ {0xA0EC0000u, 45u}, // mhi -> Latn
+ {0xACEC0000u, 45u}, // mhl -> Latn
+ {0x6D690000u, 45u}, // mi -> Latn
+ {0x950C0000u, 45u}, // mif -> Latn
+ {0xB50C0000u, 45u}, // min -> Latn
+ {0xD90C0000u, 45u}, // miw -> Latn
{0x6D6B0000u, 18u}, // mk -> Cyrl
{0xA14C0000u, 2u}, // mki -> Arab
- {0xAD4C0000u, 46u}, // mkl -> Latn
- {0xBD4C0000u, 46u}, // mkp -> Latn
- {0xD94C0000u, 46u}, // mkw -> Latn
- {0x6D6C0000u, 56u}, // ml -> Mlym
- {0x916C0000u, 46u}, // mle -> Latn
- {0xBD6C0000u, 46u}, // mlp -> Latn
- {0xC96C0000u, 46u}, // mls -> Latn
- {0xB98C0000u, 46u}, // mmo -> Latn
- {0xD18C0000u, 46u}, // mmu -> Latn
- {0xDD8C0000u, 46u}, // mmx -> Latn
+ {0xAD4C0000u, 45u}, // mkl -> Latn
+ {0xBD4C0000u, 45u}, // mkp -> Latn
+ {0xD94C0000u, 45u}, // mkw -> Latn
+ {0x6D6C0000u, 55u}, // ml -> Mlym
+ {0x916C0000u, 45u}, // mle -> Latn
+ {0xBD6C0000u, 45u}, // mlp -> Latn
+ {0xC96C0000u, 45u}, // mls -> Latn
+ {0xB98C0000u, 45u}, // mmo -> Latn
+ {0xD18C0000u, 45u}, // mmu -> Latn
+ {0xDD8C0000u, 45u}, // mmx -> Latn
{0x6D6E0000u, 18u}, // mn -> Cyrl
- {0x6D6E434Eu, 57u}, // mn-CN -> Mong
- {0x81AC0000u, 46u}, // mna -> Latn
- {0x95AC0000u, 46u}, // mnf -> Latn
+ {0x6D6E434Eu, 56u}, // mn-CN -> Mong
+ {0x81AC0000u, 45u}, // mna -> Latn
+ {0x95AC0000u, 45u}, // mnf -> Latn
{0xA1AC0000u, 8u}, // mni -> Beng
- {0xD9AC0000u, 59u}, // mnw -> Mymr
- {0x6D6F0000u, 46u}, // mo -> Latn
- {0x81CC0000u, 46u}, // moa -> Latn
- {0x91CC0000u, 46u}, // moe -> Latn
- {0x9DCC0000u, 46u}, // moh -> Latn
- {0xC9CC0000u, 46u}, // mos -> Latn
- {0xDDCC0000u, 46u}, // mox -> Latn
- {0xBDEC0000u, 46u}, // mpp -> Latn
- {0xC9EC0000u, 46u}, // mps -> Latn
- {0xCDEC0000u, 46u}, // mpt -> Latn
- {0xDDEC0000u, 46u}, // mpx -> Latn
- {0xAE0C0000u, 46u}, // mql -> Latn
+ {0xD9AC0000u, 58u}, // mnw -> Mymr
+ {0x6D6F0000u, 45u}, // mo -> Latn
+ {0x81CC0000u, 45u}, // moa -> Latn
+ {0x91CC0000u, 45u}, // moe -> Latn
+ {0x9DCC0000u, 45u}, // moh -> Latn
+ {0xC9CC0000u, 45u}, // mos -> Latn
+ {0xDDCC0000u, 45u}, // mox -> Latn
+ {0xBDEC0000u, 45u}, // mpp -> Latn
+ {0xC9EC0000u, 45u}, // mps -> Latn
+ {0xCDEC0000u, 45u}, // mpt -> Latn
+ {0xDDEC0000u, 45u}, // mpx -> Latn
+ {0xAE0C0000u, 45u}, // mql -> Latn
{0x6D720000u, 19u}, // mr -> Deva
{0x8E2C0000u, 19u}, // mrd -> Deva
{0xA62C0000u, 18u}, // mrj -> Cyrl
- {0xBA2C0000u, 58u}, // mro -> Mroo
- {0x6D730000u, 46u}, // ms -> Latn
+ {0xBA2C0000u, 57u}, // mro -> Mroo
+ {0x6D730000u, 45u}, // ms -> Latn
{0x6D734343u, 2u}, // ms-CC -> Arab
- {0x6D740000u, 46u}, // mt -> Latn
- {0x8A6C0000u, 46u}, // mtc -> Latn
- {0x966C0000u, 46u}, // mtf -> Latn
- {0xA26C0000u, 46u}, // mti -> Latn
+ {0x6D740000u, 45u}, // mt -> Latn
+ {0x8A6C0000u, 45u}, // mtc -> Latn
+ {0x966C0000u, 45u}, // mtf -> Latn
+ {0xA26C0000u, 45u}, // mti -> Latn
{0xC66C0000u, 19u}, // mtr -> Deva
- {0x828C0000u, 46u}, // mua -> Latn
- {0xC68C0000u, 46u}, // mur -> Latn
- {0xCA8C0000u, 46u}, // mus -> Latn
- {0x82AC0000u, 46u}, // mva -> Latn
- {0xB6AC0000u, 46u}, // mvn -> Latn
+ {0x828C0000u, 45u}, // mua -> Latn
+ {0xC68C0000u, 45u}, // mur -> Latn
+ {0xCA8C0000u, 45u}, // mus -> Latn
+ {0x82AC0000u, 45u}, // mva -> Latn
+ {0xB6AC0000u, 45u}, // mvn -> Latn
{0xE2AC0000u, 2u}, // mvy -> Arab
- {0xAACC0000u, 46u}, // mwk -> Latn
+ {0xAACC0000u, 45u}, // mwk -> Latn
{0xC6CC0000u, 19u}, // mwr -> Deva
- {0xD6CC0000u, 46u}, // mwv -> Latn
- {0xDACC0000u, 34u}, // mww -> Hmnp
- {0x8AEC0000u, 46u}, // mxc -> Latn
- {0xB2EC0000u, 46u}, // mxm -> Latn
- {0x6D790000u, 59u}, // my -> Mymr
- {0xAB0C0000u, 46u}, // myk -> Latn
+ {0xD6CC0000u, 45u}, // mwv -> Latn
+ {0xDACC0000u, 33u}, // mww -> Hmnp
+ {0x8AEC0000u, 45u}, // mxc -> Latn
+ {0xB2EC0000u, 45u}, // mxm -> Latn
+ {0x6D790000u, 58u}, // my -> Mymr
+ {0xAB0C0000u, 45u}, // myk -> Latn
{0xB30C0000u, 21u}, // mym -> Ethi
{0xD70C0000u, 18u}, // myv -> Cyrl
- {0xDB0C0000u, 46u}, // myw -> Latn
- {0xDF0C0000u, 46u}, // myx -> Latn
- {0xE70C0000u, 52u}, // myz -> Mand
- {0xAB2C0000u, 46u}, // mzk -> Latn
- {0xB32C0000u, 46u}, // mzm -> Latn
+ {0xDB0C0000u, 45u}, // myw -> Latn
+ {0xDF0C0000u, 45u}, // myx -> Latn
+ {0xE70C0000u, 51u}, // myz -> Mand
+ {0xAB2C0000u, 45u}, // mzk -> Latn
+ {0xB32C0000u, 45u}, // mzm -> Latn
{0xB72C0000u, 2u}, // mzn -> Arab
- {0xBF2C0000u, 46u}, // mzp -> Latn
- {0xDB2C0000u, 46u}, // mzw -> Latn
- {0xE72C0000u, 46u}, // mzz -> Latn
- {0x6E610000u, 46u}, // na -> Latn
- {0x880D0000u, 46u}, // nac -> Latn
- {0x940D0000u, 46u}, // naf -> Latn
- {0xA80D0000u, 46u}, // nak -> Latn
+ {0xBF2C0000u, 45u}, // mzp -> Latn
+ {0xDB2C0000u, 45u}, // mzw -> Latn
+ {0xE72C0000u, 45u}, // mzz -> Latn
+ {0x6E610000u, 45u}, // na -> Latn
+ {0x880D0000u, 45u}, // nac -> Latn
+ {0x940D0000u, 45u}, // naf -> Latn
+ {0xA80D0000u, 45u}, // nak -> Latn
{0xB40D0000u, 29u}, // nan -> Hans
- {0xBC0D0000u, 46u}, // nap -> Latn
- {0xC00D0000u, 46u}, // naq -> Latn
- {0xC80D0000u, 46u}, // nas -> Latn
- {0x6E620000u, 46u}, // nb -> Latn
- {0x804D0000u, 46u}, // nca -> Latn
- {0x904D0000u, 46u}, // nce -> Latn
- {0x944D0000u, 46u}, // ncf -> Latn
- {0x9C4D0000u, 46u}, // nch -> Latn
- {0xB84D0000u, 46u}, // nco -> Latn
- {0xD04D0000u, 46u}, // ncu -> Latn
- {0x6E640000u, 46u}, // nd -> Latn
- {0x886D0000u, 46u}, // ndc -> Latn
- {0xC86D0000u, 46u}, // nds -> Latn
+ {0xBC0D0000u, 45u}, // nap -> Latn
+ {0xC00D0000u, 45u}, // naq -> Latn
+ {0xC80D0000u, 45u}, // nas -> Latn
+ {0x6E620000u, 45u}, // nb -> Latn
+ {0x804D0000u, 45u}, // nca -> Latn
+ {0x904D0000u, 45u}, // nce -> Latn
+ {0x944D0000u, 45u}, // ncf -> Latn
+ {0x9C4D0000u, 45u}, // nch -> Latn
+ {0xB84D0000u, 45u}, // nco -> Latn
+ {0xD04D0000u, 45u}, // ncu -> Latn
+ {0x6E640000u, 45u}, // nd -> Latn
+ {0x886D0000u, 45u}, // ndc -> Latn
+ {0xC86D0000u, 45u}, // nds -> Latn
{0x6E650000u, 19u}, // ne -> Deva
- {0x848D0000u, 46u}, // neb -> Latn
+ {0x848D0000u, 45u}, // neb -> Latn
{0xD88D0000u, 19u}, // new -> Deva
- {0xDC8D0000u, 46u}, // nex -> Latn
- {0xC4AD0000u, 46u}, // nfr -> Latn
- {0x6E670000u, 46u}, // ng -> Latn
- {0x80CD0000u, 46u}, // nga -> Latn
- {0x84CD0000u, 46u}, // ngb -> Latn
- {0xACCD0000u, 46u}, // ngl -> Latn
- {0x84ED0000u, 46u}, // nhb -> Latn
- {0x90ED0000u, 46u}, // nhe -> Latn
- {0xD8ED0000u, 46u}, // nhw -> Latn
- {0x950D0000u, 46u}, // nif -> Latn
- {0xA10D0000u, 46u}, // nii -> Latn
- {0xA50D0000u, 46u}, // nij -> Latn
- {0xB50D0000u, 46u}, // nin -> Latn
- {0xD10D0000u, 46u}, // niu -> Latn
- {0xE10D0000u, 46u}, // niy -> Latn
- {0xE50D0000u, 46u}, // niz -> Latn
- {0xB92D0000u, 46u}, // njo -> Latn
- {0x994D0000u, 46u}, // nkg -> Latn
- {0xB94D0000u, 46u}, // nko -> Latn
- {0x6E6C0000u, 46u}, // nl -> Latn
- {0x998D0000u, 46u}, // nmg -> Latn
- {0xE58D0000u, 46u}, // nmz -> Latn
- {0x6E6E0000u, 46u}, // nn -> Latn
- {0x95AD0000u, 46u}, // nnf -> Latn
- {0x9DAD0000u, 46u}, // nnh -> Latn
- {0xA9AD0000u, 46u}, // nnk -> Latn
- {0xB1AD0000u, 46u}, // nnm -> Latn
- {0xBDAD0000u, 95u}, // nnp -> Wcho
- {0x6E6F0000u, 46u}, // no -> Latn
- {0x8DCD0000u, 44u}, // nod -> Lana
+ {0xDC8D0000u, 45u}, // nex -> Latn
+ {0xC4AD0000u, 45u}, // nfr -> Latn
+ {0x6E670000u, 45u}, // ng -> Latn
+ {0x80CD0000u, 45u}, // nga -> Latn
+ {0x84CD0000u, 45u}, // ngb -> Latn
+ {0xACCD0000u, 45u}, // ngl -> Latn
+ {0x84ED0000u, 45u}, // nhb -> Latn
+ {0x90ED0000u, 45u}, // nhe -> Latn
+ {0xD8ED0000u, 45u}, // nhw -> Latn
+ {0x950D0000u, 45u}, // nif -> Latn
+ {0xA10D0000u, 45u}, // nii -> Latn
+ {0xA50D0000u, 45u}, // nij -> Latn
+ {0xB50D0000u, 45u}, // nin -> Latn
+ {0xD10D0000u, 45u}, // niu -> Latn
+ {0xE10D0000u, 45u}, // niy -> Latn
+ {0xE50D0000u, 45u}, // niz -> Latn
+ {0xB92D0000u, 45u}, // njo -> Latn
+ {0x994D0000u, 45u}, // nkg -> Latn
+ {0xB94D0000u, 45u}, // nko -> Latn
+ {0x6E6C0000u, 45u}, // nl -> Latn
+ {0x998D0000u, 45u}, // nmg -> Latn
+ {0xE58D0000u, 45u}, // nmz -> Latn
+ {0x6E6E0000u, 45u}, // nn -> Latn
+ {0x95AD0000u, 45u}, // nnf -> Latn
+ {0x9DAD0000u, 45u}, // nnh -> Latn
+ {0xA9AD0000u, 45u}, // nnk -> Latn
+ {0xB1AD0000u, 45u}, // nnm -> Latn
+ {0xBDAD0000u, 98u}, // nnp -> Wcho
+ {0x6E6F0000u, 45u}, // no -> Latn
+ {0x8DCD0000u, 43u}, // nod -> Lana
{0x91CD0000u, 19u}, // noe -> Deva
- {0xB5CD0000u, 73u}, // non -> Runr
- {0xBDCD0000u, 46u}, // nop -> Latn
- {0xD1CD0000u, 46u}, // nou -> Latn
- {0xBA0D0000u, 61u}, // nqo -> Nkoo
- {0x6E720000u, 46u}, // nr -> Latn
- {0x862D0000u, 46u}, // nrb -> Latn
+ {0xB5CD0000u, 74u}, // non -> Runr
+ {0xBDCD0000u, 45u}, // nop -> Latn
+ {0xD1CD0000u, 45u}, // nou -> Latn
+ {0xBA0D0000u, 60u}, // nqo -> Nkoo
+ {0x6E720000u, 45u}, // nr -> Latn
+ {0x862D0000u, 45u}, // nrb -> Latn
{0xAA4D0000u, 11u}, // nsk -> Cans
- {0xB64D0000u, 46u}, // nsn -> Latn
- {0xBA4D0000u, 46u}, // nso -> Latn
- {0xCA4D0000u, 46u}, // nss -> Latn
- {0xB26D0000u, 46u}, // ntm -> Latn
- {0xC66D0000u, 46u}, // ntr -> Latn
- {0xA28D0000u, 46u}, // nui -> Latn
- {0xBE8D0000u, 46u}, // nup -> Latn
- {0xCA8D0000u, 46u}, // nus -> Latn
- {0xD68D0000u, 46u}, // nuv -> Latn
- {0xDE8D0000u, 46u}, // nux -> Latn
- {0x6E760000u, 46u}, // nv -> Latn
- {0x86CD0000u, 46u}, // nwb -> Latn
- {0xC2ED0000u, 46u}, // nxq -> Latn
- {0xC6ED0000u, 46u}, // nxr -> Latn
- {0x6E790000u, 46u}, // ny -> Latn
- {0xB30D0000u, 46u}, // nym -> Latn
- {0xB70D0000u, 46u}, // nyn -> Latn
- {0xA32D0000u, 46u}, // nzi -> Latn
- {0x6F630000u, 46u}, // oc -> Latn
- {0x88CE0000u, 46u}, // ogc -> Latn
- {0xC54E0000u, 46u}, // okr -> Latn
- {0xD54E0000u, 46u}, // okv -> Latn
- {0x6F6D0000u, 46u}, // om -> Latn
- {0x99AE0000u, 46u}, // ong -> Latn
- {0xB5AE0000u, 46u}, // onn -> Latn
- {0xC9AE0000u, 46u}, // ons -> Latn
- {0xB1EE0000u, 46u}, // opm -> Latn
- {0x6F720000u, 66u}, // or -> Orya
- {0xBA2E0000u, 46u}, // oro -> Latn
+ {0xB64D0000u, 45u}, // nsn -> Latn
+ {0xBA4D0000u, 45u}, // nso -> Latn
+ {0xCA4D0000u, 45u}, // nss -> Latn
+ {0xCE4D0000u, 94u}, // nst -> Tnsa
+ {0xB26D0000u, 45u}, // ntm -> Latn
+ {0xC66D0000u, 45u}, // ntr -> Latn
+ {0xA28D0000u, 45u}, // nui -> Latn
+ {0xBE8D0000u, 45u}, // nup -> Latn
+ {0xCA8D0000u, 45u}, // nus -> Latn
+ {0xD68D0000u, 45u}, // nuv -> Latn
+ {0xDE8D0000u, 45u}, // nux -> Latn
+ {0x6E760000u, 45u}, // nv -> Latn
+ {0x86CD0000u, 45u}, // nwb -> Latn
+ {0xC2ED0000u, 45u}, // nxq -> Latn
+ {0xC6ED0000u, 45u}, // nxr -> Latn
+ {0x6E790000u, 45u}, // ny -> Latn
+ {0xB30D0000u, 45u}, // nym -> Latn
+ {0xB70D0000u, 45u}, // nyn -> Latn
+ {0xA32D0000u, 45u}, // nzi -> Latn
+ {0x6F630000u, 45u}, // oc -> Latn
+ {0x88CE0000u, 45u}, // ogc -> Latn
+ {0xC54E0000u, 45u}, // okr -> Latn
+ {0xD54E0000u, 45u}, // okv -> Latn
+ {0x6F6D0000u, 45u}, // om -> Latn
+ {0x99AE0000u, 45u}, // ong -> Latn
+ {0xB5AE0000u, 45u}, // onn -> Latn
+ {0xC9AE0000u, 45u}, // ons -> Latn
+ {0xB1EE0000u, 45u}, // opm -> Latn
+ {0x6F720000u, 65u}, // or -> Orya
+ {0xBA2E0000u, 45u}, // oro -> Latn
{0xD22E0000u, 2u}, // oru -> Arab
{0x6F730000u, 18u}, // os -> Cyrl
- {0x824E0000u, 67u}, // osa -> Osge
+ {0x824E0000u, 66u}, // osa -> Osge
{0x826E0000u, 2u}, // ota -> Arab
- {0xAA6E0000u, 65u}, // otk -> Orkh
- {0xB32E0000u, 46u}, // ozm -> Latn
+ {0xAA6E0000u, 64u}, // otk -> Orkh
+ {0xA28E0000u, 67u}, // oui -> Ougr
+ {0xB32E0000u, 45u}, // ozm -> Latn
{0x70610000u, 28u}, // pa -> Guru
{0x7061504Bu, 2u}, // pa-PK -> Arab
- {0x980F0000u, 46u}, // pag -> Latn
+ {0x980F0000u, 45u}, // pag -> Latn
{0xAC0F0000u, 69u}, // pal -> Phli
- {0xB00F0000u, 46u}, // pam -> Latn
- {0xBC0F0000u, 46u}, // pap -> Latn
- {0xD00F0000u, 46u}, // pau -> Latn
- {0xA02F0000u, 46u}, // pbi -> Latn
- {0x8C4F0000u, 46u}, // pcd -> Latn
- {0xB04F0000u, 46u}, // pcm -> Latn
- {0x886F0000u, 46u}, // pdc -> Latn
- {0xCC6F0000u, 46u}, // pdt -> Latn
- {0x8C8F0000u, 46u}, // ped -> Latn
- {0xB88F0000u, 96u}, // peo -> Xpeo
- {0xDC8F0000u, 46u}, // pex -> Latn
- {0xACAF0000u, 46u}, // pfl -> Latn
+ {0xB00F0000u, 45u}, // pam -> Latn
+ {0xBC0F0000u, 45u}, // pap -> Latn
+ {0xD00F0000u, 45u}, // pau -> Latn
+ {0xA02F0000u, 45u}, // pbi -> Latn
+ {0x8C4F0000u, 45u}, // pcd -> Latn
+ {0xB04F0000u, 45u}, // pcm -> Latn
+ {0x886F0000u, 45u}, // pdc -> Latn
+ {0xCC6F0000u, 45u}, // pdt -> Latn
+ {0x8C8F0000u, 45u}, // ped -> Latn
+ {0xB88F0000u, 99u}, // peo -> Xpeo
+ {0xDC8F0000u, 45u}, // pex -> Latn
+ {0xACAF0000u, 45u}, // pfl -> Latn
{0xACEF0000u, 2u}, // phl -> Arab
{0xB4EF0000u, 70u}, // phn -> Phnx
- {0xAD0F0000u, 46u}, // pil -> Latn
- {0xBD0F0000u, 46u}, // pip -> Latn
+ {0xAD0F0000u, 45u}, // pil -> Latn
+ {0xBD0F0000u, 45u}, // pip -> Latn
{0x814F0000u, 9u}, // pka -> Brah
- {0xB94F0000u, 46u}, // pko -> Latn
- {0x706C0000u, 46u}, // pl -> Latn
- {0x816F0000u, 46u}, // pla -> Latn
- {0xC98F0000u, 46u}, // pms -> Latn
- {0x99AF0000u, 46u}, // png -> Latn
- {0xB5AF0000u, 46u}, // pnn -> Latn
+ {0xB94F0000u, 45u}, // pko -> Latn
+ {0x706C0000u, 45u}, // pl -> Latn
+ {0x816F0000u, 45u}, // pla -> Latn
+ {0xC98F0000u, 45u}, // pms -> Latn
+ {0x99AF0000u, 45u}, // png -> Latn
+ {0xB5AF0000u, 45u}, // pnn -> Latn
{0xCDAF0000u, 26u}, // pnt -> Grek
- {0xB5CF0000u, 46u}, // pon -> Latn
+ {0xB5CF0000u, 45u}, // pon -> Latn
{0x81EF0000u, 19u}, // ppa -> Deva
- {0xB9EF0000u, 46u}, // ppo -> Latn
- {0x822F0000u, 39u}, // pra -> Khar
+ {0xB9EF0000u, 45u}, // ppo -> Latn
+ {0x822F0000u, 38u}, // pra -> Khar
{0x8E2F0000u, 2u}, // prd -> Arab
- {0x9A2F0000u, 46u}, // prg -> Latn
+ {0x9A2F0000u, 45u}, // prg -> Latn
{0x70730000u, 2u}, // ps -> Arab
- {0xCA4F0000u, 46u}, // pss -> Latn
- {0x70740000u, 46u}, // pt -> Latn
- {0xBE6F0000u, 46u}, // ptp -> Latn
- {0xD28F0000u, 46u}, // puu -> Latn
- {0x82CF0000u, 46u}, // pwa -> Latn
- {0x71750000u, 46u}, // qu -> Latn
- {0x8A900000u, 46u}, // quc -> Latn
- {0x9A900000u, 46u}, // qug -> Latn
- {0xA0110000u, 46u}, // rai -> Latn
+ {0xCA4F0000u, 45u}, // pss -> Latn
+ {0x70740000u, 45u}, // pt -> Latn
+ {0xBE6F0000u, 45u}, // ptp -> Latn
+ {0xD28F0000u, 45u}, // puu -> Latn
+ {0x82CF0000u, 45u}, // pwa -> Latn
+ {0x71750000u, 45u}, // qu -> Latn
+ {0x8A900000u, 45u}, // quc -> Latn
+ {0x9A900000u, 45u}, // qug -> Latn
+ {0xA0110000u, 45u}, // rai -> Latn
{0xA4110000u, 19u}, // raj -> Deva
- {0xB8110000u, 46u}, // rao -> Latn
- {0x94510000u, 46u}, // rcf -> Latn
- {0xA4910000u, 46u}, // rej -> Latn
- {0xAC910000u, 46u}, // rel -> Latn
- {0xC8910000u, 46u}, // res -> Latn
- {0xB4D10000u, 46u}, // rgn -> Latn
- {0x98F10000u, 2u}, // rhg -> Arab
- {0x81110000u, 46u}, // ria -> Latn
- {0x95110000u, 89u}, // rif -> Tfng
- {0x95114E4Cu, 46u}, // rif-NL -> Latn
+ {0xB8110000u, 45u}, // rao -> Latn
+ {0x94510000u, 45u}, // rcf -> Latn
+ {0xA4910000u, 45u}, // rej -> Latn
+ {0xAC910000u, 45u}, // rel -> Latn
+ {0xC8910000u, 45u}, // res -> Latn
+ {0xB4D10000u, 45u}, // rgn -> Latn
+ {0x98F10000u, 73u}, // rhg -> Rohg
+ {0x81110000u, 45u}, // ria -> Latn
+ {0x95110000u, 90u}, // rif -> Tfng
+ {0x95114E4Cu, 45u}, // rif-NL -> Latn
{0xC9310000u, 19u}, // rjs -> Deva
{0xCD510000u, 8u}, // rkt -> Beng
- {0x726D0000u, 46u}, // rm -> Latn
- {0x95910000u, 46u}, // rmf -> Latn
- {0xB9910000u, 46u}, // rmo -> Latn
+ {0x726D0000u, 45u}, // rm -> Latn
+ {0x95910000u, 45u}, // rmf -> Latn
+ {0xB9910000u, 45u}, // rmo -> Latn
{0xCD910000u, 2u}, // rmt -> Arab
- {0xD1910000u, 46u}, // rmu -> Latn
- {0x726E0000u, 46u}, // rn -> Latn
- {0x81B10000u, 46u}, // rna -> Latn
- {0x99B10000u, 46u}, // rng -> Latn
- {0x726F0000u, 46u}, // ro -> Latn
- {0x85D10000u, 46u}, // rob -> Latn
- {0x95D10000u, 46u}, // rof -> Latn
- {0xB9D10000u, 46u}, // roo -> Latn
- {0xBA310000u, 46u}, // rro -> Latn
- {0xB2710000u, 46u}, // rtm -> Latn
+ {0xD1910000u, 45u}, // rmu -> Latn
+ {0x726E0000u, 45u}, // rn -> Latn
+ {0x81B10000u, 45u}, // rna -> Latn
+ {0x99B10000u, 45u}, // rng -> Latn
+ {0x726F0000u, 45u}, // ro -> Latn
+ {0x85D10000u, 45u}, // rob -> Latn
+ {0x95D10000u, 45u}, // rof -> Latn
+ {0xB9D10000u, 45u}, // roo -> Latn
+ {0xBA310000u, 45u}, // rro -> Latn
+ {0xB2710000u, 45u}, // rtm -> Latn
{0x72750000u, 18u}, // ru -> Cyrl
{0x92910000u, 18u}, // rue -> Cyrl
- {0x9A910000u, 46u}, // rug -> Latn
- {0x72770000u, 46u}, // rw -> Latn
- {0xAAD10000u, 46u}, // rwk -> Latn
- {0xBAD10000u, 46u}, // rwo -> Latn
- {0xD3110000u, 38u}, // ryu -> Kana
+ {0x9A910000u, 45u}, // rug -> Latn
+ {0x72770000u, 45u}, // rw -> Latn
+ {0xAAD10000u, 45u}, // rwk -> Latn
+ {0xBAD10000u, 45u}, // rwo -> Latn
+ {0xD3110000u, 37u}, // ryu -> Kana
{0x73610000u, 19u}, // sa -> Deva
- {0x94120000u, 46u}, // saf -> Latn
+ {0x94120000u, 45u}, // saf -> Latn
{0x9C120000u, 18u}, // sah -> Cyrl
- {0xC0120000u, 46u}, // saq -> Latn
- {0xC8120000u, 46u}, // sas -> Latn
- {0xCC120000u, 64u}, // sat -> Olck
- {0xD4120000u, 46u}, // sav -> Latn
- {0xE4120000u, 76u}, // saz -> Saur
- {0x80320000u, 46u}, // sba -> Latn
- {0x90320000u, 46u}, // sbe -> Latn
- {0xBC320000u, 46u}, // sbp -> Latn
- {0x73630000u, 46u}, // sc -> Latn
+ {0xC0120000u, 45u}, // saq -> Latn
+ {0xC8120000u, 45u}, // sas -> Latn
+ {0xCC120000u, 63u}, // sat -> Olck
+ {0xD4120000u, 45u}, // sav -> Latn
+ {0xE4120000u, 77u}, // saz -> Saur
+ {0x80320000u, 45u}, // sba -> Latn
+ {0x90320000u, 45u}, // sbe -> Latn
+ {0xBC320000u, 45u}, // sbp -> Latn
+ {0x73630000u, 45u}, // sc -> Latn
{0xA8520000u, 19u}, // sck -> Deva
{0xAC520000u, 2u}, // scl -> Arab
- {0xB4520000u, 46u}, // scn -> Latn
- {0xB8520000u, 46u}, // sco -> Latn
- {0xC8520000u, 46u}, // scs -> Latn
+ {0xB4520000u, 45u}, // scn -> Latn
+ {0xB8520000u, 45u}, // sco -> Latn
+ {0xC8520000u, 45u}, // scs -> Latn
{0x73640000u, 2u}, // sd -> Arab
- {0x88720000u, 46u}, // sdc -> Latn
+ {0x88720000u, 45u}, // sdc -> Latn
{0x9C720000u, 2u}, // sdh -> Arab
- {0x73650000u, 46u}, // se -> Latn
- {0x94920000u, 46u}, // sef -> Latn
- {0x9C920000u, 46u}, // seh -> Latn
- {0xA0920000u, 46u}, // sei -> Latn
- {0xC8920000u, 46u}, // ses -> Latn
- {0x73670000u, 46u}, // sg -> Latn
- {0x80D20000u, 63u}, // sga -> Ogam
- {0xC8D20000u, 46u}, // sgs -> Latn
+ {0x73650000u, 45u}, // se -> Latn
+ {0x94920000u, 45u}, // sef -> Latn
+ {0x9C920000u, 45u}, // seh -> Latn
+ {0xA0920000u, 45u}, // sei -> Latn
+ {0xC8920000u, 45u}, // ses -> Latn
+ {0x73670000u, 45u}, // sg -> Latn
+ {0x80D20000u, 62u}, // sga -> Ogam
+ {0xC8D20000u, 45u}, // sgs -> Latn
{0xD8D20000u, 21u}, // sgw -> Ethi
- {0xE4D20000u, 46u}, // sgz -> Latn
- {0x73680000u, 46u}, // sh -> Latn
- {0xA0F20000u, 89u}, // shi -> Tfng
- {0xA8F20000u, 46u}, // shk -> Latn
- {0xB4F20000u, 59u}, // shn -> Mymr
+ {0xE4D20000u, 45u}, // sgz -> Latn
+ {0x73680000u, 45u}, // sh -> Latn
+ {0xA0F20000u, 90u}, // shi -> Tfng
+ {0xA8F20000u, 45u}, // shk -> Latn
+ {0xB4F20000u, 58u}, // shn -> Mymr
{0xD0F20000u, 2u}, // shu -> Arab
- {0x73690000u, 78u}, // si -> Sinh
- {0x8D120000u, 46u}, // sid -> Latn
- {0x99120000u, 46u}, // sig -> Latn
- {0xAD120000u, 46u}, // sil -> Latn
- {0xB1120000u, 46u}, // sim -> Latn
- {0xC5320000u, 46u}, // sjr -> Latn
- {0x736B0000u, 46u}, // sk -> Latn
- {0x89520000u, 46u}, // skc -> Latn
+ {0x73690000u, 79u}, // si -> Sinh
+ {0x8D120000u, 45u}, // sid -> Latn
+ {0x99120000u, 45u}, // sig -> Latn
+ {0xAD120000u, 45u}, // sil -> Latn
+ {0xB1120000u, 45u}, // sim -> Latn
+ {0xC5320000u, 45u}, // sjr -> Latn
+ {0x736B0000u, 45u}, // sk -> Latn
+ {0x89520000u, 45u}, // skc -> Latn
{0xC5520000u, 2u}, // skr -> Arab
- {0xC9520000u, 46u}, // sks -> Latn
- {0x736C0000u, 46u}, // sl -> Latn
- {0x8D720000u, 46u}, // sld -> Latn
- {0xA1720000u, 46u}, // sli -> Latn
- {0xAD720000u, 46u}, // sll -> Latn
- {0xE1720000u, 46u}, // sly -> Latn
- {0x736D0000u, 46u}, // sm -> Latn
- {0x81920000u, 46u}, // sma -> Latn
- {0xA5920000u, 46u}, // smj -> Latn
- {0xB5920000u, 46u}, // smn -> Latn
- {0xBD920000u, 74u}, // smp -> Samr
- {0xC1920000u, 46u}, // smq -> Latn
- {0xC9920000u, 46u}, // sms -> Latn
- {0x736E0000u, 46u}, // sn -> Latn
- {0x89B20000u, 46u}, // snc -> Latn
- {0xA9B20000u, 46u}, // snk -> Latn
- {0xBDB20000u, 46u}, // snp -> Latn
- {0xDDB20000u, 46u}, // snx -> Latn
- {0xE1B20000u, 46u}, // sny -> Latn
- {0x736F0000u, 46u}, // so -> Latn
- {0x99D20000u, 79u}, // sog -> Sogd
- {0xA9D20000u, 46u}, // sok -> Latn
- {0xC1D20000u, 46u}, // soq -> Latn
- {0xD1D20000u, 91u}, // sou -> Thai
- {0xE1D20000u, 46u}, // soy -> Latn
- {0x8DF20000u, 46u}, // spd -> Latn
- {0xADF20000u, 46u}, // spl -> Latn
- {0xC9F20000u, 46u}, // sps -> Latn
- {0x73710000u, 46u}, // sq -> Latn
+ {0xC9520000u, 45u}, // sks -> Latn
+ {0x736C0000u, 45u}, // sl -> Latn
+ {0x8D720000u, 45u}, // sld -> Latn
+ {0xA1720000u, 45u}, // sli -> Latn
+ {0xAD720000u, 45u}, // sll -> Latn
+ {0xE1720000u, 45u}, // sly -> Latn
+ {0x736D0000u, 45u}, // sm -> Latn
+ {0x81920000u, 45u}, // sma -> Latn
+ {0xA5920000u, 45u}, // smj -> Latn
+ {0xB5920000u, 45u}, // smn -> Latn
+ {0xBD920000u, 75u}, // smp -> Samr
+ {0xC1920000u, 45u}, // smq -> Latn
+ {0xC9920000u, 45u}, // sms -> Latn
+ {0x736E0000u, 45u}, // sn -> Latn
+ {0x89B20000u, 45u}, // snc -> Latn
+ {0xA9B20000u, 45u}, // snk -> Latn
+ {0xBDB20000u, 45u}, // snp -> Latn
+ {0xDDB20000u, 45u}, // snx -> Latn
+ {0xE1B20000u, 45u}, // sny -> Latn
+ {0x736F0000u, 45u}, // so -> Latn
+ {0x99D20000u, 80u}, // sog -> Sogd
+ {0xA9D20000u, 45u}, // sok -> Latn
+ {0xC1D20000u, 45u}, // soq -> Latn
+ {0xD1D20000u, 92u}, // sou -> Thai
+ {0xE1D20000u, 45u}, // soy -> Latn
+ {0x8DF20000u, 45u}, // spd -> Latn
+ {0xADF20000u, 45u}, // spl -> Latn
+ {0xC9F20000u, 45u}, // sps -> Latn
+ {0x73710000u, 45u}, // sq -> Latn
{0x73720000u, 18u}, // sr -> Cyrl
- {0x73724D45u, 46u}, // sr-ME -> Latn
- {0x7372524Fu, 46u}, // sr-RO -> Latn
- {0x73725255u, 46u}, // sr-RU -> Latn
- {0x73725452u, 46u}, // sr-TR -> Latn
- {0x86320000u, 80u}, // srb -> Sora
- {0xB6320000u, 46u}, // srn -> Latn
- {0xC6320000u, 46u}, // srr -> Latn
+ {0x73724D45u, 45u}, // sr-ME -> Latn
+ {0x7372524Fu, 45u}, // sr-RO -> Latn
+ {0x73725255u, 45u}, // sr-RU -> Latn
+ {0x73725452u, 45u}, // sr-TR -> Latn
+ {0x86320000u, 81u}, // srb -> Sora
+ {0xB6320000u, 45u}, // srn -> Latn
+ {0xC6320000u, 45u}, // srr -> Latn
{0xDE320000u, 19u}, // srx -> Deva
- {0x73730000u, 46u}, // ss -> Latn
- {0x8E520000u, 46u}, // ssd -> Latn
- {0x9A520000u, 46u}, // ssg -> Latn
- {0xE2520000u, 46u}, // ssy -> Latn
- {0x73740000u, 46u}, // st -> Latn
- {0xAA720000u, 46u}, // stk -> Latn
- {0xC2720000u, 46u}, // stq -> Latn
- {0x73750000u, 46u}, // su -> Latn
- {0x82920000u, 46u}, // sua -> Latn
- {0x92920000u, 46u}, // sue -> Latn
- {0xAA920000u, 46u}, // suk -> Latn
- {0xC6920000u, 46u}, // sur -> Latn
- {0xCA920000u, 46u}, // sus -> Latn
- {0x73760000u, 46u}, // sv -> Latn
- {0x73770000u, 46u}, // sw -> Latn
+ {0x73730000u, 45u}, // ss -> Latn
+ {0x8E520000u, 45u}, // ssd -> Latn
+ {0x9A520000u, 45u}, // ssg -> Latn
+ {0xE2520000u, 45u}, // ssy -> Latn
+ {0x73740000u, 45u}, // st -> Latn
+ {0xAA720000u, 45u}, // stk -> Latn
+ {0xC2720000u, 45u}, // stq -> Latn
+ {0x73750000u, 45u}, // su -> Latn
+ {0x82920000u, 45u}, // sua -> Latn
+ {0x92920000u, 45u}, // sue -> Latn
+ {0xAA920000u, 45u}, // suk -> Latn
+ {0xC6920000u, 45u}, // sur -> Latn
+ {0xCA920000u, 45u}, // sus -> Latn
+ {0x73760000u, 45u}, // sv -> Latn
+ {0x73770000u, 45u}, // sw -> Latn
{0x86D20000u, 2u}, // swb -> Arab
- {0x8AD20000u, 46u}, // swc -> Latn
- {0x9AD20000u, 46u}, // swg -> Latn
- {0xBED20000u, 46u}, // swp -> Latn
+ {0x8AD20000u, 45u}, // swc -> Latn
+ {0x9AD20000u, 45u}, // swg -> Latn
+ {0xBED20000u, 45u}, // swp -> Latn
{0xD6D20000u, 19u}, // swv -> Deva
- {0xB6F20000u, 46u}, // sxn -> Latn
- {0xDAF20000u, 46u}, // sxw -> Latn
+ {0xB6F20000u, 45u}, // sxn -> Latn
+ {0xDAF20000u, 45u}, // sxw -> Latn
{0xAF120000u, 8u}, // syl -> Beng
- {0xC7120000u, 82u}, // syr -> Syrc
- {0xAF320000u, 46u}, // szl -> Latn
- {0x74610000u, 85u}, // ta -> Taml
+ {0xC7120000u, 83u}, // syr -> Syrc
+ {0xAF320000u, 45u}, // szl -> Latn
+ {0x74610000u, 86u}, // ta -> Taml
{0xA4130000u, 19u}, // taj -> Deva
- {0xAC130000u, 46u}, // tal -> Latn
- {0xB4130000u, 46u}, // tan -> Latn
- {0xC0130000u, 46u}, // taq -> Latn
- {0x88330000u, 46u}, // tbc -> Latn
- {0x8C330000u, 46u}, // tbd -> Latn
- {0x94330000u, 46u}, // tbf -> Latn
- {0x98330000u, 46u}, // tbg -> Latn
- {0xB8330000u, 46u}, // tbo -> Latn
- {0xD8330000u, 46u}, // tbw -> Latn
- {0xE4330000u, 46u}, // tbz -> Latn
- {0xA0530000u, 46u}, // tci -> Latn
- {0xE0530000u, 42u}, // tcy -> Knda
- {0x8C730000u, 83u}, // tdd -> Tale
+ {0xAC130000u, 45u}, // tal -> Latn
+ {0xB4130000u, 45u}, // tan -> Latn
+ {0xC0130000u, 45u}, // taq -> Latn
+ {0x88330000u, 45u}, // tbc -> Latn
+ {0x8C330000u, 45u}, // tbd -> Latn
+ {0x94330000u, 45u}, // tbf -> Latn
+ {0x98330000u, 45u}, // tbg -> Latn
+ {0xB8330000u, 45u}, // tbo -> Latn
+ {0xD8330000u, 45u}, // tbw -> Latn
+ {0xE4330000u, 45u}, // tbz -> Latn
+ {0xA0530000u, 45u}, // tci -> Latn
+ {0xE0530000u, 41u}, // tcy -> Knda
+ {0x8C730000u, 84u}, // tdd -> Tale
{0x98730000u, 19u}, // tdg -> Deva
{0x9C730000u, 19u}, // tdh -> Deva
- {0xD0730000u, 46u}, // tdu -> Latn
- {0x74650000u, 88u}, // te -> Telu
- {0x8C930000u, 46u}, // ted -> Latn
- {0xB0930000u, 46u}, // tem -> Latn
- {0xB8930000u, 46u}, // teo -> Latn
- {0xCC930000u, 46u}, // tet -> Latn
- {0xA0B30000u, 46u}, // tfi -> Latn
+ {0xD0730000u, 45u}, // tdu -> Latn
+ {0x74650000u, 89u}, // te -> Telu
+ {0x8C930000u, 45u}, // ted -> Latn
+ {0xB0930000u, 45u}, // tem -> Latn
+ {0xB8930000u, 45u}, // teo -> Latn
+ {0xCC930000u, 45u}, // tet -> Latn
+ {0xA0B30000u, 45u}, // tfi -> Latn
{0x74670000u, 18u}, // tg -> Cyrl
{0x7467504Bu, 2u}, // tg-PK -> Arab
- {0x88D30000u, 46u}, // tgc -> Latn
- {0xB8D30000u, 46u}, // tgo -> Latn
- {0xD0D30000u, 46u}, // tgu -> Latn
- {0x74680000u, 91u}, // th -> Thai
+ {0x88D30000u, 45u}, // tgc -> Latn
+ {0xB8D30000u, 45u}, // tgo -> Latn
+ {0xD0D30000u, 45u}, // tgu -> Latn
+ {0x74680000u, 92u}, // th -> Thai
{0xACF30000u, 19u}, // thl -> Deva
{0xC0F30000u, 19u}, // thq -> Deva
{0xC4F30000u, 19u}, // thr -> Deva
{0x74690000u, 21u}, // ti -> Ethi
- {0x95130000u, 46u}, // tif -> Latn
+ {0x95130000u, 45u}, // tif -> Latn
{0x99130000u, 21u}, // tig -> Ethi
- {0xA9130000u, 46u}, // tik -> Latn
- {0xB1130000u, 46u}, // tim -> Latn
- {0xB9130000u, 46u}, // tio -> Latn
- {0xD5130000u, 46u}, // tiv -> Latn
- {0x746B0000u, 46u}, // tk -> Latn
- {0xAD530000u, 46u}, // tkl -> Latn
- {0xC5530000u, 46u}, // tkr -> Latn
+ {0xA9130000u, 45u}, // tik -> Latn
+ {0xB1130000u, 45u}, // tim -> Latn
+ {0xB9130000u, 45u}, // tio -> Latn
+ {0xD5130000u, 45u}, // tiv -> Latn
+ {0x746B0000u, 45u}, // tk -> Latn
+ {0xAD530000u, 45u}, // tkl -> Latn
+ {0xC5530000u, 45u}, // tkr -> Latn
{0xCD530000u, 19u}, // tkt -> Deva
- {0x746C0000u, 46u}, // tl -> Latn
- {0x95730000u, 46u}, // tlf -> Latn
- {0xDD730000u, 46u}, // tlx -> Latn
- {0xE1730000u, 46u}, // tly -> Latn
- {0x9D930000u, 46u}, // tmh -> Latn
- {0xE1930000u, 46u}, // tmy -> Latn
- {0x746E0000u, 46u}, // tn -> Latn
- {0x9DB30000u, 46u}, // tnh -> Latn
- {0x746F0000u, 46u}, // to -> Latn
- {0x95D30000u, 46u}, // tof -> Latn
- {0x99D30000u, 46u}, // tog -> Latn
- {0xC1D30000u, 46u}, // toq -> Latn
- {0xA1F30000u, 46u}, // tpi -> Latn
- {0xB1F30000u, 46u}, // tpm -> Latn
- {0xE5F30000u, 46u}, // tpz -> Latn
- {0xBA130000u, 46u}, // tqo -> Latn
- {0x74720000u, 46u}, // tr -> Latn
- {0xD2330000u, 46u}, // tru -> Latn
- {0xD6330000u, 46u}, // trv -> Latn
+ {0x746C0000u, 45u}, // tl -> Latn
+ {0x95730000u, 45u}, // tlf -> Latn
+ {0xDD730000u, 45u}, // tlx -> Latn
+ {0xE1730000u, 45u}, // tly -> Latn
+ {0x9D930000u, 45u}, // tmh -> Latn
+ {0xE1930000u, 45u}, // tmy -> Latn
+ {0x746E0000u, 45u}, // tn -> Latn
+ {0x9DB30000u, 45u}, // tnh -> Latn
+ {0x746F0000u, 45u}, // to -> Latn
+ {0x95D30000u, 45u}, // tof -> Latn
+ {0x99D30000u, 45u}, // tog -> Latn
+ {0xC1D30000u, 45u}, // toq -> Latn
+ {0xA1F30000u, 45u}, // tpi -> Latn
+ {0xB1F30000u, 45u}, // tpm -> Latn
+ {0xE5F30000u, 45u}, // tpz -> Latn
+ {0xBA130000u, 45u}, // tqo -> Latn
+ {0x74720000u, 45u}, // tr -> Latn
+ {0xD2330000u, 45u}, // tru -> Latn
+ {0xD6330000u, 45u}, // trv -> Latn
{0xDA330000u, 2u}, // trw -> Arab
- {0x74730000u, 46u}, // ts -> Latn
+ {0x74730000u, 45u}, // ts -> Latn
{0x8E530000u, 26u}, // tsd -> Grek
{0x96530000u, 19u}, // tsf -> Deva
- {0x9A530000u, 46u}, // tsg -> Latn
- {0xA6530000u, 92u}, // tsj -> Tibt
- {0xDA530000u, 46u}, // tsw -> Latn
+ {0x9A530000u, 45u}, // tsg -> Latn
+ {0xA6530000u, 93u}, // tsj -> Tibt
+ {0xDA530000u, 45u}, // tsw -> Latn
{0x74740000u, 18u}, // tt -> Cyrl
- {0x8E730000u, 46u}, // ttd -> Latn
- {0x92730000u, 46u}, // tte -> Latn
- {0xA6730000u, 46u}, // ttj -> Latn
- {0xC6730000u, 46u}, // ttr -> Latn
- {0xCA730000u, 91u}, // tts -> Thai
- {0xCE730000u, 46u}, // ttt -> Latn
- {0x9E930000u, 46u}, // tuh -> Latn
- {0xAE930000u, 46u}, // tul -> Latn
- {0xB2930000u, 46u}, // tum -> Latn
- {0xC2930000u, 46u}, // tuq -> Latn
- {0x8EB30000u, 46u}, // tvd -> Latn
- {0xAEB30000u, 46u}, // tvl -> Latn
- {0xD2B30000u, 46u}, // tvu -> Latn
- {0x9ED30000u, 46u}, // twh -> Latn
- {0xC2D30000u, 46u}, // twq -> Latn
- {0x9AF30000u, 86u}, // txg -> Tang
- {0x74790000u, 46u}, // ty -> Latn
- {0x83130000u, 46u}, // tya -> Latn
+ {0x8E730000u, 45u}, // ttd -> Latn
+ {0x92730000u, 45u}, // tte -> Latn
+ {0xA6730000u, 45u}, // ttj -> Latn
+ {0xC6730000u, 45u}, // ttr -> Latn
+ {0xCA730000u, 92u}, // tts -> Thai
+ {0xCE730000u, 45u}, // ttt -> Latn
+ {0x9E930000u, 45u}, // tuh -> Latn
+ {0xAE930000u, 45u}, // tul -> Latn
+ {0xB2930000u, 45u}, // tum -> Latn
+ {0xC2930000u, 45u}, // tuq -> Latn
+ {0x8EB30000u, 45u}, // tvd -> Latn
+ {0xAEB30000u, 45u}, // tvl -> Latn
+ {0xD2B30000u, 45u}, // tvu -> Latn
+ {0x9ED30000u, 45u}, // twh -> Latn
+ {0xC2D30000u, 45u}, // twq -> Latn
+ {0x9AF30000u, 87u}, // txg -> Tang
+ {0xBAF30000u, 95u}, // txo -> Toto
+ {0x74790000u, 45u}, // ty -> Latn
+ {0x83130000u, 45u}, // tya -> Latn
{0xD7130000u, 18u}, // tyv -> Cyrl
- {0xB3330000u, 46u}, // tzm -> Latn
- {0xD0340000u, 46u}, // ubu -> Latn
+ {0xB3330000u, 45u}, // tzm -> Latn
+ {0xD0340000u, 45u}, // ubu -> Latn
{0xA0740000u, 0u}, // udi -> Aghb
{0xB0740000u, 18u}, // udm -> Cyrl
{0x75670000u, 2u}, // ug -> Arab
{0x75674B5Au, 18u}, // ug-KZ -> Cyrl
{0x75674D4Eu, 18u}, // ug-MN -> Cyrl
- {0x80D40000u, 93u}, // uga -> Ugar
+ {0x80D40000u, 96u}, // uga -> Ugar
{0x756B0000u, 18u}, // uk -> Cyrl
- {0xA1740000u, 46u}, // uli -> Latn
- {0x85940000u, 46u}, // umb -> Latn
+ {0xA1740000u, 45u}, // uli -> Latn
+ {0x85940000u, 45u}, // umb -> Latn
{0xC5B40000u, 8u}, // unr -> Beng
{0xC5B44E50u, 19u}, // unr-NP -> Deva
{0xDDB40000u, 8u}, // unx -> Beng
- {0xA9D40000u, 46u}, // uok -> Latn
+ {0xA9D40000u, 45u}, // uok -> Latn
{0x75720000u, 2u}, // ur -> Arab
- {0xA2340000u, 46u}, // uri -> Latn
- {0xCE340000u, 46u}, // urt -> Latn
- {0xDA340000u, 46u}, // urw -> Latn
- {0x82540000u, 46u}, // usa -> Latn
- {0x9E740000u, 46u}, // uth -> Latn
- {0xC6740000u, 46u}, // utr -> Latn
- {0x9EB40000u, 46u}, // uvh -> Latn
- {0xAEB40000u, 46u}, // uvl -> Latn
- {0x757A0000u, 46u}, // uz -> Latn
+ {0xA2340000u, 45u}, // uri -> Latn
+ {0xCE340000u, 45u}, // urt -> Latn
+ {0xDA340000u, 45u}, // urw -> Latn
+ {0x82540000u, 45u}, // usa -> Latn
+ {0x9E740000u, 45u}, // uth -> Latn
+ {0xC6740000u, 45u}, // utr -> Latn
+ {0x9EB40000u, 45u}, // uvh -> Latn
+ {0xAEB40000u, 45u}, // uvl -> Latn
+ {0x757A0000u, 45u}, // uz -> Latn
{0x757A4146u, 2u}, // uz-AF -> Arab
{0x757A434Eu, 18u}, // uz-CN -> Cyrl
- {0x98150000u, 46u}, // vag -> Latn
- {0xA0150000u, 94u}, // vai -> Vaii
- {0xB4150000u, 46u}, // van -> Latn
- {0x76650000u, 46u}, // ve -> Latn
- {0x88950000u, 46u}, // vec -> Latn
- {0xBC950000u, 46u}, // vep -> Latn
- {0x76690000u, 46u}, // vi -> Latn
- {0x89150000u, 46u}, // vic -> Latn
- {0xD5150000u, 46u}, // viv -> Latn
- {0xC9750000u, 46u}, // vls -> Latn
- {0x95950000u, 46u}, // vmf -> Latn
- {0xD9950000u, 46u}, // vmw -> Latn
- {0x766F0000u, 46u}, // vo -> Latn
- {0xCDD50000u, 46u}, // vot -> Latn
- {0xBA350000u, 46u}, // vro -> Latn
- {0xB6950000u, 46u}, // vun -> Latn
- {0xCE950000u, 46u}, // vut -> Latn
- {0x77610000u, 46u}, // wa -> Latn
- {0x90160000u, 46u}, // wae -> Latn
- {0xA4160000u, 46u}, // waj -> Latn
+ {0x98150000u, 45u}, // vag -> Latn
+ {0xA0150000u, 97u}, // vai -> Vaii
+ {0xB4150000u, 45u}, // van -> Latn
+ {0x76650000u, 45u}, // ve -> Latn
+ {0x88950000u, 45u}, // vec -> Latn
+ {0xBC950000u, 45u}, // vep -> Latn
+ {0x76690000u, 45u}, // vi -> Latn
+ {0x89150000u, 45u}, // vic -> Latn
+ {0xD5150000u, 45u}, // viv -> Latn
+ {0xC9750000u, 45u}, // vls -> Latn
+ {0x95950000u, 45u}, // vmf -> Latn
+ {0xD9950000u, 45u}, // vmw -> Latn
+ {0x766F0000u, 45u}, // vo -> Latn
+ {0xCDD50000u, 45u}, // vot -> Latn
+ {0xBA350000u, 45u}, // vro -> Latn
+ {0xB6950000u, 45u}, // vun -> Latn
+ {0xCE950000u, 45u}, // vut -> Latn
+ {0x77610000u, 45u}, // wa -> Latn
+ {0x90160000u, 45u}, // wae -> Latn
+ {0xA4160000u, 45u}, // waj -> Latn
{0xAC160000u, 21u}, // wal -> Ethi
- {0xB4160000u, 46u}, // wan -> Latn
- {0xC4160000u, 46u}, // war -> Latn
- {0xBC360000u, 46u}, // wbp -> Latn
- {0xC0360000u, 88u}, // wbq -> Telu
+ {0xB4160000u, 45u}, // wan -> Latn
+ {0xC4160000u, 45u}, // war -> Latn
+ {0xBC360000u, 45u}, // wbp -> Latn
+ {0xC0360000u, 89u}, // wbq -> Telu
{0xC4360000u, 19u}, // wbr -> Deva
- {0xA0560000u, 46u}, // wci -> Latn
- {0xC4960000u, 46u}, // wer -> Latn
- {0xA0D60000u, 46u}, // wgi -> Latn
- {0x98F60000u, 46u}, // whg -> Latn
- {0x85160000u, 46u}, // wib -> Latn
- {0xD1160000u, 46u}, // wiu -> Latn
- {0xD5160000u, 46u}, // wiv -> Latn
- {0x81360000u, 46u}, // wja -> Latn
- {0xA1360000u, 46u}, // wji -> Latn
- {0xC9760000u, 46u}, // wls -> Latn
- {0xB9960000u, 46u}, // wmo -> Latn
- {0x89B60000u, 46u}, // wnc -> Latn
+ {0xA0560000u, 45u}, // wci -> Latn
+ {0xC4960000u, 45u}, // wer -> Latn
+ {0xA0D60000u, 45u}, // wgi -> Latn
+ {0x98F60000u, 45u}, // whg -> Latn
+ {0x85160000u, 45u}, // wib -> Latn
+ {0xD1160000u, 45u}, // wiu -> Latn
+ {0xD5160000u, 45u}, // wiv -> Latn
+ {0x81360000u, 45u}, // wja -> Latn
+ {0xA1360000u, 45u}, // wji -> Latn
+ {0xC9760000u, 45u}, // wls -> Latn
+ {0xB9960000u, 45u}, // wmo -> Latn
+ {0x89B60000u, 45u}, // wnc -> Latn
{0xA1B60000u, 2u}, // wni -> Arab
- {0xD1B60000u, 46u}, // wnu -> Latn
- {0x776F0000u, 46u}, // wo -> Latn
- {0x85D60000u, 46u}, // wob -> Latn
- {0xC9D60000u, 46u}, // wos -> Latn
- {0xCA360000u, 46u}, // wrs -> Latn
+ {0xD1B60000u, 45u}, // wnu -> Latn
+ {0x776F0000u, 45u}, // wo -> Latn
+ {0x85D60000u, 45u}, // wob -> Latn
+ {0xC9D60000u, 45u}, // wos -> Latn
+ {0xCA360000u, 45u}, // wrs -> Latn
{0x9A560000u, 23u}, // wsg -> Gong
- {0xAA560000u, 46u}, // wsk -> Latn
+ {0xAA560000u, 45u}, // wsk -> Latn
{0xB2760000u, 19u}, // wtm -> Deva
{0xD2960000u, 29u}, // wuu -> Hans
- {0xD6960000u, 46u}, // wuv -> Latn
- {0x82D60000u, 46u}, // wwa -> Latn
- {0xD4170000u, 46u}, // xav -> Latn
- {0xA0370000u, 46u}, // xbi -> Latn
+ {0xD6960000u, 45u}, // wuv -> Latn
+ {0x82D60000u, 45u}, // wwa -> Latn
+ {0xD4170000u, 45u}, // xav -> Latn
+ {0xA0370000u, 45u}, // xbi -> Latn
{0xB8570000u, 15u}, // xco -> Chrs
{0xC4570000u, 12u}, // xcr -> Cari
- {0xC8970000u, 46u}, // xes -> Latn
- {0x78680000u, 46u}, // xh -> Latn
- {0x81770000u, 46u}, // xla -> Latn
- {0x89770000u, 50u}, // xlc -> Lyci
- {0x8D770000u, 51u}, // xld -> Lydi
+ {0xC8970000u, 45u}, // xes -> Latn
+ {0x78680000u, 45u}, // xh -> Latn
+ {0x81770000u, 45u}, // xla -> Latn
+ {0x89770000u, 49u}, // xlc -> Lyci
+ {0x8D770000u, 50u}, // xld -> Lydi
{0x95970000u, 22u}, // xmf -> Geor
- {0xB5970000u, 53u}, // xmn -> Mani
- {0xC5970000u, 55u}, // xmr -> Merc
- {0x81B70000u, 60u}, // xna -> Narb
+ {0xB5970000u, 52u}, // xmn -> Mani
+ {0xC5970000u, 54u}, // xmr -> Merc
+ {0x81B70000u, 59u}, // xna -> Narb
{0xC5B70000u, 19u}, // xnr -> Deva
- {0x99D70000u, 46u}, // xog -> Latn
- {0xB5D70000u, 46u}, // xon -> Latn
+ {0x99D70000u, 45u}, // xog -> Latn
+ {0xB5D70000u, 45u}, // xon -> Latn
{0xC5F70000u, 72u}, // xpr -> Prti
- {0x86370000u, 46u}, // xrb -> Latn
- {0x82570000u, 75u}, // xsa -> Sarb
- {0xA2570000u, 46u}, // xsi -> Latn
- {0xB2570000u, 46u}, // xsm -> Latn
+ {0x86370000u, 45u}, // xrb -> Latn
+ {0x82570000u, 76u}, // xsa -> Sarb
+ {0xA2570000u, 45u}, // xsi -> Latn
+ {0xB2570000u, 45u}, // xsm -> Latn
{0xC6570000u, 19u}, // xsr -> Deva
- {0x92D70000u, 46u}, // xwe -> Latn
- {0xB0180000u, 46u}, // yam -> Latn
- {0xB8180000u, 46u}, // yao -> Latn
- {0xBC180000u, 46u}, // yap -> Latn
- {0xC8180000u, 46u}, // yas -> Latn
- {0xCC180000u, 46u}, // yat -> Latn
- {0xD4180000u, 46u}, // yav -> Latn
- {0xE0180000u, 46u}, // yay -> Latn
- {0xE4180000u, 46u}, // yaz -> Latn
- {0x80380000u, 46u}, // yba -> Latn
- {0x84380000u, 46u}, // ybb -> Latn
- {0xE0380000u, 46u}, // yby -> Latn
- {0xC4980000u, 46u}, // yer -> Latn
- {0xC4D80000u, 46u}, // ygr -> Latn
- {0xD8D80000u, 46u}, // ygw -> Latn
+ {0x92D70000u, 45u}, // xwe -> Latn
+ {0xB0180000u, 45u}, // yam -> Latn
+ {0xB8180000u, 45u}, // yao -> Latn
+ {0xBC180000u, 45u}, // yap -> Latn
+ {0xC8180000u, 45u}, // yas -> Latn
+ {0xCC180000u, 45u}, // yat -> Latn
+ {0xD4180000u, 45u}, // yav -> Latn
+ {0xE0180000u, 45u}, // yay -> Latn
+ {0xE4180000u, 45u}, // yaz -> Latn
+ {0x80380000u, 45u}, // yba -> Latn
+ {0x84380000u, 45u}, // ybb -> Latn
+ {0xE0380000u, 45u}, // yby -> Latn
+ {0xC4980000u, 45u}, // yer -> Latn
+ {0xC4D80000u, 45u}, // ygr -> Latn
+ {0xD8D80000u, 45u}, // ygw -> Latn
{0x79690000u, 31u}, // yi -> Hebr
- {0xB9580000u, 46u}, // yko -> Latn
- {0x91780000u, 46u}, // yle -> Latn
- {0x99780000u, 46u}, // ylg -> Latn
- {0xAD780000u, 46u}, // yll -> Latn
- {0xAD980000u, 46u}, // yml -> Latn
- {0x796F0000u, 46u}, // yo -> Latn
- {0xB5D80000u, 46u}, // yon -> Latn
- {0x86380000u, 46u}, // yrb -> Latn
- {0x92380000u, 46u}, // yre -> Latn
- {0xAE380000u, 46u}, // yrl -> Latn
- {0xCA580000u, 46u}, // yss -> Latn
- {0x82980000u, 46u}, // yua -> Latn
+ {0xB9580000u, 45u}, // yko -> Latn
+ {0x91780000u, 45u}, // yle -> Latn
+ {0x99780000u, 45u}, // ylg -> Latn
+ {0xAD780000u, 45u}, // yll -> Latn
+ {0xAD980000u, 45u}, // yml -> Latn
+ {0x796F0000u, 45u}, // yo -> Latn
+ {0xB5D80000u, 45u}, // yon -> Latn
+ {0x86380000u, 45u}, // yrb -> Latn
+ {0x92380000u, 45u}, // yre -> Latn
+ {0xAE380000u, 45u}, // yrl -> Latn
+ {0xCA580000u, 45u}, // yss -> Latn
+ {0x82980000u, 45u}, // yua -> Latn
{0x92980000u, 30u}, // yue -> Hant
{0x9298434Eu, 29u}, // yue-CN -> Hans
- {0xA6980000u, 46u}, // yuj -> Latn
- {0xCE980000u, 46u}, // yut -> Latn
- {0xDA980000u, 46u}, // yuw -> Latn
- {0x7A610000u, 46u}, // za -> Latn
- {0x98190000u, 46u}, // zag -> Latn
+ {0xA6980000u, 45u}, // yuj -> Latn
+ {0xCE980000u, 45u}, // yut -> Latn
+ {0xDA980000u, 45u}, // yuw -> Latn
+ {0x7A610000u, 45u}, // za -> Latn
+ {0x98190000u, 45u}, // zag -> Latn
{0xA4790000u, 2u}, // zdj -> Arab
- {0x80990000u, 46u}, // zea -> Latn
- {0x9CD90000u, 89u}, // zgh -> Tfng
+ {0x80990000u, 45u}, // zea -> Latn
+ {0x9CD90000u, 90u}, // zgh -> Tfng
{0x7A680000u, 29u}, // zh -> Hans
{0x7A684155u, 30u}, // zh-AU -> Hant
{0x7A68424Eu, 30u}, // zh-BN -> Hant
@@ -1486,14 +1493,14 @@
{0x7A685457u, 30u}, // zh-TW -> Hant
{0x7A685553u, 30u}, // zh-US -> Hant
{0x7A68564Eu, 30u}, // zh-VN -> Hant
- {0xDCF90000u, 62u}, // zhx -> Nshu
- {0x81190000u, 46u}, // zia -> Latn
- {0xCD590000u, 41u}, // zkt -> Kits
- {0xB1790000u, 46u}, // zlm -> Latn
- {0xA1990000u, 46u}, // zmi -> Latn
- {0x91B90000u, 46u}, // zne -> Latn
- {0x7A750000u, 46u}, // zu -> Latn
- {0x83390000u, 46u}, // zza -> Latn
+ {0xDCF90000u, 61u}, // zhx -> Nshu
+ {0x81190000u, 45u}, // zia -> Latn
+ {0xCD590000u, 40u}, // zkt -> Kits
+ {0xB1790000u, 45u}, // zlm -> Latn
+ {0xA1990000u, 45u}, // zmi -> Latn
+ {0x91B90000u, 45u}, // zne -> Latn
+ {0x7A750000u, 45u}, // zu -> Latn
+ {0x83390000u, 45u}, // zza -> Latn
});
std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
@@ -1573,6 +1580,7 @@
0xCD21534E4C61746ELLU, // bjt_Latn_SN
0xB141434D4C61746ELLU, // bkm_Latn_CM
0xD14150484C61746ELLU, // bku_Latn_PH
+ 0x99614D594C61746ELLU, // blg_Latn_MY
0xCD61564E54617674LLU, // blt_Tavt_VN
0x626D4D4C4C61746ELLU, // bm_Latn_ML
0xC1814D4C4C61746ELLU, // bmq_Latn_ML
@@ -1748,7 +1756,7 @@
0x8D87434E506C7264LLU, // hmd_Plrd_CN
0x8DA7504B41726162LLU, // hnd_Arab_PK
0x91A7494E44657661LLU, // hne_Deva_IN
- 0xA5A74C41486D6E67LLU, // hnj_Hmng_LA
+ 0xA5A75553486D6E70LLU, // hnj_Hmnp_US
0xB5A750484C61746ELLU, // hnn_Latn_PH
0xB9A7504B41726162LLU, // hno_Arab_PK
0x686F50474C61746ELLU, // ho_Latn_PG
@@ -1797,7 +1805,7 @@
0x984A4E474C61746ELLU, // kcg_Latn_NG
0xA84A5A574C61746ELLU, // kck_Latn_ZW
0x906A545A4C61746ELLU, // kde_Latn_TZ
- 0x9C6A544741726162LLU, // kdh_Arab_TG
+ 0x9C6A54474C61746ELLU, // kdh_Latn_TG
0xCC6A544854686169LLU, // kdt_Thai_TH
0x808A43564C61746ELLU, // kea_Latn_CV
0xB48A434D4C61746ELLU, // ken_Latn_CM
@@ -1982,6 +1990,7 @@
0x6E725A414C61746ELLU, // nr_Latn_ZA
0xAA4D434143616E73LLU, // nsk_Cans_CA
0xBA4D5A414C61746ELLU, // nso_Latn_ZA
+ 0xCE4D494E546E7361LLU, // nst_Tnsa_IN
0xCA8D53534C61746ELLU, // nus_Latn_SS
0x6E7655534C61746ELLU, // nv_Latn_US
0xC2ED434E4C61746ELLU, // nxq_Latn_CN
@@ -1995,6 +2004,7 @@
0x6F7347454379726CLLU, // os_Cyrl_GE
0x824E55534F736765LLU, // osa_Osge_US
0xAA6E4D4E4F726B68LLU, // otk_Orkh_MN
+ 0xA28E8C814F756772LLU, // oui_Ougr_143
0x7061504B41726162LLU, // pa_Arab_PK
0x7061494E47757275LLU, // pa_Guru_IN
0x980F50484C61746ELLU, // pag_Latn_PH
@@ -2029,7 +2039,7 @@
0x945152454C61746ELLU, // rcf_Latn_RE
0xA49149444C61746ELLU, // rej_Latn_ID
0xB4D149544C61746ELLU, // rgn_Latn_IT
- 0x98F14D4D41726162LLU, // rhg_Arab_MM
+ 0x98F14D4D526F6867LLU, // rhg_Rohg_MM
0x8111494E4C61746ELLU, // ria_Latn_IN
0x95114D4154666E67LLU, // rif_Tfng_MA
0xC9314E5044657661LLU, // rjs_Deva_NP
@@ -2172,6 +2182,7 @@
0xAEB354564C61746ELLU, // tvl_Latn_TV
0xC2D34E454C61746ELLU, // twq_Latn_NE
0x9AF3434E54616E67LLU, // txg_Tang_CN
+ 0xBAF3494E546F746FLLU, // txo_Toto_IN
0x747950464C61746ELLU, // ty_Latn_PF
0xD71352554379726CLLU, // tyv_Cyrl_RU
0xB3334D414C61746ELLU, // tzm_Latn_MA
@@ -2256,6 +2267,7 @@
});
const std::unordered_map<uint32_t, uint32_t> ARAB_PARENTS({
+ {0x61724145u, 0x61729420u}, // ar-AE -> ar-015
{0x6172445Au, 0x61729420u}, // ar-DZ -> ar-015
{0x61724548u, 0x61729420u}, // ar-EH -> ar-015
{0x61724C59u, 0x61729420u}, // ar-LY -> ar-015
@@ -2279,7 +2291,6 @@
{0x656E4253u, 0x656E8400u}, // en-BS -> en-001
{0x656E4257u, 0x656E8400u}, // en-BW -> en-001
{0x656E425Au, 0x656E8400u}, // en-BZ -> en-001
- {0x656E4341u, 0x656E8400u}, // en-CA -> en-001
{0x656E4343u, 0x656E8400u}, // en-CC -> en-001
{0x656E4348u, 0x656E80A1u}, // en-CH -> en-150
{0x656E434Bu, 0x656E8400u}, // en-CK -> en-001
@@ -2332,7 +2343,6 @@
{0x656E4E55u, 0x656E8400u}, // en-NU -> en-001
{0x656E4E5Au, 0x656E8400u}, // en-NZ -> en-001
{0x656E5047u, 0x656E8400u}, // en-PG -> en-001
- {0x656E5048u, 0x656E8400u}, // en-PH -> en-001
{0x656E504Bu, 0x656E8400u}, // en-PK -> en-001
{0x656E504Eu, 0x656E8400u}, // en-PN -> en-001
{0x656E5057u, 0x656E8400u}, // en-PW -> en-001
diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING
index 9ebc996..8abe79d 100644
--- a/libs/androidfw/TEST_MAPPING
+++ b/libs/androidfw/TEST_MAPPING
@@ -2,6 +2,9 @@
"presubmit": [
{
"name": "CtsResourcesLoaderTests"
+ },
+ {
+ "name": "libandroidfw_tests"
}
]
}
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index a3b42df..1bde792 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -192,6 +192,12 @@
std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie,
Asset::AccessMode mode) const;
+ // Returns the resource id of parent style of the specified theme.
+ //
+ // Returns a null error if the name is missing/corrupt, or an I/O error if reading resource data
+ // failed.
+ base::expected<uint32_t, NullOrIOError> GetParentThemeResourceId(uint32_t resid) const;
+
// Returns the resource name of the specified resource ID.
//
// Utf8 strings are preferred, and only if they are unavailable are the Utf16 variants populated.
diff --git a/libs/hwui/ColorMode.h b/libs/hwui/ColorMode.h
index 6d387f9..3df5c3c 100644
--- a/libs/hwui/ColorMode.h
+++ b/libs/hwui/ColorMode.h
@@ -29,6 +29,8 @@
Hdr = 2,
// HDR Rec2020 + 1010102
Hdr10 = 3,
+ // Alpha 8
+ A8 = 4,
};
} // namespace android::uirenderer
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 9bca4df..744739a 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -91,6 +91,8 @@
fboInfo.fFormat = GL_RGBA8;
} else if (colorType == kRGBA_1010102_SkColorType) {
fboInfo.fFormat = GL_RGB10_A2;
+ } else if (colorType == kAlpha_8_SkColorType) {
+ fboInfo.fFormat = GL_R8;
} else {
LOG_ALWAYS_FATAL("Unsupported color type.");
}
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 4e7471d..bc386fe 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -613,6 +613,10 @@
mSurfaceColorType = SkColorType::kRGBA_1010102_SkColorType;
mSurfaceColorSpace = SkColorSpace::MakeRGB(GetPQSkTransferFunction(), SkNamedGamut::kRec2020);
break;
+ case ColorMode::A8:
+ mSurfaceColorType = SkColorType::kAlpha_8_SkColorType;
+ mSurfaceColorSpace = nullptr;
+ break;
}
}
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index c7d7a17..2f8ddee 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -90,6 +90,7 @@
, mEglConfig(nullptr)
, mEglConfigF16(nullptr)
, mEglConfig1010102(nullptr)
+ , mEglConfigA8(nullptr)
, mEglContext(EGL_NO_CONTEXT)
, mPBufferSurface(EGL_NO_SURFACE)
, mCurrentSurface(EGL_NO_SURFACE)
@@ -246,6 +247,52 @@
return config;
}
+EGLConfig EglManager::loadA8Config(EGLDisplay display, EglManager::SwapBehavior swapBehavior) {
+ EGLint eglSwapBehavior =
+ (swapBehavior == SwapBehavior::Preserved) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+ EGLint attribs[] = {EGL_RENDERABLE_TYPE,
+ EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE,
+ 8,
+ EGL_GREEN_SIZE,
+ 0,
+ EGL_BLUE_SIZE,
+ 0,
+ EGL_ALPHA_SIZE,
+ 0,
+ EGL_DEPTH_SIZE,
+ 0,
+ EGL_STENCIL_SIZE,
+ STENCIL_BUFFER_SIZE,
+ EGL_SURFACE_TYPE,
+ EGL_WINDOW_BIT | eglSwapBehavior,
+ EGL_NONE};
+ EGLint numConfigs = 1;
+ if (!eglChooseConfig(display, attribs, nullptr, numConfigs, &numConfigs)) {
+ return EGL_NO_CONFIG_KHR;
+ }
+
+ std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR);
+ if (!eglChooseConfig(display, attribs, configs.data(), numConfigs, &numConfigs)) {
+ return EGL_NO_CONFIG_KHR;
+ }
+
+ // The component sizes passed to eglChooseConfig are minimums, so configs
+ // contains entries that exceed them. Choose one that matches the sizes
+ // exactly.
+ for (EGLConfig config : configs) {
+ EGLint r{0}, g{0}, b{0}, a{0};
+ eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
+ eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
+ eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
+ eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
+ if (8 == r && 0 == g && 0 == b && 0 == a) {
+ return config;
+ }
+ }
+ return EGL_NO_CONFIG_KHR;
+}
+
void EglManager::initExtensions() {
auto extensions = StringUtils::split(eglQueryString(mEglDisplay, EGL_EXTENSIONS));
@@ -307,6 +354,10 @@
ALOGW("Failed to initialize 101010-2 format, error = %s",
eglErrorString());
}
+ mEglConfigA8 = loadA8Config(mEglDisplay, mSwapBehavior);
+ if (mEglConfigA8 == EGL_NO_CONFIG_KHR) {
+ ALOGE("Failed to initialize A8 format, error = %s", eglErrorString());
+ }
}
void EglManager::createContext() {
@@ -345,10 +396,14 @@
sk_sp<SkColorSpace> colorSpace) {
LOG_ALWAYS_FATAL_IF(!hasEglContext(), "Not initialized");
- if (!mHasWideColorGamutSupport || !EglExtensions.noConfigContext) {
+ if (!EglExtensions.noConfigContext) {
+ // The caller shouldn't use A8 if we cannot switch modes.
+ LOG_ALWAYS_FATAL_IF(colorMode == ColorMode::A8,
+ "Cannot use A8 without EGL_KHR_no_config_context!");
+
+ // Cannot switch modes without EGL_KHR_no_config_context.
colorMode = ColorMode::Default;
}
-
// The color space we want to use depends on whether linear blending is turned
// on and whether the app has requested wide color gamut rendering. When wide
// color gamut rendering is off, the app simply renders in the display's native
@@ -374,42 +429,57 @@
EGLint attribs[] = {EGL_NONE, EGL_NONE, EGL_NONE};
EGLConfig config = mEglConfig;
- if (DeviceInfo::get()->getWideColorType() == kRGBA_F16_SkColorType) {
- if (mEglConfigF16 == EGL_NO_CONFIG_KHR) {
+ if (colorMode == ColorMode::A8) {
+ // A8 doesn't use a color space
+ config = mEglConfigA8;
+
+ LOG_ALWAYS_FATAL_IF(!mEglConfigA8, "Requested ColorMode::A8, but EGL lacks support!");
+ } else {
+ if (!mHasWideColorGamutSupport) {
colorMode = ColorMode::Default;
- } else {
- config = mEglConfigF16;
}
- }
- if (EglExtensions.glColorSpace) {
- attribs[0] = EGL_GL_COLORSPACE_KHR;
- switch (colorMode) {
- case ColorMode::Default:
- attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
- break;
- case ColorMode::WideColorGamut: {
- skcms_Matrix3x3 colorGamut;
- LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&colorGamut),
- "Could not get gamut matrix from color space");
- if (memcmp(&colorGamut, &SkNamedGamut::kDisplayP3, sizeof(colorGamut)) == 0) {
- attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
- } else if (memcmp(&colorGamut, &SkNamedGamut::kSRGB, sizeof(colorGamut)) == 0) {
- attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT;
- } else if (memcmp(&colorGamut, &SkNamedGamut::kRec2020, sizeof(colorGamut)) == 0) {
- attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
- } else {
- LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
- }
- break;
- }
- case ColorMode::Hdr:
+
+ if (DeviceInfo::get()->getWideColorType() == kRGBA_F16_SkColorType) {
+ if (mEglConfigF16 == EGL_NO_CONFIG_KHR) {
+ colorMode = ColorMode::Default;
+ } else {
config = mEglConfigF16;
- attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
- break;
- case ColorMode::Hdr10:
- config = mEglConfig1010102;
- attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
- break;
+ }
+ }
+ if (EglExtensions.glColorSpace) {
+ attribs[0] = EGL_GL_COLORSPACE_KHR;
+ switch (colorMode) {
+ case ColorMode::Default:
+ attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
+ break;
+ case ColorMode::WideColorGamut: {
+ skcms_Matrix3x3 colorGamut;
+ LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&colorGamut),
+ "Could not get gamut matrix from color space");
+ if (memcmp(&colorGamut, &SkNamedGamut::kDisplayP3, sizeof(colorGamut)) == 0) {
+ attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
+ } else if (memcmp(&colorGamut, &SkNamedGamut::kSRGB, sizeof(colorGamut)) == 0) {
+ attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT;
+ } else if (memcmp(&colorGamut, &SkNamedGamut::kRec2020, sizeof(colorGamut)) ==
+ 0) {
+ attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
+ } else {
+ LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
+ }
+ break;
+ }
+ case ColorMode::Hdr:
+ config = mEglConfigF16;
+ attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
+ break;
+ case ColorMode::Hdr10:
+ config = mEglConfig1010102;
+ attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
+ break;
+ case ColorMode::A8:
+ LOG_ALWAYS_FATAL("Unreachable: A8 doesn't use a color space");
+ break;
+ }
}
}
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index 69f3ed0..fc6b28d 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -89,6 +89,7 @@
static EGLConfig load8BitsConfig(EGLDisplay display, SwapBehavior swapBehavior);
static EGLConfig loadFP16Config(EGLDisplay display, SwapBehavior swapBehavior);
static EGLConfig load1010102Config(EGLDisplay display, SwapBehavior swapBehavior);
+ static EGLConfig loadA8Config(EGLDisplay display, SwapBehavior swapBehavior);
void initExtensions();
void createPBufferSurface();
@@ -100,6 +101,7 @@
EGLConfig mEglConfig;
EGLConfig mEglConfigF16;
EGLConfig mEglConfig1010102;
+ EGLConfig mEglConfigA8;
EGLContext mEglContext;
EGLSurface mPBufferSurface;
EGLSurface mCurrentSurface;
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 9e8a1e1..a9ff2c6 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -35,6 +35,9 @@
#include "pipeline/skia/ShaderCache.h"
#include "renderstate/RenderState.h"
+#undef LOG_TAG
+#define LOG_TAG "VulkanManager"
+
namespace android {
namespace uirenderer {
namespace renderthread {
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index 611a4d9..7dd3561 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -24,6 +24,9 @@
#include "VulkanManager.h"
#include "utils/Color.h"
+#undef LOG_TAG
+#define LOG_TAG "VulkanSurface"
+
namespace android {
namespace uirenderer {
namespace renderthread {
@@ -197,8 +200,9 @@
outWindowInfo->bufferFormat = ColorTypeToBufferFormat(colorType);
outWindowInfo->colorspace = colorSpace;
outWindowInfo->dataspace = ColorSpaceToADataSpace(colorSpace.get(), colorType);
- LOG_ALWAYS_FATAL_IF(outWindowInfo->dataspace == HAL_DATASPACE_UNKNOWN,
- "Unsupported colorspace");
+ LOG_ALWAYS_FATAL_IF(
+ outWindowInfo->dataspace == HAL_DATASPACE_UNKNOWN && colorType != kAlpha_8_SkColorType,
+ "Unsupported colorspace");
VkFormat vkPixelFormat;
switch (colorType) {
@@ -211,6 +215,9 @@
case kRGBA_1010102_SkColorType:
vkPixelFormat = VK_FORMAT_A2B10G10R10_UNORM_PACK32;
break;
+ case kAlpha_8_SkColorType:
+ vkPixelFormat = VK_FORMAT_R8_UNORM;
+ break;
default:
LOG_ALWAYS_FATAL("Unsupported colorType: %d", (int)colorType);
}
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 9f3be16..2293ace 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -57,6 +57,10 @@
colorType = kRGBA_F16_SkColorType;
alphaType = kPremul_SkAlphaType;
break;
+ case AHARDWAREBUFFER_FORMAT_R8_UNORM:
+ colorType = kAlpha_8_SkColorType;
+ alphaType = kPremul_SkAlphaType;
+ break;
default:
ALOGV("Unsupported format: %d, return unknown by default", format);
break;
@@ -90,6 +94,8 @@
// Hardcoding the value from android::PixelFormat
static constexpr uint64_t kRGBA4444 = 7;
return kRGBA4444;
+ case kAlpha_8_SkColorType:
+ return AHARDWAREBUFFER_FORMAT_R8_UNORM;
default:
ALOGV("Unsupported colorType: %d, return RGBA_8888 by default", (int)colorType);
return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java
index 7caac89..1448c49 100644
--- a/media/java/android/media/AudioDeviceAttributes.java
+++ b/media/java/android/media/AudioDeviceAttributes.java
@@ -110,7 +110,7 @@
mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(type);
} else if (role == ROLE_INPUT) {
AudioDeviceInfo.enforceValidAudioDeviceTypeIn(type);
- mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(type);
+ mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(type, address);
} else {
mNativeType = AudioSystem.DEVICE_NONE;
}
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index a186566..211a50e 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -581,7 +581,16 @@
/** @hide */
public static int convertDeviceTypeToInternalInputDevice(int deviceType) {
- return EXT_TO_INT_INPUT_DEVICE_MAPPING.get(deviceType, AudioSystem.DEVICE_NONE);
+ return convertDeviceTypeToInternalInputDevice(deviceType, "");
+ }
+ /** @hide */
+ public static int convertDeviceTypeToInternalInputDevice(int deviceType, String address) {
+ int internalType = EXT_TO_INT_INPUT_DEVICE_MAPPING.get(deviceType, AudioSystem.DEVICE_NONE);
+ if (internalType == AudioSystem.DEVICE_IN_BUILTIN_MIC
+ && "back".equals(address)) {
+ internalType = AudioSystem.DEVICE_IN_BACK_MIC;
+ }
+ return internalType;
}
private static final SparseIntArray INT_TO_EXT_DEVICE_MAPPING;
@@ -671,9 +680,6 @@
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_USB_ACCESSORY, AudioSystem.DEVICE_OUT_USB_ACCESSORY);
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_DOCK, AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_FM, AudioSystem.DEVICE_OUT_FM);
- EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BUILTIN_MIC, AudioSystem.DEVICE_IN_BUILTIN_MIC);
- EXT_TO_INT_DEVICE_MAPPING.put(TYPE_FM_TUNER, AudioSystem.DEVICE_IN_FM_TUNER);
- EXT_TO_INT_DEVICE_MAPPING.put(TYPE_TV_TUNER, AudioSystem.DEVICE_IN_TV_TUNER);
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_TELEPHONY, AudioSystem.DEVICE_OUT_TELEPHONY_TX);
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_AUX_LINE, AudioSystem.DEVICE_OUT_AUX_LINE);
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_IP, AudioSystem.DEVICE_OUT_IP);
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index d721291..0722417 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4003,8 +4003,7 @@
* @hide
* flag set on test API calls,
* see {@link #requestAudioFocusForTest(AudioFocusRequest, String, int, int)},
- * note that it isn't used in conjunction with other flags, it is passed as the single
- * value for flags */
+ */
public static final int AUDIOFOCUS_FLAG_TEST = 0x1 << 3;
/** @hide */
public static final int AUDIOFOCUS_FLAGS_APPS = AUDIOFOCUS_FLAG_DELAY_OK
@@ -4187,7 +4186,9 @@
afr.getFocusGain(),
mICallBack,
mAudioFocusDispatcher,
- clientFakeId, "com.android.test.fakeclient", clientFakeUid, clientTargetSdk);
+ clientFakeId, "com.android.test.fakeclient",
+ afr.getFlags() | AudioManager.AUDIOFOCUS_FLAG_TEST,
+ clientFakeUid, clientTargetSdk);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index cc37c38..e8792b3 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1915,7 +1915,7 @@
types[i] = devices.get(i).getInternalType();
if (types[i] == AudioSystem.DEVICE_NONE) {
types[i] = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(
- devices.get(i).getType());
+ devices.get(i).getType(), devices.get(i).getAddress());
}
addresses[i] = devices.get(i).getAddress();
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index afcbc57..7f6fb90 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -384,7 +384,7 @@
int requestAudioFocusForTest(in AudioAttributes aa, int durationHint, IBinder cb,
in IAudioFocusDispatcher fd, in String clientId, in String callingPackageName,
- int uid, int sdk);
+ int flags, int uid, int sdk);
int abandonAudioFocusForTest(in IAudioFocusDispatcher fd, in String clientId,
in AudioAttributes aa, in String callingPackageName);
@@ -460,4 +460,8 @@
boolean register);
void setTestDeviceConnectionState(in AudioDeviceAttributes device, boolean connected);
+
+ List<AudioFocusInfo> getFocusStack();
+
+ boolean sendFocusLoss(in AudioFocusInfo focusLoser, in IAudioPolicyCallback apcb);
}
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 70bb960..28f238e 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -511,7 +511,12 @@
int deviceType = AudioSystem.DEVICE_NONE;
String deviceAddress = "";
if (device != null) {
- deviceType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType());
+ if (device.getRole() == AudioDeviceAttributes.ROLE_OUTPUT) {
+ deviceType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType());
+ } else {
+ deviceType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(
+ device.getType(), device.getAddress());
+ }
deviceAddress = device.getAddress();
}
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 0f08d79..3ba1d1f 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
@@ -230,7 +231,7 @@
* If set to {@code true}, it is mandatory to set an
* {@link AudioPolicy.AudioPolicyFocusListener} in order to successfully build
* an {@code AudioPolicy} instance.
- * @param enforce true if the policy will govern audio focus decisions.
+ * @param isFocusPolicy true if the policy will govern audio focus decisions.
* @return the same Builder instance.
*/
@NonNull
@@ -723,6 +724,45 @@
}
/**
+ * Returns the list of entries in the focus stack.
+ * The list is ordered with increasing rank of focus ownership, where the last entry is at the
+ * top of the focus stack and is the current focus owner.
+ * @return the ordered list of focus owners
+ * @see AudioManager#registerAudioPolicy(AudioPolicy)
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public @NonNull List<AudioFocusInfo> getFocusStack() {
+ try {
+ return getService().getFocusStack();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Send AUDIOFOCUS_LOSS to a specific stack entry, causing it to be notified of the focus
+ * loss, and for it to exit the focus stack (its focus listener will not be invoked after that).
+ * This operation is only valid for a registered policy (with
+ * {@link AudioManager#registerAudioPolicy(AudioPolicy)}) that is also set as the policy focus
+ * listener (with {@link Builder#setAudioPolicyFocusListener(AudioPolicyFocusListener)}.
+ * @param focusLoser the stack entry that is exiting the stack through a focus loss
+ * @return false if the focusLoser wasn't found in the stack, true otherwise
+ * @throws IllegalStateException if used on an unregistered policy, or a registered policy
+ * with no {@link AudioPolicyFocusListener} set
+ * @see AudioManager#registerAudioPolicy(AudioPolicy)
+ * @see Builder#setAudioPolicyStatusListener(AudioPolicyStatusListener)
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser) throws IllegalStateException {
+ Objects.requireNonNull(focusLoser);
+ try {
+ return getService().sendFocusLoss(focusLoser, cb());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Create an {@link AudioRecord} instance that is associated with the given {@link AudioMix}.
* Audio buffers recorded through the created instance will contain the mix of the audio
* streams that fed the given mixer.
diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
index cd87a09..9859a5f 100644
--- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
@@ -22,6 +22,7 @@
import android.media.tv.interactive.ITvIAppManagerCallback;
import android.media.tv.interactive.TvIAppInfo;
import android.net.Uri;
+import android.os.Bundle;
import android.view.Surface;
/**
@@ -31,6 +32,7 @@
interface ITvIAppManager {
List<TvIAppInfo> getTvIAppServiceList(int userId);
void prepare(String tiasId, int type, int userId);
+ void notifyAppLinkInfo(String tiasId, in Bundle info, int userId);
void startIApp(in IBinder sessionToken, int userId);
void createSession(
in ITvIAppClient client, in String iAppServiceId, int type, int seq, int userId);
diff --git a/media/java/android/media/tv/interactive/ITvIAppService.aidl b/media/java/android/media/tv/interactive/ITvIAppService.aidl
index af15dd8..72db3fa 100644
--- a/media/java/android/media/tv/interactive/ITvIAppService.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppService.aidl
@@ -18,6 +18,7 @@
import android.media.tv.interactive.ITvIAppServiceCallback;
import android.media.tv.interactive.ITvIAppSessionCallback;
+import android.os.Bundle;
import android.view.InputChannel;
/**
@@ -31,4 +32,5 @@
void createSession(in InputChannel channel, in ITvIAppSessionCallback callback,
in String iAppServiceId, int type);
void prepare(int type);
+ void notifyAppLinkInfo(in Bundle info);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java
index 2272084..8766055 100644
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ b/media/java/android/media/tv/interactive/TvIAppManager.java
@@ -26,6 +26,7 @@
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvInputManager;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -87,6 +88,51 @@
*/
public static final int TV_IAPP_RTE_STATE_ERROR = 4;
+ /**
+ * Key for package name in app link.
+ * <p>Type: String
+ *
+ * @see #notifyAppLinkInfo(String, Bundle)
+ * @hide
+ */
+ public static final String KEY_PACKAGE_NAME = "package_name";
+
+ /**
+ * Key for class name in app link.
+ * <p>Type: String
+ *
+ * @see #notifyAppLinkInfo(String, Bundle)
+ * @hide
+ */
+ public static final String KEY_CLASS_NAME = "class_name";
+
+ /**
+ * Key for URI scheme in app link.
+ * <p>Type: String
+ *
+ * @see #notifyAppLinkInfo(String, Bundle)
+ * @hide
+ */
+ public static final String KEY_URI_SCHEME = "uri_scheme";
+
+ /**
+ * Key for URI host in app link.
+ * <p>Type: String
+ *
+ * @see #notifyAppLinkInfo(String, Bundle)
+ * @hide
+ */
+ public static final String KEY_URI_HOST = "uri_host";
+
+ /**
+ * Key for URI prefix in app link.
+ * <p>Type: String
+ *
+ * @see #notifyAppLinkInfo(String, Bundle)
+ * @hide
+ */
+ public static final String KEY_URI_PREFIX = "uri_prefix";
+
private final ITvIAppManager mService;
private final int mUserId;
@@ -418,6 +464,18 @@
}
/**
+ * Notifies app link info.
+ * @hide
+ */
+ public void notifyAppLinkInfo(String tvIAppServiceId, Bundle appLinkInfo) {
+ try {
+ mService.notifyAppLinkInfo(tvIAppServiceId, appLinkInfo, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Registers a {@link TvIAppManager.TvIAppCallback}.
*
* @param callback A callback used to monitor status of the TV IApp services.
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
index f93b597..6bf8028 100644
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvIAppService.java
@@ -30,6 +30,7 @@
import android.media.tv.BroadcastInfoResponse;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -123,6 +124,11 @@
public void prepare(int type) {
onPrepare(type);
}
+
+ @Override
+ public void notifyAppLinkInfo(Bundle appLinkInfo) {
+ onAppLinkInfo(appLinkInfo);
+ }
};
return tvIAppServiceBinder;
}
@@ -135,6 +141,14 @@
// TODO: make it abstract when unhide
}
+ /**
+ * Registers App link info.
+ * @hide
+ */
+ public void onAppLinkInfo(Bundle appLinkInfo) {
+ // TODO: make it abstract when unhide
+ }
+
/**
* Returns a concrete implementation of {@link Session}.
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 94de7fa..255b391b 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -285,7 +285,7 @@
@Nullable
private FrontendInfo mFrontendInfo;
private Integer mFrontendHandle;
- private Boolean mIsSharedFrontend = false;
+ private Tuner mFeOwnerTuner = null;
private int mFrontendType = FrontendSettings.TYPE_UNDEFINED;
private int mUserId;
private Lnb mLnb;
@@ -442,11 +442,10 @@
mFrontendLock.lock();
try {
mTunerResourceManager.shareFrontend(mClientId, tuner.mClientId);
- synchronized (mIsSharedFrontend) {
- mFrontendHandle = tuner.mFrontendHandle;
- mFrontend = tuner.mFrontend;
- mIsSharedFrontend = true;
- }
+ mFeOwnerTuner = tuner;
+ mFeOwnerTuner.registerFrontendCallbackListener(this);
+ mFrontendHandle = mFeOwnerTuner.mFrontendHandle;
+ mFrontend = mFeOwnerTuner.mFrontend;
nativeShareFrontend(mFrontend.mId);
} finally {
releaseTRMSLock();
@@ -513,6 +512,27 @@
private long mNativeContext; // used by native jMediaTuner
/**
+ * Registers a tuner as a listener for frontend callbacks.
+ */
+ private void registerFrontendCallbackListener(Tuner tuner) {
+ nativeRegisterFeCbListener(tuner.getNativeContext());
+ }
+
+ /**
+ * Unregisters a tuner as a listener for frontend callbacks.
+ */
+ private void unregisterFrontendCallbackListener(Tuner tuner) {
+ nativeUnregisterFeCbListener(tuner.getNativeContext());
+ }
+
+ /**
+ * Returns the pointer to the associated JTuner.
+ */
+ long getNativeContext() {
+ return mNativeContext;
+ }
+
+ /**
* Releases the Tuner instance.
*/
@Override
@@ -526,19 +546,21 @@
}
}
- private void releaseAll() {
+ private void releaseFrontend() {
mFrontendLock.lock();
try {
if (mFrontendHandle != null) {
- synchronized (mIsSharedFrontend) {
- if (!mIsSharedFrontend) {
- int res = nativeCloseFrontend(mFrontendHandle);
- if (res != Tuner.RESULT_SUCCESS) {
- TunerUtils.throwExceptionForResult(res, "failed to close frontend");
- }
- mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId);
+ if (mFeOwnerTuner != null) {
+ // unregister self from the Frontend callback
+ mFeOwnerTuner.unregisterFrontendCallbackListener(this);
+ mFeOwnerTuner = null;
+ } else {
+ // close resource as owner
+ int res = nativeCloseFrontend(mFrontendHandle);
+ if (res != Tuner.RESULT_SUCCESS) {
+ TunerUtils.throwExceptionForResult(res, "failed to close frontend");
}
- mIsSharedFrontend = false;
+ mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId);
}
FrameworkStatsLog
.write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
@@ -549,9 +571,14 @@
} finally {
mFrontendLock.unlock();
}
+ }
+
+ private void releaseAll() {
+ releaseFrontend();
mLnbLock.lock();
try {
+ // mLnb will be non-null only for owner tuner
if (mLnb != null) {
mLnb.close();
}
@@ -641,6 +668,8 @@
*/
private native Frontend nativeOpenFrontendByHandle(int handle);
private native int nativeShareFrontend(int id);
+ private native void nativeRegisterFeCbListener(long nativeContext);
+ private native void nativeUnregisterFeCbListener(long nativeContext);
@Result
private native int nativeTune(int type, FrontendSettings settings);
private native int nativeStopTune();
@@ -1276,7 +1305,7 @@
}
private void onFrontendEvent(int eventType) {
- Log.d(TAG, "Got event from tuning. Event type: " + eventType);
+ Log.d(TAG, "Got event from tuning. Event type: " + eventType + " for " + this);
synchronized (mOnTuneEventLock) {
if (mOnTuneEventExecutor != null && mOnTuneEventListener != null) {
mOnTuneEventExecutor.execute(() -> {
diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
index cfd8583..6f3ab03 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
@@ -98,6 +98,7 @@
private native void nativeSetFileDescriptor(int fd);
private native long nativeRead(long size);
private native long nativeRead(byte[] bytes, long offset, long size);
+ private native long nativeSeek(long pos);
private DvrPlayback() {
mUserId = Process.myUid();
@@ -243,7 +244,7 @@
*
* @param fd the file descriptor to read data.
* @see #read(long)
- * @see #read(byte[], long, long)
+ * @see #seek(long)
*/
public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) {
nativeSetFileDescriptor(fd.getFd());
@@ -261,19 +262,30 @@
}
/**
- * Reads data from the buffer for DVR playback and copies to the given byte array.
+ * Reads data from the buffer for DVR playback.
*
- * @param bytes the byte array to store the data.
- * @param offset the index of the first byte in {@code bytes} to copy to.
+ * @param buffer the byte array where DVR reads data from.
+ * @param offset the index of the first byte in {@code buffer} to read.
* @param size the maximum number of bytes to read.
* @return the number of bytes read.
*/
@BytesLong
- public long read(@NonNull byte[] bytes, @BytesLong long offset, @BytesLong long size) {
- if (size + offset > bytes.length) {
+ public long read(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) {
+ if (size + offset > buffer.length) {
throw new ArrayIndexOutOfBoundsException(
- "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size);
+ "Array length=" + buffer.length + ", offset=" + offset + ", size=" + size);
}
- return nativeRead(bytes, offset, size);
+ return nativeRead(buffer, offset, size);
+ }
+
+ /**
+ * Sets the file pointer offset of the file descriptor.
+ *
+ * @param pos the offset position, measured in bytes from the beginning of the file.
+ * @return the new offset position.
+ */
+ @BytesLong
+ public long seek(@BytesLong long pos) {
+ return nativeSeek(pos);
}
}
diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
index 212a713..e72026a 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
@@ -216,7 +216,6 @@
*
* @param fd the file descriptor to write data.
* @see #write(long)
- * @see #write(byte[], long, long)
*/
public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) {
nativeSetFileDescriptor(fd.getFd());
@@ -236,17 +235,17 @@
/**
* Writes recording data to buffer.
*
- * @param bytes the byte array stores the data to be written to DVR.
- * @param offset the index of the first byte in {@code bytes} to be written to DVR.
+ * @param buffer the byte array stores the data from DVR.
+ * @param offset the index of the first byte in {@code buffer} to write the data from DVR.
* @param size the maximum number of bytes to write.
* @return the number of bytes written.
*/
@BytesLong
- public long write(@NonNull byte[] bytes, @BytesLong long offset, @BytesLong long size) {
- if (size + offset > bytes.length) {
+ public long write(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) {
+ if (size + offset > buffer.length) {
throw new ArrayIndexOutOfBoundsException(
- "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size);
+ "Array length=" + buffer.length + ", offset=" + offset + ", size=" + size);
}
- return nativeWrite(bytes, offset, size);
+ return nativeWrite(buffer, offset, size);
}
}
diff --git a/media/java/android/media/tv/tuner/filter/DownloadEvent.java b/media/java/android/media/tv/tuner/filter/DownloadEvent.java
index 394211be..25989db 100644
--- a/media/java/android/media/tv/tuner/filter/DownloadEvent.java
+++ b/media/java/android/media/tv/tuner/filter/DownloadEvent.java
@@ -27,15 +27,17 @@
@SystemApi
public class DownloadEvent extends FilterEvent {
private final int mItemId;
+ private final int mDownloadId;
private final int mMpuSequenceNumber;
private final int mItemFragmentIndex;
private final int mLastItemFragmentIndex;
private final int mDataLength;
// This constructor is used by JNI code only
- private DownloadEvent(int itemId, int mpuSequenceNumber, int itemFragmentIndex,
+ private DownloadEvent(int itemId, int downloadId, int mpuSequenceNumber, int itemFragmentIndex,
int lastItemFragmentIndex, int dataLength) {
mItemId = itemId;
+ mDownloadId = downloadId;
mMpuSequenceNumber = mpuSequenceNumber;
mItemFragmentIndex = itemFragmentIndex;
mLastItemFragmentIndex = lastItemFragmentIndex;
@@ -50,6 +52,15 @@
}
/**
+ * Gets download ID.
+ *
+ * <p>This query is only supported in Tuner 2.0 or higher version. Unsupported version will
+ * return {@code -1}.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
+ */
+ public int getDownloadId() { return mDownloadId; }
+
+ /**
* Gets MPU sequence number of filtered data.
*/
@IntRange(from = 0)
@@ -80,4 +91,3 @@
return mDataLength;
}
}
-
diff --git a/media/java/android/media/tv/tuner/filter/DownloadSettings.java b/media/java/android/media/tv/tuner/filter/DownloadSettings.java
index 7ba923e..e2cfd7c 100644
--- a/media/java/android/media/tv/tuner/filter/DownloadSettings.java
+++ b/media/java/android/media/tv/tuner/filter/DownloadSettings.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.media.tv.tuner.TunerUtils;
+import android.media.tv.tuner.TunerVersionChecker;
/**
* Filter Settings for a Download.
@@ -27,10 +28,12 @@
*/
@SystemApi
public class DownloadSettings extends Settings {
+ private final boolean mUseDownloadId;
private final int mDownloadId;
- private DownloadSettings(int mainType, int downloadId) {
+ private DownloadSettings(int mainType, boolean useDownloadId, int downloadId) {
super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_DOWNLOAD));
+ mUseDownloadId = useDownloadId;
mDownloadId = downloadId;
}
@@ -42,6 +45,15 @@
}
/**
+ * Gets whether download ID is used.
+ *
+ * <p>This query is only supported in Tuner 2.0 or higher version. Unsupported version will
+ * return {@code false}.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
+ */
+ public boolean useDownloadId() { return mUseDownloadId; }
+
+ /**
* Creates a builder for {@link DownloadSettings}.
*
* @param mainType the filter main type.
@@ -56,6 +68,7 @@
*/
public static class Builder {
private final int mMainType;
+ private boolean mUseDownloadId = false;
private int mDownloadId;
private Builder(int mainType) {
@@ -63,6 +76,24 @@
}
/**
+ * Sets whether download ID is used or not.
+ *
+ * <p>This configuration is only supported in Tuner 2.0 or higher version. Unsupported
+ * version will cause no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the
+ * version information.
+ *
+ * <p>Default value is {@code false}.
+ */
+ @NonNull
+ public Builder setUseDownloadId(boolean useDownloadId) {
+ if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "setUseDownloadId")) {
+ mUseDownloadId = useDownloadId;
+ }
+ return this;
+ }
+
+ /**
* Sets download ID.
*/
@NonNull
@@ -76,7 +107,7 @@
*/
@NonNull
public DownloadSettings build() {
- return new DownloadSettings(mMainType, mDownloadId);
+ return new DownloadSettings(mMainType, mUseDownloadId, mDownloadId);
}
}
}
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index dbd85e9..79d4062 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -40,6 +40,8 @@
private final int mStreamId;
private final boolean mIsPtsPresent;
private final long mPts;
+ private final boolean mIsDtsPresent;
+ private final long mDts;
private final long mDataLength;
private final long mOffset;
private LinearBlock mLinearBlock;
@@ -50,12 +52,14 @@
private final AudioDescriptor mExtraMetaData;
// This constructor is used by JNI code only
- private MediaEvent(int streamId, boolean isPtsPresent, long pts, long dataLength, long offset,
- LinearBlock buffer, boolean isSecureMemory, long dataId, int mpuSequenceNumber,
- boolean isPrivateData, AudioDescriptor extraMetaData) {
+ private MediaEvent(int streamId, boolean isPtsPresent, long pts, boolean isDtsPresent, long dts,
+ long dataLength, long offset, LinearBlock buffer, boolean isSecureMemory, long dataId,
+ int mpuSequenceNumber, boolean isPrivateData, AudioDescriptor extraMetaData) {
mStreamId = streamId;
mIsPtsPresent = isPtsPresent;
mPts = pts;
+ mIsDtsPresent = isDtsPresent;
+ mDts = dts;
mDataLength = dataLength;
mOffset = offset;
mLinearBlock = buffer;
@@ -90,6 +94,26 @@
}
/**
+ * Returns whether DTS (Decode Time Stamp) is present.
+ *
+ * <p>This query is only supported in Tuner 2.0 or higher version. Unsupported version will
+ * return {@code false}.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
+ *
+ * @return {@code true} if DTS is present in PES header; {@code false} otherwise.
+ */
+ public boolean isDtsPresent() { return mIsDtsPresent; }
+
+ /**
+ * Gets DTS (Decode Time Stamp) for audio or video frame.
+ *
+ * * <p>This query is only supported in Tuner 2.0 or higher version. Unsupported version will
+ * return {@code -1}.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
+ */
+ public long getDts() { return mDts; }
+
+ /**
* Gets data size in bytes of audio or video frame.
*/
@BytesLong
diff --git a/media/java/android/media/tv/tuner/filter/SectionEvent.java b/media/java/android/media/tv/tuner/filter/SectionEvent.java
index ff12492..182bb94 100644
--- a/media/java/android/media/tv/tuner/filter/SectionEvent.java
+++ b/media/java/android/media/tv/tuner/filter/SectionEvent.java
@@ -28,10 +28,10 @@
private final int mTableId;
private final int mVersion;
private final int mSectionNum;
- private final int mDataLength;
+ private final long mDataLength;
// This constructor is used by JNI code only
- private SectionEvent(int tableId, int version, int sectionNum, int dataLength) {
+ private SectionEvent(int tableId, int version, int sectionNum, long dataLength) {
mTableId = tableId;
mVersion = version;
mSectionNum = sectionNum;
@@ -61,8 +61,13 @@
/**
* Gets data size in bytes of filtered data.
+ *
+ * @deprecated Use {@link #getDataLengthLong()}
*/
+ @Deprecated
public int getDataLength() {
- return mDataLength;
+ return (int) getDataLengthLong();
}
+
+ public long getDataLengthLong() { return mDataLength; }
}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index 31f1a63..582e4f5 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -19,10 +19,10 @@
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.media.tv.tuner.Lnb;
import android.media.tv.tuner.TunerVersionChecker;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -53,7 +53,7 @@
FRONTEND_STATUS_TYPE_MODULATIONS_EXT, FRONTEND_STATUS_TYPE_ROLL_OFF,
FRONTEND_STATUS_TYPE_IS_MISO_ENABLED, FRONTEND_STATUS_TYPE_IS_LINEAR,
FRONTEND_STATUS_TYPE_IS_SHORT_FRAMES_ENABLED, FRONTEND_STATUS_TYPE_ISDBT_MODE,
- FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG})
+ FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG, FRONTEND_STATUS_TYPE_STREAM_ID_LIST})
@Retention(RetentionPolicy.SOURCE)
public @interface FrontendStatusType {}
@@ -254,6 +254,12 @@
public static final int FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG =
android.hardware.tv.tuner.FrontendStatusType.ISDBT_PARTIAL_RECEPTION_FLAG;
+ /**
+ * Stream ID list included in a transponder.
+ */
+ public static final int FRONTEND_STATUS_TYPE_STREAM_ID_LIST =
+ android.hardware.tv.tuner.FrontendStatusType.STREAM_ID_LIST;
+
/** @hide */
@IntDef(value = {
AtscFrontendSettings.MODULATION_UNDEFINED,
@@ -493,6 +499,7 @@
private Boolean mIsShortFrames;
private Integer mIsdbtMode;
private Integer mIsdbtPartialReceptionFlag;
+ private int[] mStreamIds;
// Constructed and fields set by JNI code.
private FrontendStatus() {
@@ -1001,6 +1008,24 @@
}
/**
+ * Gets stream id list included in a transponder.
+ *
+ * <p>This query is only supported by Tuner HAL 2.0 or higher. Unsupported version or if HAL
+ * doesn't return stream id list status will throw IllegalStateException. Use
+ * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ @SuppressLint("ArrayReturn")
+ @NonNull
+ public int[] getStreamIdList() {
+ TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "stream id list status");
+ if (mStreamIds == null) {
+ throw new IllegalStateException("stream id list status is empty");
+ }
+ return mStreamIds;
+ }
+
+ /**
* Information of each tuning Physical Layer Pipes.
*/
public static class Atsc3PlpTuningInfo {
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index ded9652..e91e238 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -588,13 +588,13 @@
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/SectionEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIII)V");
+ jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIJ)V");
const DemuxFilterSectionEvent §ionEvent = event.get<DemuxFilterEvent::Tag::section>();
jint tableId = sectionEvent.tableId;
jint version = sectionEvent.version;
jint sectionNum = sectionEvent.sectionNum;
- jint dataLength = sectionEvent.dataLength;
+ jlong dataLength = sectionEvent.dataLength;
jobject obj = env->NewObject(eventClazz, eventInit, tableId, version, sectionNum, dataLength);
env->SetObjectArrayElement(arr, size, obj);
@@ -604,10 +604,9 @@
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MediaEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz,
- "<init>",
- "(IZJJJLandroid/media/MediaCodec$LinearBlock;"
- "ZJIZLandroid/media/tv/tuner/filter/AudioDescriptor;)V");
+ jmethodID eventInit = env->GetMethodID(eventClazz, "<init>",
+ "(IZJZJJJLandroid/media/MediaCodec$LinearBlock;"
+ "ZJIZLandroid/media/tv/tuner/filter/AudioDescriptor;)V");
jfieldID eventContext = env->GetFieldID(eventClazz, "mNativeContext", "J");
const DemuxFilterMediaEvent &mediaEvent = event.get<DemuxFilterEvent::Tag::media>();
@@ -633,15 +632,17 @@
jint streamId = mediaEvent.streamId;
jboolean isPtsPresent = mediaEvent.isPtsPresent;
jlong pts = mediaEvent.pts;
+ jboolean isDtsPresent = mediaEvent.isDtsPresent;
+ jlong dts = mediaEvent.dts;
jlong offset = mediaEvent.offset;
jboolean isSecureMemory = mediaEvent.isSecureMemory;
jlong avDataId = mediaEvent.avDataId;
jint mpuSequenceNumber = mediaEvent.mpuSequenceNumber;
jboolean isPesPrivateData = mediaEvent.isPesPrivateData;
- jobject obj = env->NewObject(eventClazz, eventInit, streamId, isPtsPresent, pts, dataLength,
- offset, nullptr, isSecureMemory, avDataId, mpuSequenceNumber,
- isPesPrivateData, audioDescriptor);
+ jobject obj = env->NewObject(eventClazz, eventInit, streamId, isPtsPresent, pts, isDtsPresent,
+ dts, dataLength, offset, nullptr, isSecureMemory, avDataId,
+ mpuSequenceNumber, isPesPrivateData, audioDescriptor);
uint64_t avSharedMemSize = mFilterClient->getAvSharedHandleInfo().size;
if (mediaEvent.avMemory.fds.size() > 0 || mediaEvent.avDataId != 0 ||
@@ -733,16 +734,17 @@
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/DownloadEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIII)V");
+ jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIIII)V");
const DemuxFilterDownloadEvent &downloadEvent = event.get<DemuxFilterEvent::Tag::download>();
jint itemId = downloadEvent.itemId;
+ jint downloadId = downloadEvent.downloadId;
jint mpuSequenceNumber = downloadEvent.mpuSequenceNumber;
jint itemFragmentIndex = downloadEvent.itemFragmentIndex;
jint lastItemFragmentIndex = downloadEvent.lastItemFragmentIndex;
jint dataLength = downloadEvent.dataLength;
- jobject obj = env->NewObject(eventClazz, eventInit, itemId, mpuSequenceNumber,
+ jobject obj = env->NewObject(eventClazz, eventInit, itemId, downloadId, mpuSequenceNumber,
itemFragmentIndex, lastItemFragmentIndex, dataLength);
env->SetObjectArrayElement(arr, size, obj);
}
@@ -951,20 +953,45 @@
}
/////////////// FrontendClientCallbackImpl ///////////////////////
-FrontendClientCallbackImpl::FrontendClientCallbackImpl(jweak tunerObj) : mObject(tunerObj) {}
+FrontendClientCallbackImpl::FrontendClientCallbackImpl(JTuner* jtuner, jweak listener) {
+ ALOGV("FrontendClientCallbackImpl() with listener:%p", listener);
+ addCallbackListener(jtuner, listener);
+}
+
+void FrontendClientCallbackImpl::addCallbackListener(JTuner* jtuner, jweak listener) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jweak listenerRef = env->NewWeakGlobalRef(listener);
+ ALOGV("addCallbackListener() with listener:%p and ref:%p @%p", listener, listenerRef, this);
+ std::scoped_lock<std::mutex> lock(mMutex);
+ mListenersMap[jtuner] = listenerRef;
+}
+
+void FrontendClientCallbackImpl::removeCallbackListener(JTuner* listener) {
+ ALOGV("removeCallbackListener for listener:%p", listener);
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ std::scoped_lock<std::mutex> lock(mMutex);
+ if (mListenersMap.find(listener) != mListenersMap.end() && mListenersMap[listener]) {
+ env->DeleteWeakGlobalRef(mListenersMap[listener]);
+ mListenersMap.erase(listener);
+ }
+}
void FrontendClientCallbackImpl::onEvent(FrontendEventType frontendEventType) {
ALOGV("FrontendClientCallbackImpl::onEvent, type=%d", frontendEventType);
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jobject frontend(env->NewLocalRef(mObject));
- if (!env->IsSameObject(frontend, nullptr)) {
- env->CallVoidMethod(
- frontend,
- gFields.onFrontendEventID,
- (jint)frontendEventType);
- } else {
- ALOGE("FrontendClientCallbackImpl::onEvent:"
- "Frontend object has been freed. Ignoring callback.");
+ std::scoped_lock<std::mutex> lock(mMutex);
+ for (const auto& mapEntry : mListenersMap) {
+ ALOGV("JTuner:%p, jweak:%p", mapEntry.first, mapEntry.second);
+ jobject frontend(env->NewLocalRef(mapEntry.second));
+ if (!env->IsSameObject(frontend, nullptr)) {
+ env->CallVoidMethod(
+ frontend,
+ gFields.onFrontendEventID,
+ (jint)frontendEventType);
+ } else {
+ ALOGW("FrontendClientCallbackImpl::onEvent:"
+ "Frontend object has been freed. Ignoring callback.");
+ }
}
}
@@ -973,12 +1000,25 @@
ALOGV("FrontendClientCallbackImpl::onScanMessage, type=%d", type);
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
- jobject frontend(env->NewLocalRef(mObject));
- if (env->IsSameObject(frontend, nullptr)) {
- ALOGE("FrontendClientCallbackImpl::onScanMessage:"
- "Frontend object has been freed. Ignoring callback.");
- return;
+
+ std::scoped_lock<std::mutex> lock(mMutex);
+ for (const auto& mapEntry : mListenersMap) {
+ jobject frontend(env->NewLocalRef(mapEntry.second));
+ if (env->IsSameObject(frontend, nullptr)) {
+ ALOGE("FrontendClientCallbackImpl::onScanMessage:"
+ "Tuner object has been freed. Ignoring callback.");
+ continue;
+ }
+ executeOnScanMessage(env, clazz, frontend, type, message);
}
+}
+
+void FrontendClientCallbackImpl::executeOnScanMessage(
+ JNIEnv *env, const jclass& clazz, const jobject& frontend,
+ FrontendScanMessageType type,
+ const FrontendScanMessage& message) {
+ ALOGV("FrontendClientCallbackImpl::executeOnScanMessage, type=%d", type);
+
switch(type) {
case FrontendScanMessageType::LOCKED: {
if (message.get<FrontendScanMessage::Tag::isLocked>()) {
@@ -1157,11 +1197,14 @@
}
FrontendClientCallbackImpl::~FrontendClientCallbackImpl() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (mObject != nullptr) {
- env->DeleteWeakGlobalRef(mObject);
- mObject = nullptr;
+ JNIEnv *env = android::AndroidRuntime::getJNIEnv();
+ ALOGV("~FrontendClientCallbackImpl()");
+ std::scoped_lock<std::mutex> lock(mMutex);
+ for (const auto& mapEntry : mListenersMap) {
+ ALOGV("deleteRef :%p at @ %p", mapEntry.second, this);
+ env->DeleteWeakGlobalRef(mapEntry.second);
}
+ mListenersMap.clear();
}
/////////////// Tuner ///////////////////////
@@ -1180,6 +1223,10 @@
mSharedFeId = (int)Constant::INVALID_FRONTEND_ID;
}
+jweak JTuner::getObject() {
+ return mObject;
+}
+
JTuner::~JTuner() {
if (mFeClient != nullptr) {
mFeClient->close();
@@ -1192,6 +1239,7 @@
env->DeleteWeakGlobalRef(mObject);
env->DeleteGlobalRef(mClass);
mFeClient = nullptr;
+ mFeClientCb = nullptr;
mDemuxClient = nullptr;
mClass = nullptr;
mObject = nullptr;
@@ -1247,9 +1295,8 @@
return nullptr;
}
- sp<FrontendClientCallbackImpl> feClientCb =
- new FrontendClientCallbackImpl(env->NewWeakGlobalRef(mObject));
- mFeClient->setCallback(feClientCb);
+ mFeClientCb = new FrontendClientCallbackImpl(this, mObject);
+ mFeClient->setCallback(mFeClientCb);
// TODO: add more fields to frontend
return env->NewObject(
env->FindClass("android/media/tv/tuner/Tuner$Frontend"),
@@ -1269,6 +1316,18 @@
return (int)Result::SUCCESS;
}
+void JTuner::registerFeCbListener(JTuner* jtuner) {
+ if (mFeClientCb != nullptr && jtuner != nullptr) {
+ mFeClientCb->addCallbackListener(jtuner, jtuner->getObject());
+ }
+}
+
+void JTuner::unregisterFeCbListener(JTuner* jtuner) {
+ if (mFeClientCb != nullptr && jtuner != nullptr) {
+ mFeClientCb->removeCallbackListener(jtuner);
+ }
+}
+
jobject JTuner::getAnalogFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendCapabilities");
jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V");
@@ -2450,7 +2509,14 @@
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- default: {
+ case FrontendStatus::Tag::streamIdList: {
+ jfieldID field = env->GetFieldID(clazz, "mStreamIds", "[I");
+ std::vector<int32_t> ids = s.get<FrontendStatus::Tag::streamIdList>();
+
+ jintArray valObj = env->NewIntArray(v.size());
+ env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint *>(&ids[0]));
+
+ env->SetObjectField(statusObj, field, valObj);
break;
}
}
@@ -3188,6 +3254,20 @@
return tuner->shareFrontend(id);
}
+static void android_media_tv_Tuner_register_fe_cb_listener(
+ JNIEnv *env, jobject thiz, jlong shareeJTuner) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ JTuner *jtuner = (JTuner *)shareeJTuner;
+ tuner->registerFeCbListener(jtuner);
+}
+
+static void android_media_tv_Tuner_unregister_fe_cb_listener(
+ JNIEnv *env, jobject thiz, jlong shareeJTuner) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ JTuner *jtuner = (JTuner *)shareeJTuner;
+ tuner->unregisterFeCbListener(jtuner);
+}
+
static int android_media_tv_Tuner_tune(JNIEnv *env, jobject thiz, jint type, jobject settings) {
sp<JTuner> tuner = getTuner(env, thiz);
FrontendSettings setting = getFrontendSettings(env, type, settings);
@@ -3469,10 +3549,13 @@
static DemuxFilterDownloadSettings getFilterDownloadSettings(JNIEnv *env, const jobject& settings) {
jclass clazz = env->FindClass("android/media/tv/tuner/filter/DownloadSettings");
+ bool useDownloadId =
+ env->GetBooleanField(settings, env->GetFieldID(clazz, "mUseDownloadId", "Z"));
int32_t downloadId = env->GetIntField(settings, env->GetFieldID(clazz, "mDownloadId", "I"));
- DemuxFilterDownloadSettings filterDownloadSettings {
- .downloadId = downloadId,
+ DemuxFilterDownloadSettings filterDownloadSettings{
+ .useDownloadId = useDownloadId,
+ .downloadId = downloadId,
};
return filterDownloadSettings;
}
@@ -4253,6 +4336,17 @@
return (jlong)dvrClient->readFromFile(size);
}
+static jlong android_media_tv_Tuner_seek_dvr(JNIEnv *env, jobject dvr, jlong pos) {
+ sp<DvrClient> dvrClient = getDvrClient(env, dvr);
+ if (dvrClient == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Failed to seek dvr: dvr client not found");
+ return -1;
+ }
+
+ return (jlong)dvrClient->seekFile(pos);
+}
+
static jlong android_media_tv_Tuner_read_dvr_from_array(
JNIEnv* env, jobject dvr, jbyteArray buffer, jlong offset, jlong size) {
sp<DvrClient> dvrClient = getDvrClient(env, dvr);
@@ -4361,6 +4455,10 @@
(void *)android_media_tv_Tuner_open_frontend_by_handle },
{ "nativeShareFrontend", "(I)I",
(void *)android_media_tv_Tuner_share_frontend },
+ { "nativeRegisterFeCbListener", "(J)V",
+ (void*)android_media_tv_Tuner_register_fe_cb_listener },
+ { "nativeUnregisterFeCbListener", "(J)V",
+ (void*)android_media_tv_Tuner_unregister_fe_cb_listener },
{ "nativeTune", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;)I",
(void *)android_media_tv_Tuner_tune },
{ "nativeStopTune", "()I", (void *)android_media_tv_Tuner_stop_tune },
@@ -4403,38 +4501,37 @@
{ "nativeClose", "()I", (void *)android_media_tv_Tuner_close_tuner },
{ "nativeCloseFrontend", "(I)I", (void *)android_media_tv_Tuner_close_frontend },
{ "nativeCloseDemux", "(I)I", (void *)android_media_tv_Tuner_close_demux },
- {"nativeOpenSharedFilter",
+ { "nativeOpenSharedFilter",
"(Ljava/lang/String;)Landroid/media/tv/tuner/filter/SharedFilter;",
(void *)android_media_tv_Tuner_open_shared_filter},
};
static const JNINativeMethod gFilterMethods[] = {
{ "nativeConfigureFilter", "(IILandroid/media/tv/tuner/filter/FilterConfiguration;)I",
- (void *)android_media_tv_Tuner_configure_filter },
- { "nativeGetId", "()I", (void *)android_media_tv_Tuner_get_filter_id },
- { "nativeGetId64Bit", "()J",
- (void *)android_media_tv_Tuner_get_filter_64bit_id },
+ (void *)android_media_tv_Tuner_configure_filter},
+ { "nativeGetId", "()I", (void *)android_media_tv_Tuner_get_filter_id},
+ { "nativeGetId64Bit", "()J", (void *)android_media_tv_Tuner_get_filter_64bit_id},
{ "nativeConfigureMonitorEvent", "(I)I",
- (void *)android_media_tv_Tuner_configure_monitor_event },
+ (void *)android_media_tv_Tuner_configure_monitor_event},
{ "nativeSetDataSource", "(Landroid/media/tv/tuner/filter/Filter;)I",
- (void *)android_media_tv_Tuner_set_filter_data_source },
- { "nativeStartFilter", "()I", (void *)android_media_tv_Tuner_start_filter },
- { "nativeStopFilter", "()I", (void *)android_media_tv_Tuner_stop_filter },
- { "nativeFlushFilter", "()I", (void *)android_media_tv_Tuner_flush_filter },
- { "nativeRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq },
- { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_filter },
- {"nativeAcquireSharedFilterToken", "()Ljava/lang/String;",
+ (void *)android_media_tv_Tuner_set_filter_data_source},
+ { "nativeStartFilter", "()I", (void *)android_media_tv_Tuner_start_filter},
+ { "nativeStopFilter", "()I", (void *)android_media_tv_Tuner_stop_filter},
+ { "nativeFlushFilter", "()I", (void *)android_media_tv_Tuner_flush_filter},
+ { "nativeRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq},
+ { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_filter},
+ { "nativeAcquireSharedFilterToken", "()Ljava/lang/String;",
(void *)android_media_tv_Tuner_acquire_shared_filter_token},
- {"nativeFreeSharedFilterToken", "(Ljava/lang/String;)V",
+ { "nativeFreeSharedFilterToken", "(Ljava/lang/String;)V",
(void *)android_media_tv_Tuner_free_shared_filter_token},
};
static const JNINativeMethod gSharedFilterMethods[] = {
- {"nativeStartSharedFilter", "()I", (void *)android_media_tv_Tuner_start_filter},
- {"nativeStopSharedFilter", "()I", (void *)android_media_tv_Tuner_stop_filter},
- {"nativeFlushSharedFilter", "()I", (void *)android_media_tv_Tuner_flush_filter},
- {"nativeSharedRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq},
- {"nativeSharedClose", "()I", (void *)android_media_tv_Tuner_close_filter},
+ { "nativeStartSharedFilter", "()I", (void *)android_media_tv_Tuner_start_filter},
+ { "nativeStopSharedFilter", "()I", (void *)android_media_tv_Tuner_stop_filter},
+ { "nativeFlushSharedFilter", "()I", (void *)android_media_tv_Tuner_flush_filter},
+ { "nativeSharedRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq},
+ { "nativeSharedClose", "()I", (void *)android_media_tv_Tuner_close_filter},
};
static const JNINativeMethod gTimeFilterMethods[] = {
@@ -4474,18 +4571,19 @@
static const JNINativeMethod gDvrPlaybackMethods[] = {
{ "nativeAttachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I",
- (void *)android_media_tv_Tuner_attach_filter },
+ (void *)android_media_tv_Tuner_attach_filter},
{ "nativeDetachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I",
- (void *)android_media_tv_Tuner_detach_filter },
+ (void *)android_media_tv_Tuner_detach_filter},
{ "nativeConfigureDvr", "(Landroid/media/tv/tuner/dvr/DvrSettings;)I",
- (void *)android_media_tv_Tuner_configure_dvr },
- { "nativeStartDvr", "()I", (void *)android_media_tv_Tuner_start_dvr },
- { "nativeStopDvr", "()I", (void *)android_media_tv_Tuner_stop_dvr },
- { "nativeFlushDvr", "()I", (void *)android_media_tv_Tuner_flush_dvr },
- { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_dvr },
- { "nativeSetFileDescriptor", "(I)V", (void *)android_media_tv_Tuner_dvr_set_fd },
- { "nativeRead", "(J)J", (void *)android_media_tv_Tuner_read_dvr },
- { "nativeRead", "([BJJ)J", (void *)android_media_tv_Tuner_read_dvr_from_array },
+ (void *)android_media_tv_Tuner_configure_dvr},
+ { "nativeStartDvr", "()I", (void *)android_media_tv_Tuner_start_dvr},
+ { "nativeStopDvr", "()I", (void *)android_media_tv_Tuner_stop_dvr},
+ { "nativeFlushDvr", "()I", (void *)android_media_tv_Tuner_flush_dvr},
+ { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_dvr},
+ { "nativeSetFileDescriptor", "(I)V", (void *)android_media_tv_Tuner_dvr_set_fd},
+ { "nativeRead", "(J)J", (void *)android_media_tv_Tuner_read_dvr},
+ { "nativeRead", "([BJJ)J", (void *)android_media_tv_Tuner_read_dvr_from_array},
+ { "nativeSeek", "(J)J", (void *)android_media_tv_Tuner_seek_dvr},
};
static const JNINativeMethod gLnbMethods[] = {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 31d24ee..06e2492 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -149,14 +149,21 @@
void getRestartEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
};
+struct JTuner;
struct FrontendClientCallbackImpl : public FrontendClientCallback {
- FrontendClientCallbackImpl(jweak tunerObj);
+ FrontendClientCallbackImpl(JTuner*, jweak);
~FrontendClientCallbackImpl();
virtual void onEvent(FrontendEventType frontendEventType);
virtual void onScanMessage(
FrontendScanMessageType type, const FrontendScanMessage& message);
- jweak mObject;
+ void executeOnScanMessage(JNIEnv *env, const jclass& clazz, const jobject& frontend,
+ FrontendScanMessageType type,
+ const FrontendScanMessage& message);
+ void addCallbackListener(JTuner*, jweak obj);
+ void removeCallbackListener(JTuner* jtuner);
+ std::unordered_map<JTuner*, jweak> mListenersMap;
+ std::mutex mMutex;
};
struct JTuner : public RefBase {
@@ -171,6 +178,8 @@
jobject getFrontendIds();
jobject openFrontendByHandle(int feHandle);
int shareFrontend(int feId);
+ void registerFeCbListener(JTuner* jtuner);
+ void unregisterFeCbListener(JTuner* jtuner);
jint closeFrontendById(int id);
jobject getFrontendInfo(int id);
int tune(const FrontendSettings& settings);
@@ -192,6 +201,8 @@
jint closeFrontend();
jint closeDemux();
+ jweak getObject();
+
protected:
virtual ~JTuner();
@@ -200,6 +211,7 @@
jweak mObject;
static sp<TunerClient> mTunerClient;
sp<FrontendClient> mFeClient;
+ sp<FrontendClientCallbackImpl> mFeClientCb;
int mFeId;
int mSharedFeId;
sp<LnbClient> mLnbClient;
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 253b4e3..00c4a97 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -185,7 +185,7 @@
auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
soundpool::Stream* stream = mStreamManager.findStream(streamID);
if (stream != nullptr && stream->requestStop(streamID)) {
- mStreamManager.moveToRestartQueue(stream);
+ mStreamManager.moveToRestartQueue(stream, streamID);
}
}
diff --git a/media/jni/tuner/DvrClient.cpp b/media/jni/tuner/DvrClient.cpp
index 3027838..05683b6 100644
--- a/media/jni/tuner/DvrClient.cpp
+++ b/media/jni/tuner/DvrClient.cpp
@@ -22,6 +22,8 @@
#include <aidl/android/hardware/tv/tuner/DemuxQueueNotifyBits.h>
#include <android-base/logging.h>
#include <inttypes.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <utils/Log.h>
#include "ClientHelper.h"
@@ -200,6 +202,14 @@
return size;
}
+int64_t DvrClient::seekFile(int64_t pos) {
+ if (mFd < 0) {
+ ALOGE("Failed to seekFile. File is not configured");
+ return -1;
+ }
+ return lseek64(mFd, pos, SEEK_SET);
+}
+
Result DvrClient::configure(DvrSettings settings) {
if (mTunerDvr != nullptr) {
Status s = mTunerDvr->configure(settings);
diff --git a/media/jni/tuner/DvrClient.h b/media/jni/tuner/DvrClient.h
index 9080c72..61c0325 100644
--- a/media/jni/tuner/DvrClient.h
+++ b/media/jni/tuner/DvrClient.h
@@ -82,6 +82,11 @@
int64_t writeToFile(int64_t size);
/**
+ * Seeks the Dvr file descriptor from the beginning of the file.
+ */
+ int64_t seekFile(int64_t pos);
+
+ /**
* Write data to the given buffer with given size. Return the actual write size.
*/
int64_t writeToBuffer(int8_t* buffer, int64_t size);
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index ceba4d6..f76811e 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -364,7 +364,7 @@
sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
- sp<GraphicBuffer> graphic_buffer(reinterpret_cast<GraphicBuffer*>(buffer));
+ sp<GraphicBuffer> graphic_buffer(GraphicBuffer::fromAHardwareBuffer(buffer));
std::optional<sp<Fence>> fence = std::nullopt;
if (acquire_fence_fd != -1) {
diff --git a/packages/CarrierDefaultApp/AndroidManifest.xml b/packages/CarrierDefaultApp/AndroidManifest.xml
index 3e65df2..632dfb3 100644
--- a/packages/CarrierDefaultApp/AndroidManifest.xml
+++ b/packages/CarrierDefaultApp/AndroidManifest.xml
@@ -27,6 +27,7 @@
<uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" />
<uses-permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS" />
<uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:label="@string/app_name"
diff --git a/packages/ConnectivityT/OWNERS b/packages/ConnectivityT/OWNERS
new file mode 100644
index 0000000..e267d19
--- /dev/null
+++ b/packages/ConnectivityT/OWNERS
@@ -0,0 +1,2 @@
+file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+per-file **IpSec* = file:/services/core/java/com/android/server/vcn/OWNERS
diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp
new file mode 100644
index 0000000..931a55b
--- /dev/null
+++ b/packages/ConnectivityT/framework-t/Android.bp
@@ -0,0 +1,139 @@
+//
+// 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.
+//
+
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+// NetworkStats related libraries.
+
+filegroup {
+ name: "framework-connectivity-netstats-internal-sources",
+ srcs: [
+ "src/android/app/usage/*.java",
+ "src/android/net/DataUsage*.*",
+ "src/android/net/INetworkStats*.*",
+ "src/android/net/NetworkIdentity*.java",
+ "src/android/net/NetworkStateSnapshot.*",
+ "src/android/net/NetworkStats*.*",
+ "src/android/net/NetworkTemplate.*",
+ "src/android/net/TrafficStats.java",
+ "src/android/net/UnderlyingNetworkInfo.*",
+ "src/android/net/netstats/**/*.*",
+ ],
+ path: "src",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+filegroup {
+ name: "framework-connectivity-netstats-aidl-export-sources",
+ srcs: [
+ "aidl-export/android/net/NetworkStats.aidl",
+ "aidl-export/android/net/NetworkTemplate.aidl",
+ ],
+ path: "aidl-export",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+filegroup {
+ name: "framework-connectivity-netstats-sources",
+ srcs: [
+ ":framework-connectivity-netstats-internal-sources",
+ ":framework-connectivity-netstats-aidl-export-sources",
+ ],
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+// Nsd related libraries.
+
+filegroup {
+ name: "framework-connectivity-nsd-internal-sources",
+ srcs: [
+ "src/android/net/nsd/*.aidl",
+ "src/android/net/nsd/*.java",
+ ],
+ path: "src",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+filegroup {
+ name: "framework-connectivity-nsd-aidl-export-sources",
+ srcs: [
+ "aidl-export/android/net/nsd/*.aidl",
+ ],
+ path: "aidl-export",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+filegroup {
+ name: "framework-connectivity-nsd-sources",
+ srcs: [
+ ":framework-connectivity-nsd-internal-sources",
+ ":framework-connectivity-nsd-aidl-export-sources",
+ ],
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+// IpSec related libraries.
+
+filegroup {
+ name: "framework-connectivity-ipsec-sources",
+ srcs: [
+ "src/android/net/IIpSecService.aidl",
+ "src/android/net/IpSec*.*",
+ ],
+ path: "src",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+// Connectivity-T common libraries.
+
+filegroup {
+ name: "framework-connectivity-tiramisu-internal-sources",
+ srcs: [
+ "src/android/net/ConnectivityFrameworkInitializerTiramisu.java",
+ ],
+ path: "src",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+filegroup {
+ name: "framework-connectivity-tiramisu-sources",
+ srcs: [
+ ":framework-connectivity-ipsec-sources",
+ ":framework-connectivity-netstats-sources",
+ ":framework-connectivity-nsd-sources",
+ ":framework-connectivity-tiramisu-internal-sources",
+ ],
+ visibility: ["//frameworks/base"],
+}
diff --git a/core/java/android/net/NetworkStats.aidl b/packages/ConnectivityT/framework-t/aidl-export/android/net/NetworkStats.aidl
similarity index 100%
rename from core/java/android/net/NetworkStats.aidl
rename to packages/ConnectivityT/framework-t/aidl-export/android/net/NetworkStats.aidl
diff --git a/core/java/android/net/NetworkTemplate.aidl b/packages/ConnectivityT/framework-t/aidl-export/android/net/NetworkTemplate.aidl
similarity index 100%
rename from core/java/android/net/NetworkTemplate.aidl
rename to packages/ConnectivityT/framework-t/aidl-export/android/net/NetworkTemplate.aidl
diff --git a/packages/Nsd/framework/aidl-export/android/net/nsd/NsdServiceInfo.aidl b/packages/ConnectivityT/framework-t/aidl-export/android/net/nsd/NsdServiceInfo.aidl
similarity index 100%
rename from packages/Nsd/framework/aidl-export/android/net/nsd/NsdServiceInfo.aidl
rename to packages/ConnectivityT/framework-t/aidl-export/android/net/nsd/NsdServiceInfo.aidl
diff --git a/core/java/android/app/usage/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
similarity index 100%
rename from core/java/android/app/usage/NetworkStats.java
rename to packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
similarity index 100%
rename from core/java/android/app/usage/NetworkStatsManager.java
rename to packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
diff --git a/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java b/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java
new file mode 100644
index 0000000..630f902e
--- /dev/null
+++ b/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+package android.net;
+
+import android.annotation.SystemApi;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+import android.net.nsd.INsdManager;
+import android.net.nsd.NsdManager;
+
+/**
+ * Class for performing registration for Connectivity services which are exposed via updatable APIs
+ * since Android T.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class ConnectivityFrameworkInitializerTiramisu {
+ private ConnectivityFrameworkInitializerTiramisu() {}
+
+ /**
+ * Called by {@link SystemServiceRegistry}'s static initializer and registers nsd services to
+ * {@link Context}, so that {@link Context#getSystemService} can return them.
+ *
+ * @throws IllegalStateException if this is called anywhere besides
+ * {@link SystemServiceRegistry}.
+ */
+ public static void registerServiceWrappers() {
+ SystemServiceRegistry.registerContextAwareService(
+ Context.NSD_SERVICE,
+ NsdManager.class,
+ (context, serviceBinder) -> {
+ INsdManager service = INsdManager.Stub.asInterface(serviceBinder);
+ return new NsdManager(context, service);
+ }
+ );
+ }
+}
diff --git a/core/java/android/net/DataUsageRequest.aidl b/packages/ConnectivityT/framework-t/src/android/net/DataUsageRequest.aidl
similarity index 100%
rename from core/java/android/net/DataUsageRequest.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/DataUsageRequest.aidl
diff --git a/core/java/android/net/DataUsageRequest.java b/packages/ConnectivityT/framework-t/src/android/net/DataUsageRequest.java
similarity index 100%
rename from core/java/android/net/DataUsageRequest.java
rename to packages/ConnectivityT/framework-t/src/android/net/DataUsageRequest.java
diff --git a/core/java/android/net/IIpSecService.aidl b/packages/ConnectivityT/framework-t/src/android/net/IIpSecService.aidl
similarity index 100%
rename from core/java/android/net/IIpSecService.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/IIpSecService.aidl
diff --git a/core/java/android/net/INetworkStatsService.aidl b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
similarity index 100%
rename from core/java/android/net/INetworkStatsService.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
diff --git a/core/java/android/net/INetworkStatsSession.aidl b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl
similarity index 79%
rename from core/java/android/net/INetworkStatsSession.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl
index f13f2cb..dfedf66 100644
--- a/core/java/android/net/INetworkStatsSession.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl
@@ -33,7 +33,17 @@
@UnsupportedAppUsage
NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template, int fields);
- /** Return network layer usage summary per UID for traffic that matches template. */
+ /**
+ * Return network layer usage summary per UID for traffic that matches template.
+ *
+ * <p>The resulting {@code NetworkStats#getElapsedRealtime()} contains time delta between
+ * {@code start} and {@code end}.
+ *
+ * @param template - a predicate to filter netstats.
+ * @param start - start of the range, timestamp in milliseconds since the epoch.
+ * @param end - end of the range, timestamp in milliseconds since the epoch.
+ * @param includeTags - includes data usage tags if true.
+ */
@UnsupportedAppUsage
NetworkStats getSummaryForAllUid(in NetworkTemplate template, long start, long end, boolean includeTags);
/** Return historical network layer stats for specific UID traffic that matches template. */
diff --git a/core/java/android/net/IpSecAlgorithm.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
similarity index 100%
rename from core/java/android/net/IpSecAlgorithm.java
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
diff --git a/core/java/android/net/IpSecConfig.aidl b/packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.aidl
similarity index 100%
rename from core/java/android/net/IpSecConfig.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.aidl
diff --git a/core/java/android/net/IpSecConfig.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.java
similarity index 100%
rename from core/java/android/net/IpSecConfig.java
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.java
diff --git a/core/java/android/net/IpSecManager.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
similarity index 100%
rename from core/java/android/net/IpSecManager.java
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
diff --git a/core/java/android/net/IpSecSpiResponse.aidl b/packages/ConnectivityT/framework-t/src/android/net/IpSecSpiResponse.aidl
similarity index 100%
rename from core/java/android/net/IpSecSpiResponse.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecSpiResponse.aidl
diff --git a/core/java/android/net/IpSecSpiResponse.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecSpiResponse.java
similarity index 100%
rename from core/java/android/net/IpSecSpiResponse.java
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecSpiResponse.java
diff --git a/core/java/android/net/IpSecTransform.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java
similarity index 100%
rename from core/java/android/net/IpSecTransform.java
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java
diff --git a/core/java/android/net/IpSecTransformResponse.aidl b/packages/ConnectivityT/framework-t/src/android/net/IpSecTransformResponse.aidl
similarity index 100%
rename from core/java/android/net/IpSecTransformResponse.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecTransformResponse.aidl
diff --git a/core/java/android/net/IpSecTransformResponse.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecTransformResponse.java
similarity index 100%
rename from core/java/android/net/IpSecTransformResponse.java
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecTransformResponse.java
diff --git a/core/java/android/net/IpSecTunnelInterfaceResponse.aidl b/packages/ConnectivityT/framework-t/src/android/net/IpSecTunnelInterfaceResponse.aidl
similarity index 100%
rename from core/java/android/net/IpSecTunnelInterfaceResponse.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecTunnelInterfaceResponse.aidl
diff --git a/core/java/android/net/IpSecTunnelInterfaceResponse.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecTunnelInterfaceResponse.java
similarity index 100%
rename from core/java/android/net/IpSecTunnelInterfaceResponse.java
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecTunnelInterfaceResponse.java
diff --git a/core/java/android/net/IpSecUdpEncapResponse.aidl b/packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.aidl
similarity index 100%
rename from core/java/android/net/IpSecUdpEncapResponse.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.aidl
diff --git a/core/java/android/net/IpSecUdpEncapResponse.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.java
similarity index 100%
rename from core/java/android/net/IpSecUdpEncapResponse.java
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.java
diff --git a/core/java/android/net/NetworkIdentity.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
similarity index 100%
rename from core/java/android/net/NetworkIdentity.java
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
diff --git a/services/core/java/com/android/server/net/NetworkIdentitySet.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
similarity index 96%
rename from services/core/java/com/android/server/net/NetworkIdentitySet.java
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
index 22ed781..abbebef 100644
--- a/services/core/java/com/android/server/net/NetworkIdentitySet.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.android.server.net;
+package android.net;
-import android.net.NetworkIdentity;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+
import android.service.NetworkIdentitySetProto;
import android.util.proto.ProtoOutputStream;
@@ -25,8 +26,6 @@
import java.io.IOException;
import java.util.HashSet;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-
/**
* Identity of a {@code iface}, defined by the set of {@link NetworkIdentity}
* active on that interface.
@@ -97,6 +96,9 @@
}
}
+ /**
+ * Method to serialize this object into a {@code DataOutput}.
+ */
public void writeToStream(DataOutput out) throws IOException {
out.writeInt(VERSION_ADD_OEM_MANAGED_NETWORK);
out.writeInt(size());
@@ -179,6 +181,9 @@
return ident.compareTo(anotherIdent);
}
+ /**
+ * Method to dump this object into proto debug file.
+ */
public void dumpDebug(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
diff --git a/core/java/android/net/NetworkStateSnapshot.aidl b/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl
similarity index 100%
rename from core/java/android/net/NetworkStateSnapshot.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl
diff --git a/core/java/android/net/NetworkStateSnapshot.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java
similarity index 100%
rename from core/java/android/net/NetworkStateSnapshot.java
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java
diff --git a/core/java/android/net/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
similarity index 99%
rename from core/java/android/net/NetworkStats.java
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
index 46c83df..c7ffc19 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
@@ -220,8 +220,10 @@
// TODO: move fields to "mVariable" notation
/**
- * {@link SystemClock#elapsedRealtime()} timestamp when this data was
+ * {@link SystemClock#elapsedRealtime()} timestamp in milliseconds when this data was
* generated.
+ * It's a timestamps delta when {@link #subtract()},
+ * {@code INetworkStatsSession#getSummaryForAllUid()} methods are used.
*/
private long elapsedRealtime;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
diff --git a/services/core/java/com/android/server/net/NetworkStatsAccess.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsAccess.java
similarity index 97%
rename from services/core/java/com/android/server/net/NetworkStatsAccess.java
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkStatsAccess.java
index d25eae4..3885a9e 100644
--- a/services/core/java/com/android/server/net/NetworkStatsAccess.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsAccess.java
@@ -11,10 +11,10 @@
* 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
+ * limitations under the License.
*/
-package com.android.server.net;
+package android.net;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.net.NetworkStats.UID_ALL;
@@ -37,7 +37,11 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-/** Utility methods for controlling access to network stats APIs. */
+/**
+ * Utility methods for controlling access to network stats APIs.
+ *
+ * @hide
+ */
public final class NetworkStatsAccess {
private NetworkStatsAccess() {}
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
similarity index 95%
rename from services/core/java/com/android/server/net/NetworkStatsCollection.java
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
index df372b1..0d3b9ed 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.net;
+package android.net;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
@@ -31,13 +31,7 @@
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import static com.android.internal.net.NetworkUtilsInternal.multiplySafeByRational;
-import static com.android.server.net.NetworkStatsService.TAG;
-import android.net.NetworkIdentity;
-import android.net.NetworkStats;
-import android.net.NetworkStatsHistory;
-import android.net.NetworkTemplate;
-import android.net.TrafficStats;
import android.os.Binder;
import android.service.NetworkStatsCollectionKeyProto;
import android.service.NetworkStatsCollectionProto;
@@ -59,16 +53,15 @@
import com.android.internal.util.FileRotator;
import com.android.internal.util.IndentingPrintWriter;
-import libcore.io.IoUtils;
-
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
+import libcore.io.IoUtils;
+
import java.io.BufferedInputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
-import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -86,8 +79,11 @@
/**
* Collection of {@link NetworkStatsHistory}, stored based on combined key of
* {@link NetworkIdentitySet}, UID, set, and tag. Knows how to persist itself.
+ *
+ * @hide
*/
public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.Writer {
+ private static final String TAG = NetworkStatsCollection.class.getSimpleName();
/** File header magic number: "ANET" */
private static final int FILE_MAGIC = 0x414E4554;
@@ -335,7 +331,13 @@
/**
* Summarize all {@link NetworkStatsHistory} in this collection which match
- * the requested parameters.
+ * the requested parameters across the requested range.
+ *
+ * @param template - a predicate for filtering netstats.
+ * @param start - start of the range, timestamp in milliseconds since the epoch.
+ * @param end - end of the range, timestamp in milliseconds since the epoch.
+ * @param accessLevel - caller access level.
+ * @param callerUid - caller UID.
*/
public NetworkStats getSummary(NetworkTemplate template, long start, long end,
@NetworkStatsAccess.Level int accessLevel, int callerUid) {
@@ -361,8 +363,8 @@
entry.uid = key.uid;
entry.set = key.set;
entry.tag = key.tag;
- entry.defaultNetwork = key.ident.areAllMembersOnDefaultNetwork() ?
- DEFAULT_NETWORK_YES : DEFAULT_NETWORK_NO;
+ entry.defaultNetwork = key.ident.areAllMembersOnDefaultNetwork()
+ ? DEFAULT_NETWORK_YES : DEFAULT_NETWORK_NO;
entry.metered = key.ident.isAnyMemberMetered() ? METERED_YES : METERED_NO;
entry.roaming = key.ident.isAnyMemberRoaming() ? ROAMING_YES : ROAMING_NO;
entry.rxBytes = historyEntry.rxBytes;
@@ -516,6 +518,12 @@
}
}
+ /**
+ * Read legacy network summary statistics file format into the collection,
+ * See {@code NetworkStatsService#maybeUpgradeLegacyStatsLocked}.
+ *
+ * @deprecated
+ */
@Deprecated
public void readLegacyNetwork(File file) throws IOException {
final AtomicFile inputFile = new AtomicFile(file);
@@ -555,6 +563,12 @@
}
}
+ /**
+ * Read legacy Uid statistics file format into the collection,
+ * See {@code NetworkStatsService#maybeUpgradeLegacyStatsLocked}.
+ *
+ * @deprecated
+ */
@Deprecated
public void readLegacyUid(File file, boolean onlyTags) throws IOException {
final AtomicFile inputFile = new AtomicFile(file);
@@ -769,19 +783,19 @@
public final int set;
public final int tag;
- private final int hashCode;
+ private final int mHashCode;
- public Key(NetworkIdentitySet ident, int uid, int set, int tag) {
+ Key(NetworkIdentitySet ident, int uid, int set, int tag) {
this.ident = ident;
this.uid = uid;
this.set = set;
this.tag = tag;
- hashCode = Objects.hash(ident, uid, set, tag);
+ mHashCode = Objects.hash(ident, uid, set, tag);
}
@Override
public int hashCode() {
- return hashCode;
+ return mHashCode;
}
@Override
diff --git a/core/java/android/net/NetworkStatsHistory.aidl b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.aidl
similarity index 100%
rename from core/java/android/net/NetworkStatsHistory.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.aidl
diff --git a/core/java/android/net/NetworkStatsHistory.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
similarity index 97%
rename from core/java/android/net/NetworkStatsHistory.java
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
index f413063..a875e1a 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
@@ -526,6 +526,13 @@
/**
* Return interpolated data usage across the requested range. Interpolates
* across buckets, so values may be rounded slightly.
+ *
+ * <p>If the active bucket is not completed yet, it returns the proportional value of it
+ * based on its duration and the {@code end} param.
+ *
+ * @param start - start of the range, timestamp in milliseconds since the epoch.
+ * @param end - end of the range, timestamp in milliseconds since the epoch.
+ * @param recycle - entry instance for performance, could be null.
*/
@UnsupportedAppUsage
public Entry getValues(long start, long end, Entry recycle) {
@@ -535,6 +542,11 @@
/**
* Return interpolated data usage across the requested range. Interpolates
* across buckets, so values may be rounded slightly.
+ *
+ * @param start - start of the range, timestamp in milliseconds since the epoch.
+ * @param end - end of the range, timestamp in milliseconds since the epoch.
+ * @param now - current timestamp in milliseconds since the epoch (wall clock).
+ * @param recycle - entry instance for performance, could be null.
*/
@UnsupportedAppUsage
public Entry getValues(long start, long end, long now, Entry recycle) {
diff --git a/core/java/android/net/NetworkTemplate.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
similarity index 91%
rename from core/java/android/net/NetworkTemplate.java
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
index c906a13..8b9c14d 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
@@ -44,16 +44,10 @@
import android.telephony.Annotation.NetworkType;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import android.util.BackupUtils;
-import android.util.Log;
import com.android.internal.util.ArrayUtils;
import com.android.net.module.util.NetworkIdentityUtils;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
@@ -69,19 +63,6 @@
public class NetworkTemplate implements Parcelable {
private static final String TAG = "NetworkTemplate";
- /**
- * Initial Version of the backup serializer.
- */
- public static final int BACKUP_VERSION_1_INIT = 1;
- /**
- * Version of the backup serializer that added carrier template support.
- */
- public static final int BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE = 2;
- /**
- * Current Version of the Backup Serializer.
- */
- private static final int BACKUP_VERSION = BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE;
-
public static final int MATCH_MOBILE = 1;
public static final int MATCH_WIFI = 4;
public static final int MATCH_ETHERNET = 5;
@@ -849,58 +830,4 @@
return new NetworkTemplate[size];
}
};
-
- public byte[] getBytesForBackup() throws IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- DataOutputStream out = new DataOutputStream(baos);
-
- if (!isPersistable()) {
- Log.wtf(TAG, "Trying to backup non-persistable template: " + this);
- }
-
- out.writeInt(BACKUP_VERSION);
-
- out.writeInt(mMatchRule);
- BackupUtils.writeString(out, mSubscriberId);
- BackupUtils.writeString(out, mNetworkId);
- out.writeInt(mMetered);
- out.writeInt(mSubscriberIdMatchRule);
-
- return baos.toByteArray();
- }
-
- public static NetworkTemplate getNetworkTemplateFromBackup(DataInputStream in)
- throws IOException, BackupUtils.BadVersionException {
- int version = in.readInt();
- if (version < BACKUP_VERSION_1_INIT || version > BACKUP_VERSION) {
- throw new BackupUtils.BadVersionException("Unknown Backup Serialization Version");
- }
-
- int matchRule = in.readInt();
- String subscriberId = BackupUtils.readString(in);
- String networkId = BackupUtils.readString(in);
-
- final int metered;
- final int subscriberIdMatchRule;
- if (version >= BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE) {
- metered = in.readInt();
- subscriberIdMatchRule = in.readInt();
- } else {
- // For backward compatibility, fill the missing filters from match rules.
- metered = (matchRule == MATCH_MOBILE || matchRule == MATCH_MOBILE_WILDCARD
- || matchRule == MATCH_CARRIER) ? METERED_YES : METERED_ALL;
- subscriberIdMatchRule = SUBSCRIBER_ID_MATCH_RULE_EXACT;
- }
-
- try {
- return new NetworkTemplate(matchRule,
- subscriberId, new String[] { subscriberId },
- networkId, metered, NetworkStats.ROAMING_ALL,
- NetworkStats.DEFAULT_NETWORK_ALL, NetworkTemplate.NETWORK_TYPE_ALL,
- NetworkTemplate.OEM_MANAGED_ALL, subscriberIdMatchRule);
- } catch (IllegalArgumentException e) {
- throw new BackupUtils.BadVersionException(
- "Restored network template contains unknown match rule " + matchRule, e);
- }
- }
}
diff --git a/core/java/android/net/TrafficStats.java b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
similarity index 100%
rename from core/java/android/net/TrafficStats.java
rename to packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
diff --git a/core/java/android/net/UnderlyingNetworkInfo.aidl b/packages/ConnectivityT/framework-t/src/android/net/UnderlyingNetworkInfo.aidl
similarity index 100%
rename from core/java/android/net/UnderlyingNetworkInfo.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/UnderlyingNetworkInfo.aidl
diff --git a/core/java/android/net/UnderlyingNetworkInfo.java b/packages/ConnectivityT/framework-t/src/android/net/UnderlyingNetworkInfo.java
similarity index 100%
rename from core/java/android/net/UnderlyingNetworkInfo.java
rename to packages/ConnectivityT/framework-t/src/android/net/UnderlyingNetworkInfo.java
diff --git a/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl b/packages/ConnectivityT/framework-t/src/android/net/netstats/provider/INetworkStatsProvider.aidl
similarity index 100%
rename from core/java/android/net/netstats/provider/INetworkStatsProvider.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/netstats/provider/INetworkStatsProvider.aidl
diff --git a/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl b/packages/ConnectivityT/framework-t/src/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
similarity index 100%
rename from core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
diff --git a/core/java/android/net/netstats/provider/NetworkStatsProvider.java b/packages/ConnectivityT/framework-t/src/android/net/netstats/provider/NetworkStatsProvider.java
similarity index 100%
rename from core/java/android/net/netstats/provider/NetworkStatsProvider.java
rename to packages/ConnectivityT/framework-t/src/android/net/netstats/provider/NetworkStatsProvider.java
diff --git a/packages/Nsd/framework/src/android/net/nsd/INsdManager.aidl b/packages/ConnectivityT/framework-t/src/android/net/nsd/INsdManager.aidl
similarity index 100%
rename from packages/Nsd/framework/src/android/net/nsd/INsdManager.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/nsd/INsdManager.aidl
diff --git a/packages/Nsd/framework/src/android/net/nsd/INsdManagerCallback.aidl b/packages/ConnectivityT/framework-t/src/android/net/nsd/INsdManagerCallback.aidl
similarity index 100%
rename from packages/Nsd/framework/src/android/net/nsd/INsdManagerCallback.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/nsd/INsdManagerCallback.aidl
diff --git a/packages/Nsd/framework/src/android/net/nsd/INsdServiceConnector.aidl b/packages/ConnectivityT/framework-t/src/android/net/nsd/INsdServiceConnector.aidl
similarity index 100%
rename from packages/Nsd/framework/src/android/net/nsd/INsdServiceConnector.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/nsd/INsdServiceConnector.aidl
diff --git a/packages/Nsd/framework/src/android/net/nsd/NsdManager.java b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
similarity index 95%
rename from packages/Nsd/framework/src/android/net/nsd/NsdManager.java
rename to packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
index 6c597e2..0f21e55 100644
--- a/packages/Nsd/framework/src/android/net/nsd/NsdManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
@@ -16,10 +16,6 @@
package android.net.nsd;
-import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.internal.util.Preconditions.checkStringNotEmpty;
-
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemService;
@@ -32,11 +28,13 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Protocol;
+
+import java.util.Objects;
/**
* The Network Service Discovery Manager class provides the API to discover services
@@ -175,65 +173,63 @@
*/
public static final int NSD_STATE_ENABLED = 2;
- private static final int BASE = Protocol.BASE_NSD_MANAGER;
+ /** @hide */
+ public static final int DISCOVER_SERVICES = 1;
+ /** @hide */
+ public static final int DISCOVER_SERVICES_STARTED = 2;
+ /** @hide */
+ public static final int DISCOVER_SERVICES_FAILED = 3;
+ /** @hide */
+ public static final int SERVICE_FOUND = 4;
+ /** @hide */
+ public static final int SERVICE_LOST = 5;
/** @hide */
- public static final int DISCOVER_SERVICES = BASE + 1;
+ public static final int STOP_DISCOVERY = 6;
/** @hide */
- public static final int DISCOVER_SERVICES_STARTED = BASE + 2;
+ public static final int STOP_DISCOVERY_FAILED = 7;
/** @hide */
- public static final int DISCOVER_SERVICES_FAILED = BASE + 3;
- /** @hide */
- public static final int SERVICE_FOUND = BASE + 4;
- /** @hide */
- public static final int SERVICE_LOST = BASE + 5;
+ public static final int STOP_DISCOVERY_SUCCEEDED = 8;
/** @hide */
- public static final int STOP_DISCOVERY = BASE + 6;
+ public static final int REGISTER_SERVICE = 9;
/** @hide */
- public static final int STOP_DISCOVERY_FAILED = BASE + 7;
+ public static final int REGISTER_SERVICE_FAILED = 10;
/** @hide */
- public static final int STOP_DISCOVERY_SUCCEEDED = BASE + 8;
+ public static final int REGISTER_SERVICE_SUCCEEDED = 11;
/** @hide */
- public static final int REGISTER_SERVICE = BASE + 9;
+ public static final int UNREGISTER_SERVICE = 12;
/** @hide */
- public static final int REGISTER_SERVICE_FAILED = BASE + 10;
+ public static final int UNREGISTER_SERVICE_FAILED = 13;
/** @hide */
- public static final int REGISTER_SERVICE_SUCCEEDED = BASE + 11;
+ public static final int UNREGISTER_SERVICE_SUCCEEDED = 14;
/** @hide */
- public static final int UNREGISTER_SERVICE = BASE + 12;
+ public static final int RESOLVE_SERVICE = 15;
/** @hide */
- public static final int UNREGISTER_SERVICE_FAILED = BASE + 13;
+ public static final int RESOLVE_SERVICE_FAILED = 16;
/** @hide */
- public static final int UNREGISTER_SERVICE_SUCCEEDED = BASE + 14;
+ public static final int RESOLVE_SERVICE_SUCCEEDED = 17;
/** @hide */
- public static final int RESOLVE_SERVICE = BASE + 18;
- /** @hide */
- public static final int RESOLVE_SERVICE_FAILED = BASE + 19;
- /** @hide */
- public static final int RESOLVE_SERVICE_SUCCEEDED = BASE + 20;
+ public static final int DAEMON_CLEANUP = 18;
/** @hide */
- public static final int DAEMON_CLEANUP = BASE + 21;
+ public static final int DAEMON_STARTUP = 19;
/** @hide */
- public static final int DAEMON_STARTUP = BASE + 22;
+ public static final int ENABLE = 20;
+ /** @hide */
+ public static final int DISABLE = 21;
/** @hide */
- public static final int ENABLE = BASE + 24;
- /** @hide */
- public static final int DISABLE = BASE + 25;
+ public static final int NATIVE_DAEMON_EVENT = 22;
/** @hide */
- public static final int NATIVE_DAEMON_EVENT = BASE + 26;
-
+ public static final int REGISTER_CLIENT = 23;
/** @hide */
- public static final int REGISTER_CLIENT = BASE + 27;
- /** @hide */
- public static final int UNREGISTER_CLIENT = BASE + 28;
+ public static final int UNREGISTER_CLIENT = 24;
/** Dns based service discovery protocol */
public static final int PROTOCOL_DNS_SD = 0x0001;
@@ -550,7 +546,9 @@
final int key;
synchronized (mMapLock) {
int valueIndex = mListenerMap.indexOfValue(listener);
- checkArgument(valueIndex == -1, "listener already in use");
+ if (valueIndex != -1) {
+ throw new IllegalArgumentException("listener already in use");
+ }
key = nextListenerKey();
mListenerMap.put(key, listener);
mServiceMap.put(key, s);
@@ -569,7 +567,9 @@
checkListener(listener);
synchronized (mMapLock) {
int valueIndex = mListenerMap.indexOfValue(listener);
- checkArgument(valueIndex != -1, "listener not registered");
+ if (valueIndex == -1) {
+ throw new IllegalArgumentException("listener not registered");
+ }
return mListenerMap.keyAt(valueIndex);
}
}
@@ -598,7 +598,9 @@
*/
public void registerService(NsdServiceInfo serviceInfo, int protocolType,
RegistrationListener listener) {
- checkArgument(serviceInfo.getPort() > 0, "Invalid port number");
+ if (serviceInfo.getPort() <= 0) {
+ throw new IllegalArgumentException("Invalid port number");
+ }
checkServiceInfo(serviceInfo);
checkProtocol(protocolType);
int key = putListener(listener, serviceInfo);
@@ -660,7 +662,9 @@
* Cannot be null. Cannot be in use for an active service discovery.
*/
public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) {
- checkStringNotEmpty(serviceType, "Service type cannot be empty");
+ if (TextUtils.isEmpty(serviceType)) {
+ throw new IllegalArgumentException("Service type cannot be empty");
+ }
checkProtocol(protocolType);
NsdServiceInfo s = new NsdServiceInfo();
@@ -719,16 +723,22 @@
}
private static void checkListener(Object listener) {
- checkNotNull(listener, "listener cannot be null");
+ Objects.requireNonNull(listener, "listener cannot be null");
}
private static void checkProtocol(int protocolType) {
- checkArgument(protocolType == PROTOCOL_DNS_SD, "Unsupported protocol");
+ if (protocolType != PROTOCOL_DNS_SD) {
+ throw new IllegalArgumentException("Unsupported protocol");
+ }
}
private static void checkServiceInfo(NsdServiceInfo serviceInfo) {
- checkNotNull(serviceInfo, "NsdServiceInfo cannot be null");
- checkStringNotEmpty(serviceInfo.getServiceName(), "Service name cannot be empty");
- checkStringNotEmpty(serviceInfo.getServiceType(), "Service type cannot be empty");
+ Objects.requireNonNull(serviceInfo, "NsdServiceInfo cannot be null");
+ if (TextUtils.isEmpty(serviceInfo.getServiceName())) {
+ throw new IllegalArgumentException("Service name cannot be empty");
+ }
+ if (TextUtils.isEmpty(serviceInfo.getServiceType())) {
+ throw new IllegalArgumentException("Service type cannot be empty");
+ }
}
}
diff --git a/packages/Nsd/framework/src/android/net/nsd/NsdServiceInfo.java b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdServiceInfo.java
similarity index 100%
rename from packages/Nsd/framework/src/android/net/nsd/NsdServiceInfo.java
rename to packages/ConnectivityT/framework-t/src/android/net/nsd/NsdServiceInfo.java
diff --git a/packages/ConnectivityT/service/Android.bp b/packages/ConnectivityT/service/Android.bp
new file mode 100644
index 0000000..7b88176
--- /dev/null
+++ b/packages/ConnectivityT/service/Android.bp
@@ -0,0 +1,75 @@
+//
+// 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.
+//
+
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+// NetworkStats related libraries.
+
+filegroup {
+ name: "services.connectivity-netstats-sources",
+ srcs: [
+ "src/com/android/server/net/NetworkIdentity*.java",
+ "src/com/android/server/net/NetworkStats*.java",
+ ],
+ path: "src",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+// Nsd related libraries.
+
+filegroup {
+ name: "services.connectivity-nsd-sources",
+ srcs: [
+ "src/com/android/server/INativeDaemon*.java",
+ "src/com/android/server/NativeDaemon*.java",
+ "src/com/android/server/Nsd*.java",
+ ],
+ path: "src",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+// IpSec related libraries.
+
+filegroup {
+ name: "services.connectivity-ipsec-sources",
+ srcs: [
+ "src/com/android/server/IpSecService.java",
+ ],
+ path: "src",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+// Connectivity-T common libraries.
+
+filegroup {
+ name: "services.connectivity-tiramisu-sources",
+ srcs: [
+ ":services.connectivity-ipsec-sources",
+ ":services.connectivity-netstats-sources",
+ ":services.connectivity-nsd-sources",
+ ],
+ path: "src",
+ visibility: ["//frameworks/base/services/core"],
+}
diff --git a/packages/Nsd/service/src/com/android/server/INativeDaemonConnectorCallbacks.java b/packages/ConnectivityT/service/src/com/android/server/INativeDaemonConnectorCallbacks.java
similarity index 100%
rename from packages/Nsd/service/src/com/android/server/INativeDaemonConnectorCallbacks.java
rename to packages/ConnectivityT/service/src/com/android/server/INativeDaemonConnectorCallbacks.java
diff --git a/services/core/java/com/android/server/IpSecService.java b/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
similarity index 100%
rename from services/core/java/com/android/server/IpSecService.java
rename to packages/ConnectivityT/service/src/com/android/server/IpSecService.java
diff --git a/packages/Nsd/service/src/com/android/server/NativeDaemonConnector.java b/packages/ConnectivityT/service/src/com/android/server/NativeDaemonConnector.java
similarity index 100%
rename from packages/Nsd/service/src/com/android/server/NativeDaemonConnector.java
rename to packages/ConnectivityT/service/src/com/android/server/NativeDaemonConnector.java
diff --git a/packages/Nsd/service/src/com/android/server/NativeDaemonConnectorException.java b/packages/ConnectivityT/service/src/com/android/server/NativeDaemonConnectorException.java
similarity index 100%
rename from packages/Nsd/service/src/com/android/server/NativeDaemonConnectorException.java
rename to packages/ConnectivityT/service/src/com/android/server/NativeDaemonConnectorException.java
diff --git a/packages/Nsd/service/src/com/android/server/NativeDaemonEvent.java b/packages/ConnectivityT/service/src/com/android/server/NativeDaemonEvent.java
similarity index 100%
rename from packages/Nsd/service/src/com/android/server/NativeDaemonEvent.java
rename to packages/ConnectivityT/service/src/com/android/server/NativeDaemonEvent.java
diff --git a/packages/Nsd/service/src/com/android/server/NativeDaemonTimeoutException.java b/packages/ConnectivityT/service/src/com/android/server/NativeDaemonTimeoutException.java
similarity index 100%
rename from packages/Nsd/service/src/com/android/server/NativeDaemonTimeoutException.java
rename to packages/ConnectivityT/service/src/com/android/server/NativeDaemonTimeoutException.java
diff --git a/packages/Nsd/service/src/com/android/server/NsdService.java b/packages/ConnectivityT/service/src/com/android/server/NsdService.java
similarity index 100%
rename from packages/Nsd/service/src/com/android/server/NsdService.java
rename to packages/ConnectivityT/service/src/com/android/server/NsdService.java
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
similarity index 100%
rename from services/core/java/com/android/server/net/NetworkStatsFactory.java
rename to packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
diff --git a/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java
similarity index 100%
rename from services/core/java/com/android/server/net/NetworkStatsManagerInternal.java
rename to packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java
diff --git a/services/core/java/com/android/server/net/NetworkStatsObservers.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java
similarity index 98%
rename from services/core/java/com/android/server/net/NetworkStatsObservers.java
rename to packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java
index 2564dae..1a0866d 100644
--- a/services/core/java/com/android/server/net/NetworkStatsObservers.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java
@@ -22,7 +22,10 @@
import android.app.usage.NetworkStatsManager;
import android.net.DataUsageRequest;
+import android.net.NetworkIdentitySet;
import android.net.NetworkStats;
+import android.net.NetworkStatsAccess;
+import android.net.NetworkStatsCollection;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.os.Bundle;
diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
similarity index 98%
rename from services/core/java/com/android/server/net/NetworkStatsRecorder.java
rename to packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
index 978ae87..5e27c77 100644
--- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
@@ -21,8 +21,11 @@
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.text.format.DateUtils.YEAR_IN_MILLIS;
+import android.net.NetworkIdentitySet;
import android.net.NetworkStats;
import android.net.NetworkStats.NonMonotonicObserver;
+import android.net.NetworkStatsAccess;
+import android.net.NetworkStatsCollection;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
similarity index 99%
rename from services/core/java/com/android/server/net/NetworkStatsService.java
rename to packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index c876d41..2beca73 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -96,11 +96,14 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkIdentity;
+import android.net.NetworkIdentitySet;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
import android.net.NetworkStateSnapshot;
import android.net.NetworkStats;
import android.net.NetworkStats.NonMonotonicObserver;
+import android.net.NetworkStatsAccess;
+import android.net.NetworkStatsCollection;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TelephonyNetworkSpecifier;
diff --git a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
similarity index 100%
rename from services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
rename to packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
diff --git a/packages/Nsd/tests/unit/java/com/android/server/NativeDaemonConnectorTest.java b/packages/ConnectivityT/tests/unit/java/com/android/server/NativeDaemonConnectorTest.java
similarity index 100%
rename from packages/Nsd/tests/unit/java/com/android/server/NativeDaemonConnectorTest.java
rename to packages/ConnectivityT/tests/unit/java/com/android/server/NativeDaemonConnectorTest.java
diff --git a/packages/DynamicSystemInstallationService/AndroidManifest.xml b/packages/DynamicSystemInstallationService/AndroidManifest.xml
index 1bc4983..1765348 100644
--- a/packages/DynamicSystemInstallationService/AndroidManifest.xml
+++ b/packages/DynamicSystemInstallationService/AndroidManifest.xml
@@ -8,6 +8,7 @@
<uses-permission android:name="android.permission.REBOOT" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_OEM_UNLOCK_STATE" />
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:allowBackup="false"
diff --git a/packages/Nsd/OWNERS b/packages/Nsd/OWNERS
deleted file mode 100644
index 4862377..0000000
--- a/packages/Nsd/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
\ No newline at end of file
diff --git a/packages/Nsd/framework/Android.bp b/packages/Nsd/framework/Android.bp
deleted file mode 100644
index 2363a9f..0000000
--- a/packages/Nsd/framework/Android.bp
+++ /dev/null
@@ -1,54 +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.
-//
-
-package {
- // See: http://go/android-license-faq
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-filegroup {
- name: "framework-connectivity-nsd-internal-sources",
- srcs: [
- "src/**/*.java",
- "src/**/*.aidl",
- ],
- path: "src",
- visibility: [
- "//visibility:private",
- ],
-}
-
-filegroup {
- name: "framework-connectivity-nsd-aidl-export-sources",
- srcs: [
- "aidl-export/**/*.aidl",
- ],
- path: "aidl-export",
- visibility: [
- "//visibility:private",
- ],
-}
-
-filegroup {
- name: "framework-connectivity-nsd-sources",
- srcs: [
- ":framework-connectivity-nsd-internal-sources",
- ":framework-connectivity-nsd-aidl-export-sources",
- ],
- visibility: [
- "//frameworks/base",
- ],
-}
diff --git a/packages/Nsd/service/Android.bp b/packages/Nsd/service/Android.bp
deleted file mode 100644
index 529f58d..0000000
--- a/packages/Nsd/service/Android.bp
+++ /dev/null
@@ -1,31 +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.
-//
-
-package {
- // See: http://go/android-license-faq
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-filegroup {
- name: "services.connectivity-nsd-sources",
- srcs: [
- "src/**/*.java",
- ],
- path: "src",
- visibility: [
- "//frameworks/base/services/core",
- ],
-}
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index ca3ec3c..688d116 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -112,6 +112,8 @@
<!-- [CHAR LIMIT=none] -->
<string name="uninstall_application_text_user">Do you want to uninstall this app for the user <xliff:g id="username">%1$s</xliff:g>?</string>
<!-- [CHAR LIMIT=none] -->
+ <string name="uninstall_application_text_current_user_work_profile">Do you want to uninstall this app from your work profile?</string>
+ <!-- [CHAR LIMIT=none] -->
<string name="uninstall_update_text">Replace this app with the factory version? All data will be removed.</string>
<!-- [CHAR LIMIT=none] -->
<string name="uninstall_update_text_multiuser">Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles.</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
index 99f6a92..36294ac 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
@@ -31,6 +31,7 @@
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Bundle;
+import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
@@ -125,6 +126,7 @@
final boolean isUpdate =
((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+ final UserHandle myUserHandle = Process.myUserHandle();
UserManager userManager = UserManager.get(getActivity());
if (isUpdate) {
if (isSingleUser(userManager)) {
@@ -135,10 +137,17 @@
} else {
if (dialogInfo.allUsers && !isSingleUser(userManager)) {
messageBuilder.append(getString(R.string.uninstall_application_text_all_users));
- } else if (!dialogInfo.user.equals(android.os.Process.myUserHandle())) {
+ } else if (!dialogInfo.user.equals(myUserHandle)) {
UserInfo userInfo = userManager.getUserInfo(dialogInfo.user.getIdentifier());
- messageBuilder.append(
- getString(R.string.uninstall_application_text_user, userInfo.name));
+ if (userInfo.isManagedProfile()
+ && userInfo.profileGroupId == myUserHandle.getIdentifier()) {
+ messageBuilder.append(
+ getString(R.string.uninstall_application_text_current_user_work_profile,
+ userInfo.name));
+ } else {
+ messageBuilder.append(
+ getString(R.string.uninstall_application_text_user, userInfo.name));
+ }
} else {
messageBuilder.append(getString(R.string.uninstall_application_text));
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
index 21d25f5..2d241ca 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
@@ -21,7 +21,10 @@
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Bundle;
+import android.os.Process;
+import android.os.UserHandle;
import android.os.UserManager;
+
import androidx.leanback.app.GuidedStepFragment;
import androidx.leanback.widget.GuidanceStylist;
import androidx.leanback.widget.GuidedAction;
@@ -59,6 +62,7 @@
final boolean isUpdate =
((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+ final UserHandle myUserHandle = Process.myUserHandle();
UserManager userManager = UserManager.get(getActivity());
if (isUpdate) {
if (isSingleUser(userManager)) {
@@ -69,10 +73,17 @@
} else {
if (dialogInfo.allUsers && !isSingleUser(userManager)) {
messageBuilder.append(getString(R.string.uninstall_application_text_all_users));
- } else if (!dialogInfo.user.equals(android.os.Process.myUserHandle())) {
+ } else if (!dialogInfo.user.equals(myUserHandle)) {
UserInfo userInfo = userManager.getUserInfo(dialogInfo.user.getIdentifier());
- messageBuilder.append(
- getString(R.string.uninstall_application_text_user, userInfo.name));
+ if (userInfo.isManagedProfile()
+ && userInfo.profileGroupId == myUserHandle.getIdentifier()) {
+ messageBuilder.append(
+ getString(R.string.uninstall_application_text_current_user_work_profile,
+ userInfo.name));
+ } else {
+ messageBuilder.append(
+ getString(R.string.uninstall_application_text_user, userInfo.name));
+ }
} else {
messageBuilder.append(getString(R.string.uninstall_application_text));
}
diff --git a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
index 36c2bda..7f17d26 100644
--- a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
+++ b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ResolveInfo;
import com.android.settingslib.utils.BuildCompatUtils;
@@ -37,11 +36,10 @@
if (BuildCompatUtils.isAtLeastS()) {
final Intent intent = new Intent(ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY);
intent.setPackage(PACKAGE_NAME_SETTINGS);
- final ResolveInfo resolveInfo =
- context.getPackageManager().resolveActivity(intent, 0 /* flags */);
- return resolveInfo != null
- && resolveInfo.activityInfo != null
- && resolveInfo.activityInfo.enabled;
+ final boolean isEmbeddingActivityEnabled =
+ intent.resolveActivity(context.getPackageManager()) != null;
+
+ return isEmbeddingActivityEnabled;
}
return false;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index 6d576a1..63153f8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -159,7 +159,7 @@
}
public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
- @PackageManager.ResolveInfoFlags int flags, @UserIdInt int userId) {
+ @PackageManager.ResolveInfoFlagsBits int flags, @UserIdInt int userId) {
List<ResolveInfo> resolveInfos = new ArrayList<>();
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.activityInfo = new ActivityInfo();
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index dd1cb6b..e76e51d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -276,6 +276,8 @@
VALIDATORS.put(Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.EMERGENCY_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.EMERGENCY_GESTURE_SOUND_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
+ NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 262cf53..23c53a1 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -200,6 +200,7 @@
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.CREATE_USERS" />
+ <uses-permission android:name="android.permission.QUERY_USERS" />
<uses-permission android:name="android.permission.CREATE_VIRTUAL_DEVICE" />
<uses-permission android:name="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP" />
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
@@ -606,6 +607,10 @@
<!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
<uses-permission android:name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
+ <!-- Permission required for CTS test - CommunalManagerTest -->
+ <uses-permission android:name="android.permission.WRITE_COMMUNAL_STATE" />
+ <uses-permission android:name="android.permission.READ_COMMUNAL_STATE" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 5c19b41..1e9a41e 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -218,8 +218,8 @@
<!-- notifications & DND access -->
<uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS" />
- <uses-permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.GET_RUNTIME_PERMISSIONS" />
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- It's like, reality, but, you know, virtual -->
<uses-permission android:name="android.permission.ACCESS_VR_MANAGER" />
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_unlocked_aod.xml b/packages/SystemUI/res-keyguard/drawable/ic_unlocked_aod.xml
new file mode 100644
index 0000000..230a256
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ic_unlocked_aod.xml
@@ -0,0 +1,44 @@
+<?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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+ <group android:name="_R_G_L_2_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_2_G_D_0_P_0"
+ android:fillColor="#FF000000"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " />
+ </group>
+ <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_1_G_D_0_P_0"
+ android:strokeColor="#FF000000"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="1.5"
+ android:strokeAlpha="1"
+ android:pathData=" M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " />
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="14" android:translateY="13.5">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:strokeColor="#FF000000"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="1.5"
+ android:strokeAlpha="1"
+ android:pathData=" M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " />
+ </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_header_bg.xml b/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_header_bg.xml
new file mode 100644
index 0000000..177f695
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_header_bg.xml
@@ -0,0 +1,36 @@
+<?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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:paddingMode="stack"
+ android:paddingStart="44dp"
+ android:paddingEnd="44dp"
+ android:paddingLeft="0dp"
+ android:paddingRight="0dp">
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="?androidprv:attr/colorSurface" />
+ <corners android:radius="@dimen/keyguard_user_switcher_corner" />
+ </shape>
+ </item>
+ <item
+ android:drawable="@drawable/ic_ksh_key_down"
+ android:gravity="end|center_vertical"
+ android:width="32dp"
+ android:height="32dp"
+ android:end="12dp" />
+</layer-list>
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml b/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_popup_bg.xml
similarity index 74%
copy from libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
copy to packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_popup_bg.xml
index 22cd384..96a2d15 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
+++ b/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_popup_bg.xml
@@ -12,10 +12,11 @@
~ 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.
+ ~ limitations under the License
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
- <solid android:color="@color/size_compat_background"/>
- <corners android:radius="@dimen/size_compat_hint_corner_radius"/>
-</shape>
\ No newline at end of file
+ <solid android:color="?androidprv:attr/colorSurface" />
+ <corners android:radius="@dimen/keyguard_user_switcher_popup_corner" />
+</shape>
diff --git a/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml b/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
index c58e2e3..b3987f1 100644
--- a/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
+++ b/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
@@ -50,6 +50,11 @@
android:state_first="true"
android:state_single="true"
android:drawable="@drawable/ic_lock_aod" />
+ <item
+ android:id="@+id/unlocked_aod"
+ android:state_last="true"
+ android:state_single="true"
+ android:drawable="@drawable/ic_unlocked_aod" />
<item
android:id="@+id/no_icon"
@@ -79,4 +84,19 @@
android:fromId="@id/locked"
android:toId="@id/locked_aod"
android:drawable="@drawable/lock_ls_to_aod" />
+
+ <transition
+ android:fromId="@id/unlocked_aod"
+ android:toId="@id/unlocked"
+ android:drawable="@drawable/unlocked_aod_to_ls" />
+
+ <transition
+ android:fromId="@id/unlocked"
+ android:toId="@id/unlocked_aod"
+ android:drawable="@drawable/unlocked_ls_to_aod" />
+
+ <transition
+ android:fromId="@id/unlocked"
+ android:toId="@id/locked_aod"
+ android:drawable="@drawable/unlocked_to_aod_lock" />
</animated-selector>
diff --git a/packages/SystemUI/res-keyguard/drawable/unlocked_aod_to_ls.xml b/packages/SystemUI/res-keyguard/drawable/unlocked_aod_to_ls.xml
new file mode 100644
index 0000000..3b59ba8
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/unlocked_aod_to_ls.xml
@@ -0,0 +1,133 @@
+<?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.
+-->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <aapt:attr name="android:drawable">
+ <vector android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_2_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_2_G_D_0_P_0"
+ android:fillColor="#FF000000"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " />
+ </group>
+ <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_1_G_D_0_P_0"
+ android:strokeColor="#FF000000"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="1.5"
+ android:strokeAlpha="1"
+ android:pathData=" M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " />
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="14" android:translateY="13.5">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:strokeColor="#FF000000"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="1.5"
+ android:strokeAlpha="1"
+ android:pathData=" M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData" android:duration="333" android:startOffset="0" android:valueFrom="M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " android:valueTo="M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.372,0 0.203,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="strokeWidth"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="1.5"
+ android:valueTo="2"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.307,0 0.386,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " android:valueTo="M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.372,0 0.203,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="strokeWidth"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="1.5"
+ android:valueTo="2.5"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " android:valueTo="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="517"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/unlocked_ls_to_aod.xml b/packages/SystemUI/res-keyguard/drawable/unlocked_ls_to_aod.xml
new file mode 100644
index 0000000..1c6d0b5
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/unlocked_ls_to_aod.xml
@@ -0,0 +1,136 @@
+<?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.
+-->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <aapt:attr name="android:drawable">
+ <vector android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_2_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_2_G_D_0_P_0"
+ android:fillColor="#FF000000"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " />
+ </group>
+ <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_1_G_D_0_P_0"
+ android:strokeColor="#FF000000"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2"
+ android:strokeAlpha="1"
+ android:pathData=" M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " />
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="14" android:translateY="13.5">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:strokeColor="#FF000000"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2.5"
+ android:strokeAlpha="1"
+ android:pathData=" M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " android:valueTo="M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="strokeWidth"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="2"
+ android:valueTo="1.5"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.516,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " android:valueTo="M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="strokeWidth"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="2.5"
+ android:valueTo="1.5"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.516,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " android:valueTo="M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="517"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml b/packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml
new file mode 100644
index 0000000..b6d76e0
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml
@@ -0,0 +1,169 @@
+<?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.
+-->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
+ <aapt:attr name="android:drawable">
+ <vector android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_2_G_T_1" android:translateX="22.75" android:translateY="22.25" android:scaleX="1.02" android:scaleY="1.02">
+ <group android:name="_R_G_L_2_G" android:translateX="-8.75" android:translateY="-8.75">
+ <path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#FF000000"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2.5"
+ android:strokeAlpha="1"
+ android:pathData=" M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " />
+ </group>
+ </group>
+ <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_1_G_D_0_P_0"
+ android:strokeColor="#FF000000"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2"
+ android:strokeAlpha="1"
+ android:pathData=" M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " />
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#FF000000"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="strokeWidth"
+ android:duration="333" android:startOffset="0" android:valueFrom="2.5" android:valueTo="2" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0.833,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="83" android:startOffset="0" android:valueFrom="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " android:valueTo="M27.13 10.19 C27.13,10.19 27.13,3.67 27.13,3.67 C27.13,0.3 24.38,-1.75 21.13,-1.87 C17.68,-2.01 14.94,0.11 14.94,3.49 C14.94,3.49 15,15 15,15 " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.456,0 0.464,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData"
+ android:duration="133" android:startOffset="83" android:valueFrom="M27.13 10.19 C27.13,10.19 27.13,3.67 27.13,3.67 C27.13,0.3 24.38,-1.75 21.13,-1.87 C17.68,-2.01 14.94,0.11 14.94,3.49 C14.94,3.49 15,15 15,15 " android:valueTo="M2.5 10.38 C2.5,10.38 2.5,3.99 2.5,3.99 C2.5,0.61 5.3,-2.12 8.75,-2.12 C12.2,-2.12 15,0.61 15,3.99 C15,3.99 15,15 15,15 " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.606,0 0.035,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData"
+ android:duration="117" android:startOffset="217" android:valueFrom="M2.5 10.38 C2.5,10.38 2.5,3.99 2.5,3.99 C2.5,0.61 5.3,-2.12 8.75,-2.12 C12.2,-2.12 15,0.61 15,3.99 C15,3.99 15,15 15,15 " android:valueTo="M2.5 15 C2.5,15 2.5,8.61 2.5,8.61 C2.5,5.24 5.3,2.5 8.75,2.5 C12.2,2.5 15,5.24 15,8.61 C15,8.61 15,15 15,15 " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.511,0 0.409,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="333" android:startOffset="0" android:valueFrom="22.75" android:valueTo="23" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateY" android:duration="333" android:startOffset="0" android:valueFrom="22.25" android:valueTo="25.5" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX"
+ android:duration="333" android:startOffset="0" android:valueFrom="1.02" android:valueTo="0.72" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY"
+ android:duration="333" android:startOffset="0" android:valueFrom="1.02" android:valueTo="0.72" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="strokeWidth"
+ android:duration="333" android:startOffset="0" android:valueFrom="2" android:valueTo="1.5" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.38,0 0.274,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333" android:startOffset="0" android:valueFrom="M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " android:valueTo="M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.431,0 0.133,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333" android:startOffset="0" android:valueFrom="M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " android:valueTo="M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.431,0 0.133,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="850" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
new file mode 100644
index 0000000..4f0925f
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_bouncer_user_switcher"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:importantForAccessibility="yes"> <!-- Needed because TYPE_WINDOW_STATE_CHANGED is sent
+ from this view when bouncer is shown -->
+
+ <ImageView
+ android:id="@+id/user_icon"
+ android:layout_width="@dimen/keyguard_user_switcher_icon_size"
+ android:layout_height="@dimen/keyguard_user_switcher_icon_size" />
+
+ <!-- need to keep this outer view in order to have a correctly sized anchor
+ for the dropdown menu, as well as dropdown background in the right place -->
+ <LinearLayout
+ android:id="@+id/user_switcher_anchor"
+ android:orientation="horizontal"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginTop="30dp"
+ android:minHeight="48dp">
+ <TextView
+ style="@style/Keyguard.UserSwitcher.Spinner.Header"
+ android:clickable="false"
+ android:id="@+id/user_switcher_header"
+ android:layout_width="@dimen/keyguard_user_switcher_width"
+ android:layout_height="wrap_content" />
+ </LinearLayout>>
+
+</LinearLayout>
+
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher_item.xml
similarity index 60%
copy from libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
copy to packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher_item.xml
index 22cd384..b08e1ff 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher_item.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2021 The Android Open Source Project
~
@@ -14,8 +13,13 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="@color/size_compat_background"/>
- <corners android:radius="@dimen/size_compat_hint_corner_radius"/>
-</shape>
\ No newline at end of file
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/Keyguard.UserSwitcher.Spinner.Item"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:paddingStart="@dimen/control_menu_horizontal_padding"
+ android:paddingEnd="@dimen/control_menu_horizontal_padding"
+ android:textDirection="locale"/>
+
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index a946318..94566c7 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -49,8 +49,6 @@
android:paddingBottom="@dimen/num_pad_entry_row_margin_bottom"
androidprv:layout_constraintEnd_toEndOf="parent"
androidprv:layout_constraintStart_toStartOf="parent"
-
- androidprv:layout_constraintTop_toTopOf="parent"
androidprv:layout_constraintBottom_toTopOf="@id/key1"
androidprv:layout_constraintVertical_bias="0.0">
diff --git a/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml b/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml
index 4daa648..54bb1fc 100644
--- a/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml
+++ b/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml
@@ -18,7 +18,7 @@
<resources>
<!-- Allows PIN/Pattern to be drawn on one side of a display, and for the user to
switch sides -->
- <bool name="can_use_one_handed_bouncer">true</bool>
+ <bool name="can_use_one_handed_bouncer">false</bool>
<!-- Will display the bouncer on one side of the display, and the current user icon and
user switcher on the other side -->
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 89dd741..2819dc9 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -106,4 +106,13 @@
opacity is zero), but this controls how much motion will actually be applied to it while
animating. Larger values will cause it to move "faster" while fading out/in. -->
<dimen name="one_handed_bouncer_move_animation_translation">120dp</dimen>
+
+
+ <dimen name="keyguard_user_switcher_header_text_size">32sp</dimen>
+ <dimen name="keyguard_user_switcher_item_text_size">32sp</dimen>
+ <dimen name="keyguard_user_switcher_width">320dp</dimen>
+ <dimen name="keyguard_user_switcher_icon_size">310dp</dimen>
+ <dimen name="keyguard_user_switcher_corner">32dp</dimen>
+ <dimen name="keyguard_user_switcher_popup_corner">24dp</dimen>
+ <dimen name="keyguard_user_switcher_item_padding_vertical">15dp</dimen>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index b0bdc72..a7b2b47 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -139,4 +139,23 @@
<style name="TextAppearance.Keyguard.BottomArea.Button">
<item name="android:shadowRadius">0</item>
</style>
+
+ <style name="Keyguard.UserSwitcher.Spinner" parent="@android:style/Widget.DeviceDefault.TextView">
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:ellipsize">end</item>
+ <item name="android:paddingTop">@dimen/keyguard_user_switcher_item_padding_vertical</item>
+ <item name="android:paddingBottom">@dimen/keyguard_user_switcher_item_padding_vertical</item>
+ </style>
+
+ <style name="Keyguard.UserSwitcher.Spinner.Header">
+ <item name="android:background">@drawable/keyguard_user_switcher_header_bg</item>
+ <item name="android:textSize">@dimen/keyguard_user_switcher_header_text_size</item>
+ </style>
+
+ <style name="Keyguard.UserSwitcher.Spinner.Item">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textSize">@dimen/keyguard_user_switcher_item_text_size</item>
+ </style>
</resources>
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml b/packages/SystemUI/res/drawable/ic_signal_wifi_off.xml
similarity index 62%
copy from libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
copy to packages/SystemUI/res/drawable/ic_signal_wifi_off.xml
index af9063a..a93abb7 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_wifi_off.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2021 The Android Open Source Project
~
@@ -15,11 +14,11 @@
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/size_compat_hint_point_width"
- android:height="8dp"
- android:viewportWidth="10"
- android:viewportHeight="8">
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
<path
- android:fillColor="@color/size_compat_background"
- android:pathData="M10,0 l-4.1875,6.6875 a1,1 0 0,1 -1.625,0 l-4.1875,-6.6875z"/>
-</vector>
+ android:fillColor="@android:color/white"
+ android:pathData="M18.575,15.2L7.55,4.175q1.1,-0.325 2.212,-0.462 1.113,-0.138 2.238,-0.138 3.45,0 6.55,1.45Q21.65,6.475 24,9zM20.225,23.575l-4.75,-4.75 -3.475,4.1L0,9q0.675,-0.725 1.438,-1.4 0.762,-0.675 1.612,-1.225l-2.625,-2.6L2.1,2.1l19.8,19.8z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml
index 3c9e44e..89690e8 100644
--- a/packages/SystemUI/res/layout/auth_biometric_contents.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml
@@ -73,6 +73,9 @@
android:accessibilityLiveRegion="polite"
android:singleLine="true"
android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:scrollHorizontally="true"
+ android:fadingEdge="horizontal"
android:textColor="@color/biometric_dialog_gray"/>
<LinearLayout
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
index 46cbc25..1e0ce00 100644
--- a/packages/SystemUI/res/layout/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -18,71 +18,64 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:elevation="@dimen/biometric_dialog_elevation"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:gravity="center_horizontal"
+ android:elevation="@dimen/biometric_dialog_elevation">
- <RelativeLayout
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:id="@+id/title"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.AuthCredential.Title"/>
- <LinearLayout
- android:id="@+id/auth_credential_header"
- style="@style/AuthCredentialHeaderStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true">
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.AuthCredential.Subtitle"/>
- <ImageView
- android:id="@+id/icon"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:contentDescription="@null" />
+ <TextView
+ android:id="@+id/description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.AuthCredential.Description"/>
- <TextView
- android:id="@+id/title"
- style="@style/TextAppearance.AuthCredential.Title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
- <TextView
- android:id="@+id/subtitle"
- style="@style/TextAppearance.AuthCredential.Subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ <ImeAwareEditText
+ android:id="@+id/lockPassword"
+ android:layout_width="208dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:minHeight="48dp"
+ android:gravity="center"
+ android:inputType="textPassword"
+ android:maxLength="500"
+ android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+ style="@style/TextAppearance.AuthCredential.PasswordEntry"/>
- <TextView
- android:id="@+id/description"
- style="@style/TextAppearance.AuthCredential.Description"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/error"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.AuthCredential.Error"/>
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center"
- android:orientation="vertical"
- android:layout_alignParentBottom="true">
-
- <ImeAwareEditText
- android:id="@+id/lockPassword"
- style="@style/TextAppearance.AuthCredential.PasswordEntry"
- android:layout_width="208dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
- android:inputType="textPassword"
- android:minHeight="48dp" />
-
- <TextView
- android:id="@+id/error"
- style="@style/TextAppearance.AuthCredential.Error"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- </LinearLayout>
-
- </RelativeLayout>
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="5"/>
</com.android.systemui.biometrics.AuthCredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
index 470298e..4939ea2 100644
--- a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
@@ -22,81 +22,76 @@
android:gravity="center_horizontal"
android:elevation="@dimen/biometric_dialog_elevation">
- <RelativeLayout
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:id="@+id/title"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.AuthCredential.Title"/>
- <LinearLayout
- android:id="@+id/auth_credential_header"
- style="@style/AuthCredentialHeaderStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.AuthCredential.Subtitle"/>
- <ImageView
- android:id="@+id/icon"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:contentDescription="@null" />
+ <TextView
+ android:id="@+id/description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.AuthCredential.Description"/>
- <TextView
- android:id="@+id/title"
- style="@style/TextAppearance.AuthCredential.Title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
- <TextView
- android:id="@+id/subtitle"
- style="@style/TextAppearance.AuthCredential.Subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:paddingLeft="0dp"
+ android:paddingRight="0dp"
+ android:paddingTop="0dp"
+ android:paddingBottom="16dp"
+ android:clipToPadding="false">
- <TextView
- android:id="@+id/description"
- style="@style/TextAppearance.AuthCredential.Description"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </LinearLayout>
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ style="@style/LockPatternContainerStyle">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/auth_credential_header"
- android:gravity="center"
- android:orientation="vertical"
- android:paddingBottom="16dp"
- android:paddingTop="60dp">
-
- <FrameLayout
- style="@style/LockPatternContainerStyle"
- android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_weight="1">
-
- <com.android.internal.widget.LockPatternView
- android:id="@+id/lockPattern"
- style="@style/LockPatternStyle"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center" />
-
- </FrameLayout>
-
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true">
-
- <TextView
- android:id="@+id/error"
- style="@style/TextAppearance.AuthCredential.Error"
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPattern"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ style="@style/LockPatternStyleBiometricPrompt"/>
- </LinearLayout>
+ </FrameLayout>
- </RelativeLayout>
+ <TextView
+ android:id="@+id/error"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.AuthCredential.Error"/>
+
+ </LinearLayout>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
</com.android.systemui.biometrics.AuthCredentialPatternView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
new file mode 100644
index 0000000..b611ffa
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -0,0 +1,61 @@
+<?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.
+-->
+<com.android.systemui.dreams.DreamOverlayContainerView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/dream_overlay_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/dream_overlay_content"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent" />
+
+ <com.android.systemui.dreams.DreamOverlayStatusBarView
+ android:id="@+id/dream_overlay_status_bar"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dream_overlay_status_bar_height"
+ android:layout_marginEnd="@dimen/dream_overlay_status_bar_margin"
+ android:layout_marginStart="@dimen/dream_overlay_status_bar_margin"
+ app:layout_constraintTop_toTopOf="parent">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/dream_overlay_system_status"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ app:layout_constraintEnd_toEndOf="parent">
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/dream_overlay_wifi_status"
+ android:layout_width="@dimen/status_bar_wifi_signal_size"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+ android:visibility="gone"
+ app:layout_constraintEnd_toStartOf="@id/dream_overlay_battery" />
+
+ <com.android.systemui.battery.BatteryMeterView
+ android:id="@+id/dream_overlay_battery"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ app:layout_constraintEnd_toEndOf="parent" />
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+ </com.android.systemui.dreams.DreamOverlayStatusBarView>
+</com.android.systemui.dreams.DreamOverlayContainerView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 75fa66b..275e0a5 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -37,7 +37,7 @@
android:ellipsize="end"
android:gravity="center_vertical|center_horizontal"
android:layout_width="wrap_content"
- android:layout_height="32dp"
+ android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.InternetDialog"
android:textSize="24sp"/>
@@ -45,7 +45,7 @@
android:id="@+id/internet_dialog_subtitle"
android:gravity="center_vertical|center_horizontal"
android:layout_width="wrap_content"
- android:layout_height="20dp"
+ android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:maxLines="1"
@@ -150,6 +150,7 @@
android:gravity="start|center_vertical">
<TextView
android:id="@+id/mobile_title"
+ android:maxLines="1"
style="@style/InternetDialog.NetworkTitle"/>
<TextView
android:id="@+id/mobile_summary"
diff --git a/packages/SystemUI/res/layout/status_bar_no_notifications.xml b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
index 332dc6ee..a2abdb2 100644
--- a/packages/SystemUI/res/layout/status_bar_no_notifications.xml
+++ b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
@@ -26,8 +26,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="64dp"
- android:paddingTop="12dp"
android:textAppearance="?android:attr/textAppearanceButton"
- android:gravity="top|center_horizontal"
+ android:gravity="center"
android:text="@string/empty_shade_text"/>
</com.android.systemui.statusbar.EmptyShadeView>
diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml
index f057603..1d6f279 100644
--- a/packages/SystemUI/res/values-h800dp/dimens.xml
+++ b/packages/SystemUI/res/values-h800dp/dimens.xml
@@ -22,5 +22,5 @@
<dimen name="large_clock_text_size">200dp</dimen>
<!-- With the large clock, move up slightly from the center -->
- <dimen name="keyguard_large_clock_top_margin">-104dp</dimen>
+ <dimen name="keyguard_large_clock_top_margin">-112dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 9d4c2c3..3412722 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -78,7 +78,7 @@
<color name="biometric_dialog_error">#fff28b82</color> <!-- red 300 -->
<!-- UDFPS colors -->
- <color name="udfps_enroll_icon">#ffffff</color> <!-- 100% white -->
+ <color name="udfps_enroll_icon">#7DA7F1</color>
<color name="GM2_green_500">#FF41Af6A</color>
<color name="GM2_blue_500">#5195EA</color>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 8cad5b3..fc28f09 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -134,10 +134,10 @@
<color name="biometric_dialog_error">#ffd93025</color> <!-- red 600 -->
<!-- UDFPS colors -->
- <color name="udfps_enroll_icon">#000000</color> <!-- 100% black -->
- <color name="udfps_moving_target_fill">#cc4285f4</color> <!-- 80% blue -->
- <color name="udfps_enroll_progress">#ff669DF6</color> <!-- blue 400 -->
- <color name="udfps_enroll_progress_help">#ffEE675C</color> <!-- red 400 -->
+ <color name="udfps_enroll_icon">#7DA7F1</color>
+ <color name="udfps_moving_target_fill">#475670</color>
+ <color name="udfps_enroll_progress">#7DA7F1</color>
+ <color name="udfps_enroll_progress_help">#ffEE675C</color>
<!-- Global screenshot actions -->
<color name="screenshot_button_ripple">#1f000000</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index df8fc22..3695286 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -598,17 +598,6 @@
280
</integer>
- <!-- Haptic feedback intensity for ticks used for the udfps dwell time -->
- <item name="config_udfpsTickIntensity" translatable="false" format="float"
- type="dimen">.5</item>
-
- <!-- Haptic feedback delay between ticks used for udfps dwell time -->
- <integer name="config_udfpsTickDelay" translatable="false">25</integer>
-
- <!-- Haptic feedback tick type - if true, uses VibrationEffect.Composition.PRIMITIVE_LOW_TICK
- else uses VibrationEffect.Composition.PRIMITIVE_TICK -->
- <bool name="config_udfpsUseLowTick">true</bool>
-
<!-- package name of a built-in camera app to use to restrict implicit intent resolution
when the double-press power gesture is used. Ignored if empty. -->
<string translatable="false" name="config_cameraGesturePackage"></string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c7350a1..d4398a8 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -30,10 +30,10 @@
<dimen name="navigation_bar_deadzone_size_max">32dp</dimen>
<!-- dimensions for the navigation bar handle -->
- <dimen name="navigation_handle_radius">1dp</dimen>
- <dimen name="navigation_handle_bottom">6dp</dimen>
+ <dimen name="navigation_handle_radius">2dp</dimen>
+ <dimen name="navigation_handle_bottom">10dp</dimen>
<dimen name="navigation_handle_sample_horizontal_margin">10dp</dimen>
- <dimen name="navigation_home_handle_width">72dp</dimen>
+ <dimen name="navigation_home_handle_width">108dp</dimen>
<!-- Size of the nav bar edge panels, should be greater to the
edge sensitivity + the drag threshold -->
@@ -602,7 +602,7 @@
<!-- When large clock is showing, offset the smartspace by this amount -->
<dimen name="keyguard_smartspace_top_offset">12dp</dimen>
<!-- With the large clock, move up slightly from the center -->
- <dimen name="keyguard_large_clock_top_margin">-52dp</dimen>
+ <dimen name="keyguard_large_clock_top_margin">-60dp</dimen>
<!-- Default line spacing multiplier between hours and minutes of the keyguard clock -->
<item name="keyguard_clock_line_spacing_scale" type="dimen" format="float">.7</item>
@@ -1303,4 +1303,9 @@
<dimen name="keyguard_unfold_translation_x">16dp</dimen>
<dimen name="fgs_manager_min_width_minor">100%</dimen>
+
+ <!-- Dream overlay related dimensions -->
+ <dimen name="dream_overlay_status_bar_height">80dp</dimen>
+ <dimen name="dream_overlay_status_bar_margin">40dp</dimen>
+ <dimen name="dream_overlay_status_icon_margin">8dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 01f0c12..9358349 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -200,9 +200,9 @@
<style name="TextAppearance.DeviceManagementDialog.Title" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/>
- <style name="TextAppearance.AuthCredential"
- parent="@android:style/TextAppearance.DeviceDefault">
+ <style name="TextAppearance.AuthCredential">
<item name="android:accessibilityLiveRegion">polite</item>
+ <item name="android:gravity">center_horizontal</item>
<item name="android:textAlignment">gravity</item>
<item name="android:layout_gravity">top</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
@@ -210,57 +210,44 @@
<style name="TextAppearance.AuthCredential.Title">
<item name="android:fontFamily">google-sans</item>
- <item name="android:layout_marginTop">20dp</item>
- <item name="android:textSize">36sp</item>
+ <item name="android:paddingTop">12dp</item>
+ <item name="android:paddingHorizontal">24dp</item>
+ <item name="android:textSize">24sp</item>
</style>
<style name="TextAppearance.AuthCredential.Subtitle">
<item name="android:fontFamily">google-sans</item>
- <item name="android:layout_marginTop">20dp</item>
- <item name="android:textSize">18sp</item>
+ <item name="android:paddingTop">8dp</item>
+ <item name="android:paddingHorizontal">24dp</item>
+ <item name="android:textSize">16sp</item>
</style>
<style name="TextAppearance.AuthCredential.Description">
<item name="android:fontFamily">google-sans</item>
- <item name="android:layout_marginTop">20dp</item>
- <item name="android:textSize">16sp</item>
+ <item name="android:paddingTop">8dp</item>
+ <item name="android:paddingHorizontal">24dp</item>
+ <item name="android:textSize">14sp</item>
</style>
<style name="TextAppearance.AuthCredential.Error">
<item name="android:paddingTop">6dp</item>
- <item name="android:paddingBottom">18dp</item>
<item name="android:paddingHorizontal">24dp</item>
<item name="android:textSize">14sp</item>
<item name="android:textColor">?android:attr/colorError</item>
- <item name="android:gravity">center</item>
</style>
- <style name="TextAppearance.AuthCredential.PasswordEntry">
+ <style name="TextAppearance.AuthCredential.PasswordEntry" parent="@android:style/TextAppearance.DeviceDefault">
<item name="android:gravity">center</item>
<item name="android:singleLine">true</item>
<item name="android:textColor">?android:attr/colorForeground</item>
<item name="android:textSize">24sp</item>
</style>
- <style name="AuthCredentialHeaderStyle">
- <item name="android:paddingStart">48dp</item>
- <item name="android:paddingEnd">24dp</item>
- <item name="android:paddingTop">28dp</item>
- <item name="android:paddingBottom">20dp</item>
- <item name="android:orientation">vertical</item>
- <item name="android:layout_gravity">top</item>
- </style>
-
<style name="DeviceManagementDialogTitle">
<item name="android:gravity">center</item>
<item name="android:textAppearance">@style/TextAppearance.DeviceManagementDialog.Title</item>
</style>
- <style name="AuthCredentialPasswordTheme" parent="@style/Theme.MaterialComponents.DayNight">
- <item name="colorPrimary">?android:attr/colorPrimary</item>
- <item name="colorPrimaryDark">?android:attr/colorPrimary</item>
- </style>
-
<style name="TextAppearance.DeviceManagementDialog.Content" parent="@*android:style/TextAppearance.DeviceDefault.Subhead"/>
<style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI">
@@ -320,8 +307,9 @@
<item name="android:maxWidth">420dp</item>
<item name="android:minHeight">0dp</item>
<item name="android:minWidth">0dp</item>
- <item name="android:paddingHorizontal">60dp</item>
- <item name="android:paddingBottom">40dp</item>
+ <item name="android:paddingBottom">0dp</item>
+ <item name="android:paddingHorizontal">44dp</item>
+ <item name="android:paddingTop">0dp</item>
</style>
<style name="LockPatternStyle">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagListenable.kt
similarity index 61%
rename from packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
rename to packages/SystemUI/shared/src/com/android/systemui/flags/FlagListenable.kt
index 26c6a4b..195ba465 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagListenable.kt
@@ -18,28 +18,24 @@
/**
* Plugin for loading flag values
*/
-interface FlagReader {
- /** Returns a boolean value for the given flag. */
- fun isEnabled(flag: BooleanFlag): Boolean {
- return flag.default
- }
-
- fun isEnabled(flag: ResourceBooleanFlag): Boolean
-
- /** Returns a boolean value for the given flag. */
- fun isEnabled(id: Int, def: Boolean): Boolean {
- return def
- }
-
- /** Add a listener to be alerted when any flag changes. */
- fun addListener(listener: Listener) {}
+interface FlagListenable {
+ /** Add a listener to be alerted when the given flag changes. */
+ fun addListener(flag: Flag<*>, listener: Listener)
/** Remove a listener to be alerted when any flag changes. */
- fun removeListener(listener: Listener) {}
+ fun removeListener(listener: Listener)
/** A simple listener to be alerted when a flag changes. */
fun interface Listener {
- /** */
- fun onFlagChanged(id: Int)
+ /** Called when the flag changes */
+ fun onFlagChanged(event: FlagEvent)
+ }
+
+ /** An event representing the change */
+ interface FlagEvent {
+ /** the id of the flag which changed */
+ val flagId: Int
+ /** if all listeners alerted invoke this method, the restart will be skipped */
+ fun requestNoRestart()
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
index b2ca2d7..ec619dd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
@@ -24,30 +24,39 @@
import android.net.Uri
import android.os.Bundle
import android.os.Handler
-import android.provider.Settings
import androidx.concurrent.futures.CallbackToFutureAdapter
import com.google.common.util.concurrent.ListenableFuture
-import org.json.JSONException
-import org.json.JSONObject
+import java.util.function.Consumer
class FlagManager constructor(
private val context: Context,
+ private val settings: FlagSettingsHelper,
private val handler: Handler
-) : FlagReader {
+) : FlagListenable {
companion object {
const val RECEIVING_PACKAGE = "com.android.systemui"
const val ACTION_SET_FLAG = "com.android.systemui.action.SET_FLAG"
const val ACTION_GET_FLAGS = "com.android.systemui.action.GET_FLAGS"
const val FLAGS_PERMISSION = "com.android.systemui.permission.FLAGS"
- const val FIELD_ID = "id"
- const val FIELD_VALUE = "value"
- const val FIELD_TYPE = "type"
- const val FIELD_FLAGS = "flags"
- const val TYPE_BOOLEAN = "boolean"
+ const val EXTRA_ID = "id"
+ const val EXTRA_VALUE = "value"
+ const val EXTRA_FLAGS = "flags"
private const val SETTINGS_PREFIX = "systemui/flags"
}
- private val listeners: MutableSet<FlagReader.Listener> = mutableSetOf()
+ constructor(context: Context, handler: Handler) : this(
+ context,
+ FlagSettingsHelper(context.contentResolver),
+ handler
+ )
+
+ /**
+ * An action called on restart which takes as an argument whether the listeners requested
+ * that the restart be suppressed
+ */
+ var restartAction: Consumer<Boolean>? = null
+ var clearCacheAction: Consumer<Int>? = null
+ private val listeners: MutableSet<PerFlagListener> = mutableSetOf()
private val settingsObserver: ContentObserver = SettingsObserver()
fun getFlagsFuture(): ListenableFuture<Collection<Flag<*>>> {
@@ -61,7 +70,7 @@
override fun onReceive(context: Context, intent: Intent) {
val extras: Bundle? = getResultExtras(false)
val listOfFlags: java.util.ArrayList<ParcelableFlag<*>>? =
- extras?.getParcelableArrayList(FIELD_FLAGS)
+ extras?.getParcelableArrayList(EXTRA_FLAGS)
if (listOfFlags != null) {
completer.set(listOfFlags)
} else {
@@ -73,9 +82,19 @@
} as ListenableFuture<Collection<Flag<*>>>
}
+ /**
+ * Returns the stored value or null if not set.
+ * This API is used by TheFlippinApp.
+ */
+ fun isEnabled(id: Int): Boolean? = readFlagValue(id, BooleanFlagSerializer)
+
+ /**
+ * Sets the value of a boolean flag.
+ * This API is used by TheFlippinApp.
+ */
fun setFlagValue(id: Int, enabled: Boolean) {
val intent = createIntent(id)
- intent.putExtra(FIELD_VALUE, enabled)
+ intent.putExtra(EXTRA_VALUE, enabled)
context.sendBroadcast(intent)
}
@@ -86,49 +105,30 @@
context.sendBroadcast(intent)
}
- override fun isEnabled(id: Int, def: Boolean): Boolean {
- return isEnabled(id) ?: def
- }
-
/** Returns the stored value or null if not set. */
- fun isEnabled(id: Int): Boolean? {
- val data: String? = Settings.Secure.getString(
- context.contentResolver, keyToSettingsPrefix(id))
- if (data == null || data?.isEmpty()) {
- return null
- }
- val json: JSONObject
- try {
- json = JSONObject(data)
- return if (!assertType(json, TYPE_BOOLEAN)) {
- null
- } else json.getBoolean(FIELD_VALUE)
- } catch (e: JSONException) {
- throw InvalidFlagStorageException()
- }
+ fun <T> readFlagValue(id: Int, serializer: FlagSerializer<T>): T? {
+ val data = settings.getString(idToSettingsKey(id))
+ return serializer.fromSettingsData(data)
}
- override fun isEnabled(flag: ResourceBooleanFlag): Boolean {
- throw RuntimeException("Not implemented in FlagManager")
- }
-
- override fun addListener(listener: FlagReader.Listener) {
+ override fun addListener(flag: Flag<*>, listener: FlagListenable.Listener) {
synchronized(listeners) {
val registerNeeded = listeners.isEmpty()
- listeners.add(listener)
+ listeners.add(PerFlagListener(flag.id, listener))
if (registerNeeded) {
- context.contentResolver.registerContentObserver(
- Settings.Secure.getUriFor(SETTINGS_PREFIX), true, settingsObserver)
+ settings.registerContentObserver(SETTINGS_PREFIX, true, settingsObserver)
}
}
}
- override fun removeListener(listener: FlagReader.Listener) {
+ override fun removeListener(listener: FlagListenable.Listener) {
synchronized(listeners) {
- val isRegistered = !listeners.isEmpty()
- listeners.remove(listener)
- if (isRegistered && listeners.isEmpty()) {
- context.contentResolver.unregisterContentObserver(settingsObserver)
+ if (listeners.isEmpty()) {
+ return
+ }
+ listeners.removeIf { it.listener == listener }
+ if (listeners.isEmpty()) {
+ settings.unregisterContentObserver(settingsObserver)
}
}
}
@@ -136,21 +136,13 @@
private fun createIntent(id: Int): Intent {
val intent = Intent(ACTION_SET_FLAG)
intent.setPackage(RECEIVING_PACKAGE)
- intent.putExtra(FIELD_ID, id)
+ intent.putExtra(EXTRA_ID, id)
return intent
}
- fun keyToSettingsPrefix(key: Int): String {
- return SETTINGS_PREFIX + "/" + key
- }
-
- private fun assertType(json: JSONObject, type: String): Boolean {
- return try {
- json.getString(FIELD_TYPE) == TYPE_BOOLEAN
- } catch (e: JSONException) {
- false
- }
+ fun idToSettingsKey(id: Int): String {
+ return "$SETTINGS_PREFIX/$id"
}
inner class SettingsObserver : ContentObserver(handler) {
@@ -160,17 +152,40 @@
}
val parts = uri.pathSegments
val idStr = parts[parts.size - 1]
- try {
- val id = idStr.toInt()
- listeners.forEach { l -> l.onFlagChanged(id) }
- } catch (e: NumberFormatException) {
- // no-op
- }
+ val id = try { idStr.toInt() } catch (e: NumberFormatException) { return }
+ clearCacheAction?.accept(id)
+ dispatchListenersAndMaybeRestart(id)
}
}
-}
-class InvalidFlagStorageException : Exception("Data found but is invalid")
+ fun dispatchListenersAndMaybeRestart(id: Int) {
+ val filteredListeners: List<FlagListenable.Listener> = synchronized(listeners) {
+ listeners.mapNotNull { if (it.id == id) it.listener else null }
+ }
+ // If there are no listeners, there's nothing to dispatch to, and nothing to suppress it.
+ if (filteredListeners.isEmpty()) {
+ restartAction?.accept(false)
+ return
+ }
+ // Dispatch to every listener and save whether each one called requestNoRestart.
+ val suppressRestartList: List<Boolean> = filteredListeners.map { listener ->
+ var didRequestNoRestart = false
+ val event = object : FlagListenable.FlagEvent {
+ override val flagId = id
+ override fun requestNoRestart() {
+ didRequestNoRestart = true
+ }
+ }
+ listener.onFlagChanged(event)
+ didRequestNoRestart
+ }
+ // Suppress restart only if ALL listeners request it.
+ val suppressRestart = suppressRestartList.all { it }
+ restartAction?.accept(suppressRestart)
+ }
+
+ private data class PerFlagListener(val id: Int, val listener: FlagListenable.Listener)
+}
class NoFlagResultsException : Exception(
"SystemUI failed to communicate its flags back successfully")
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSerializer.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSerializer.kt
new file mode 100644
index 0000000..e9ea19d
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSerializer.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.flags
+
+import android.util.Log
+import org.json.JSONException
+import org.json.JSONObject
+
+private const val FIELD_VALUE = "value"
+private const val FIELD_TYPE = "type"
+private const val TYPE_BOOLEAN = "boolean"
+private const val TYPE_STRING = "string"
+
+private const val TAG = "FlagSerializer"
+
+abstract class FlagSerializer<T>(
+ private val type: String,
+ private val setter: (JSONObject, String, T) -> Unit,
+ private val getter: (JSONObject, String) -> T
+) {
+ fun toSettingsData(value: T): String? {
+ return try {
+ JSONObject()
+ .put(FIELD_TYPE, type)
+ .also { setter(it, FIELD_VALUE, value) }
+ .toString()
+ } catch (e: JSONException) {
+ Log.w(TAG, "write error", e)
+ null
+ }
+ }
+
+ /**
+ * @throws InvalidFlagStorageException
+ */
+ fun fromSettingsData(data: String?): T? {
+ if (data == null || data.isEmpty()) {
+ return null
+ }
+ try {
+ val json = JSONObject(data)
+ return if (json.getString(FIELD_TYPE) == type) {
+ getter(json, FIELD_VALUE)
+ } else {
+ null
+ }
+ } catch (e: JSONException) {
+ Log.w(TAG, "read error", e)
+ throw InvalidFlagStorageException()
+ }
+ }
+}
+
+object BooleanFlagSerializer : FlagSerializer<Boolean>(
+ TYPE_BOOLEAN,
+ JSONObject::put,
+ JSONObject::getBoolean
+)
+
+object StringFlagSerializer : FlagSerializer<String>(
+ TYPE_STRING,
+ JSONObject::put,
+ JSONObject::getString
+)
+
+class InvalidFlagStorageException : Exception("Data found but is invalid")
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSettingsHelper.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSettingsHelper.kt
new file mode 100644
index 0000000..742bb0b
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSettingsHelper.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.flags
+
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.provider.Settings
+
+class FlagSettingsHelper(private val contentResolver: ContentResolver) {
+
+ fun getString(key: String): String? = Settings.Secure.getString(contentResolver, key)
+
+ fun registerContentObserver(
+ name: String,
+ notifyForDescendants: Boolean,
+ observer: ContentObserver
+ ) {
+ contentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(name),
+ notifyForDescendants,
+ observer
+ )
+ }
+
+ fun unregisterContentObserver(observer: ContentObserver) {
+ contentResolver.unregisterContentObserver(observer)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
index 7d0fb5d..567e7aa 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -53,8 +53,8 @@
tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
.setPosition(leash, positionX, positionY)
.setCornerRadius(leash, cornerRadius);
- return new PictureInPictureSurfaceTransaction(
- positionX, positionY, mTmpFloat9, 0 /* rotation */, cornerRadius, sourceBounds);
+ return newPipSurfaceTransaction(positionX, positionY,
+ mTmpFloat9, 0 /* rotation */, cornerRadius, sourceBounds);
}
public PictureInPictureSurfaceTransaction scale(
@@ -70,8 +70,8 @@
tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
.setPosition(leash, positionX, positionY)
.setCornerRadius(leash, cornerRadius);
- return new PictureInPictureSurfaceTransaction(
- positionX, positionY, mTmpFloat9, degree, cornerRadius, sourceBounds);
+ return newPipSurfaceTransaction(positionX, positionY,
+ mTmpFloat9, degree, cornerRadius, sourceBounds);
}
public PictureInPictureSurfaceTransaction scaleAndCrop(
@@ -93,8 +93,8 @@
.setWindowCrop(leash, mTmpDestinationRect)
.setPosition(leash, left, top)
.setCornerRadius(leash, cornerRadius);
- return new PictureInPictureSurfaceTransaction(
- left, top, mTmpFloat9, 0 /* rotation */, cornerRadius, mTmpDestinationRect);
+ return newPipSurfaceTransaction(left, top,
+ mTmpFloat9, 0 /* rotation */, cornerRadius, mTmpDestinationRect);
}
public PictureInPictureSurfaceTransaction scaleAndRotate(
@@ -125,8 +125,7 @@
.setWindowCrop(leash, mTmpDestinationRect)
.setPosition(leash, adjustedPositionX, adjustedPositionY)
.setCornerRadius(leash, cornerRadius);
- return new PictureInPictureSurfaceTransaction(
- adjustedPositionX, adjustedPositionY,
+ return newPipSurfaceTransaction(adjustedPositionX, adjustedPositionY,
mTmpFloat9, degree, cornerRadius, mTmpDestinationRect);
}
@@ -137,6 +136,17 @@
return mCornerRadius * scale;
}
+ private static PictureInPictureSurfaceTransaction newPipSurfaceTransaction(
+ float posX, float posY, float[] float9, float rotation, float cornerRadius,
+ Rect windowCrop) {
+ return new PictureInPictureSurfaceTransaction.Builder()
+ .setPosition(posX, posY)
+ .setTransform(float9, rotation)
+ .setCornerRadius(cornerRadius)
+ .setWindowCrop(windowCrop)
+ .build();
+ }
+
/** @return {@link SurfaceControl.Transaction} instance with vsync-id */
public static SurfaceControl.Transaction newSurfaceControlTransaction() {
final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PackageManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PackageManagerWrapper.java
index 443c1e1..5c37ecce 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PackageManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PackageManagerWrapper.java
@@ -22,7 +22,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.PackageManager.ResolveInfoFlagsBits;
import android.content.pm.ResolveInfo;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -73,7 +73,7 @@
/**
* Determine the best Activity to perform for a given Intent.
*/
- public ResolveInfo resolveActivity(Intent intent, @ResolveInfoFlags int flags) {
+ public ResolveInfo resolveActivity(Intent intent, @ResolveInfoFlagsBits int flags) {
final String resolvedType =
intent.resolveTypeIfNeeded(AppGlobals.getInitialApplication().getContentResolver());
try {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 30db136..c1d9d0d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -65,6 +65,7 @@
public final Rect localBounds;
public final Rect sourceContainerBounds;
public final Rect screenSpaceBounds;
+ public final Rect startScreenSpaceBounds;
public final boolean isNotInRecents;
public final Rect contentInsets;
public final ActivityManager.RunningTaskInfo taskInfo;
@@ -88,6 +89,7 @@
localBounds = app.localBounds;
sourceContainerBounds = app.sourceContainerBounds;
screenSpaceBounds = app.screenSpaceBounds;
+ startScreenSpaceBounds = screenSpaceBounds;
prefixOrderIndex = app.prefixOrderIndex;
isNotInRecents = app.isNotInRecents;
contentInsets = app.contentInsets;
@@ -219,6 +221,8 @@
localBounds.offsetTo(change.getEndRelOffset().x, change.getEndRelOffset().y);
sourceContainerBounds = null;
screenSpaceBounds = new Rect(change.getEndAbsBounds());
+ startScreenSpaceBounds = new Rect(change.getStartAbsBounds());
+
prefixOrderIndex = order;
// TODO(shell-transitions): I guess we need to send content insets? evaluate how its used.
contentInsets = new Rect(0, 0, 0, 0);
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
index 10ceee9..adfc872 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
@@ -23,6 +23,7 @@
import dagger.Binds
import dagger.Module
import dagger.Provides
+import java.util.function.Supplier
@Module(includes = [
SettingsUtilModule::class
@@ -38,5 +39,9 @@
fun provideFlagManager(context: Context, @Main handler: Handler): FlagManager {
return FlagManager(context, handler)
}
+
+ @JvmStatic
+ @Provides
+ fun providesFlagCollector(): Supplier<Map<Int, Flag<*>>>? = null
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index ac463eb..1571913 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -168,6 +168,12 @@
if (!mIsDozing) mView.animateAppearOnLockscreen();
}
+ /** Animate the clock appearance when a foldable device goes from fully-open/half-open state to
+ * fully folded state and it goes to sleep (always on display screen) */
+ public void animateFoldAppear() {
+ mView.animateFoldAppear();
+ }
+
/**
* Updates the time for the view.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
deleted file mode 100644
index 2a0c285..0000000
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import android.annotation.FloatRange;
-import android.annotation.IntRange;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.text.format.DateFormat;
-import android.util.AttributeSet;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-
-import java.util.Calendar;
-import java.util.Locale;
-import java.util.TimeZone;
-
-import kotlin.Unit;
-
-/**
- * Displays the time with the hour positioned above the minutes. (ie: 09 above 30 is 9:30)
- * The time's text color is a gradient that changes its colors based on its controller.
- */
-public class AnimatableClockView extends TextView {
- private static final CharSequence DOUBLE_LINE_FORMAT_12_HOUR = "hh\nmm";
- private static final CharSequence DOUBLE_LINE_FORMAT_24_HOUR = "HH\nmm";
- private static final long DOZE_ANIM_DURATION = 300;
- private static final long APPEAR_ANIM_DURATION = 350;
- private static final long CHARGE_ANIM_DURATION_PHASE_0 = 500;
- private static final long CHARGE_ANIM_DURATION_PHASE_1 = 1000;
-
- private final Calendar mTime = Calendar.getInstance();
-
- private final int mDozingWeight;
- private final int mLockScreenWeight;
- private CharSequence mFormat;
- private CharSequence mDescFormat;
- private int mDozingColor;
- private int mLockScreenColor;
- private float mLineSpacingScale = 1f;
- private int mChargeAnimationDelay = 0;
-
- private TextAnimator mTextAnimator = null;
- private Runnable mOnTextAnimatorInitialized;
-
- private boolean mIsSingleLine;
-
- public AnimatableClockView(Context context) {
- this(context, null, 0, 0);
- }
-
- public AnimatableClockView(Context context, AttributeSet attrs) {
- this(context, attrs, 0, 0);
- }
-
- public AnimatableClockView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public AnimatableClockView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- TypedArray ta = context.obtainStyledAttributes(
- attrs, R.styleable.AnimatableClockView, defStyleAttr, defStyleRes);
- try {
- mDozingWeight = ta.getInt(R.styleable.AnimatableClockView_dozeWeight, 100);
- mLockScreenWeight = ta.getInt(R.styleable.AnimatableClockView_lockScreenWeight, 300);
- mChargeAnimationDelay = ta.getInt(
- R.styleable.AnimatableClockView_chargeAnimationDelay, 200);
- } finally {
- ta.recycle();
- }
-
- ta = context.obtainStyledAttributes(
- attrs, android.R.styleable.TextView, defStyleAttr, defStyleRes);
- try {
- mIsSingleLine = ta.getBoolean(android.R.styleable.TextView_singleLine, false);
- } finally {
- ta.recycle();
- }
-
- refreshFormat();
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- refreshFormat();
- }
-
- @Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- }
-
- int getDozingWeight() {
- if (useBoldedVersion()) {
- return mDozingWeight + 100;
- }
- return mDozingWeight;
- }
-
- int getLockScreenWeight() {
- if (useBoldedVersion()) {
- return mLockScreenWeight + 100;
- }
- return mLockScreenWeight;
- }
-
- /**
- * Whether to use a bolded version based on the user specified fontWeightAdjustment.
- */
- boolean useBoldedVersion() {
- // "Bold text" fontWeightAdjustment is 300.
- return getResources().getConfiguration().fontWeightAdjustment > 100;
- }
-
- void refreshTime() {
- mTime.setTimeInMillis(System.currentTimeMillis());
- setText(DateFormat.format(mFormat, mTime));
- setContentDescription(DateFormat.format(mDescFormat, mTime));
- }
-
- void onTimeZoneChanged(TimeZone timeZone) {
- mTime.setTimeZone(timeZone);
- refreshFormat();
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- if (mTextAnimator == null) {
- mTextAnimator = new TextAnimator(
- getLayout(),
- () -> {
- invalidate();
- return Unit.INSTANCE;
- });
- if (mOnTextAnimatorInitialized != null) {
- mOnTextAnimatorInitialized.run();
- mOnTextAnimatorInitialized = null;
- }
- } else {
- mTextAnimator.updateLayout(getLayout());
- }
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- mTextAnimator.draw(canvas);
- }
-
- void setLineSpacingScale(float scale) {
- mLineSpacingScale = scale;
- setLineSpacing(0, mLineSpacingScale);
- }
-
- void setColors(int dozingColor, int lockScreenColor) {
- mDozingColor = dozingColor;
- mLockScreenColor = lockScreenColor;
- }
-
- void animateAppearOnLockscreen() {
- if (mTextAnimator == null) {
- return;
- }
-
- setTextStyle(
- getDozingWeight(),
- -1 /* text size, no update */,
- mLockScreenColor,
- false /* animate */,
- 0 /* duration */,
- 0 /* delay */,
- null /* onAnimationEnd */);
-
- setTextStyle(
- getLockScreenWeight(),
- -1 /* text size, no update */,
- mLockScreenColor,
- true, /* animate */
- APPEAR_ANIM_DURATION,
- 0 /* delay */,
- null /* onAnimationEnd */);
- }
-
- void animateCharge(DozeStateGetter dozeStateGetter) {
- if (mTextAnimator == null || mTextAnimator.isRunning()) {
- // Skip charge animation if dozing animation is already playing.
- return;
- }
- Runnable startAnimPhase2 = () -> setTextStyle(
- dozeStateGetter.isDozing() ? getDozingWeight() : getLockScreenWeight() /* weight */,
- -1,
- null,
- true /* animate */,
- CHARGE_ANIM_DURATION_PHASE_1,
- 0 /* delay */,
- null /* onAnimationEnd */);
- setTextStyle(dozeStateGetter.isDozing()
- ? getLockScreenWeight()
- : getDozingWeight()/* weight */,
- -1,
- null,
- true /* animate */,
- CHARGE_ANIM_DURATION_PHASE_0,
- mChargeAnimationDelay,
- startAnimPhase2);
- }
-
- void animateDoze(boolean isDozing, boolean animate) {
- setTextStyle(isDozing ? getDozingWeight() : getLockScreenWeight() /* weight */,
- -1,
- isDozing ? mDozingColor : mLockScreenColor,
- animate,
- DOZE_ANIM_DURATION,
- 0 /* delay */,
- null /* onAnimationEnd */);
- }
-
- /**
- * Set text style with an optional animation.
- *
- * By passing -1 to weight, the view preserves its current weight.
- * By passing -1 to textSize, the view preserves its current text size.
- *
- * @param weight text weight.
- * @param textSize font size.
- * @param animate true to animate the text style change, otherwise false.
- */
- private void setTextStyle(
- @IntRange(from = 0, to = 1000) int weight,
- @FloatRange(from = 0) float textSize,
- Integer color,
- boolean animate,
- long duration,
- long delay,
- Runnable onAnimationEnd) {
- if (mTextAnimator != null) {
- mTextAnimator.setTextStyle(weight, textSize, color, animate, duration, null,
- delay, onAnimationEnd);
- } else {
- // when the text animator is set, update its start values
- mOnTextAnimatorInitialized =
- () -> mTextAnimator.setTextStyle(
- weight, textSize, color, false, duration, null,
- delay, onAnimationEnd);
- }
- }
-
- void refreshFormat() {
- Patterns.update(mContext);
-
- final boolean use24HourFormat = DateFormat.is24HourFormat(getContext());
- if (mIsSingleLine && use24HourFormat) {
- mFormat = Patterns.sClockView24;
- } else if (!mIsSingleLine && use24HourFormat) {
- mFormat = DOUBLE_LINE_FORMAT_24_HOUR;
- } else if (mIsSingleLine && !use24HourFormat) {
- mFormat = Patterns.sClockView12;
- } else {
- mFormat = DOUBLE_LINE_FORMAT_12_HOUR;
- }
-
- mDescFormat = use24HourFormat ? Patterns.sClockView24 : Patterns.sClockView12;
- refreshTime();
- }
-
- // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
- // This is an optimization to ensure we only recompute the patterns when the inputs change.
- private static final class Patterns {
- static String sClockView12;
- static String sClockView24;
- static String sCacheKey;
-
- static void update(Context context) {
- final Locale locale = Locale.getDefault();
- final Resources res = context.getResources();
- final String clockView12Skel = res.getString(R.string.clock_12hr_format);
- final String clockView24Skel = res.getString(R.string.clock_24hr_format);
- final String key = locale.toString() + clockView12Skel + clockView24Skel;
- if (key.equals(sCacheKey)) return;
- sClockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel);
-
- // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
- // format. The following code removes the AM/PM indicator if we didn't want it.
- if (!clockView12Skel.contains("a")) {
- sClockView12 = sClockView12.replaceAll("a", "").trim();
- }
- sClockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel);
- sCacheKey = key;
- }
- }
-
- interface DozeStateGetter {
- boolean isDozing();
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt
new file mode 100644
index 0000000..357be25
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt
@@ -0,0 +1,370 @@
+/*
+ * 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.
+ */
+package com.android.keyguard
+
+import android.animation.TimeInterpolator
+import android.annotation.ColorInt
+import android.annotation.FloatRange
+import android.annotation.IntRange
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.Canvas
+import android.text.format.DateFormat
+import android.util.AttributeSet
+import android.widget.TextView
+import com.android.systemui.R
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator
+import java.util.Calendar
+import java.util.Locale
+import java.util.TimeZone
+
+/**
+ * Displays the time with the hour positioned above the minutes. (ie: 09 above 30 is 9:30)
+ * The time's text color is a gradient that changes its colors based on its controller.
+ */
+class AnimatableClockView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : TextView(context, attrs, defStyleAttr, defStyleRes) {
+
+ private val time = Calendar.getInstance()
+
+ private val dozingWeightInternal: Int
+ private val lockScreenWeightInternal: Int
+ private val isSingleLineInternal: Boolean
+
+ private var format: CharSequence? = null
+ private var descFormat: CharSequence? = null
+
+ @ColorInt
+ private var dozingColor = 0
+
+ @ColorInt
+ private var lockScreenColor = 0
+
+ private var lineSpacingScale = 1f
+ private val chargeAnimationDelay: Int
+ private var textAnimator: TextAnimator? = null
+ private var onTextAnimatorInitialized: Runnable? = null
+
+ val dozingWeight: Int
+ get() = if (useBoldedVersion()) dozingWeightInternal + 100 else dozingWeightInternal
+
+ val lockScreenWeight: Int
+ get() = if (useBoldedVersion()) lockScreenWeightInternal + 100 else lockScreenWeightInternal
+
+ init {
+ val animatableClockViewAttributes = context.obtainStyledAttributes(
+ attrs, R.styleable.AnimatableClockView, defStyleAttr, defStyleRes
+ )
+
+ try {
+ dozingWeightInternal = animatableClockViewAttributes.getInt(
+ R.styleable.AnimatableClockView_dozeWeight,
+ 100
+ )
+ lockScreenWeightInternal = animatableClockViewAttributes.getInt(
+ R.styleable.AnimatableClockView_lockScreenWeight,
+ 300
+ )
+ chargeAnimationDelay = animatableClockViewAttributes.getInt(
+ R.styleable.AnimatableClockView_chargeAnimationDelay, 200
+ )
+ } finally {
+ animatableClockViewAttributes.recycle()
+ }
+
+ val textViewAttributes = context.obtainStyledAttributes(
+ attrs, android.R.styleable.TextView,
+ defStyleAttr, defStyleRes
+ )
+
+ isSingleLineInternal =
+ try {
+ textViewAttributes.getBoolean(android.R.styleable.TextView_singleLine, false)
+ } finally {
+ textViewAttributes.recycle()
+ }
+
+ refreshFormat()
+ }
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ refreshFormat()
+ }
+
+ /**
+ * Whether to use a bolded version based on the user specified fontWeightAdjustment.
+ */
+ fun useBoldedVersion(): Boolean {
+ // "Bold text" fontWeightAdjustment is 300.
+ return resources.configuration.fontWeightAdjustment > 100
+ }
+
+ fun refreshTime() {
+ time.timeInMillis = System.currentTimeMillis()
+ text = DateFormat.format(format, time)
+ contentDescription = DateFormat.format(descFormat, time)
+ }
+
+ fun onTimeZoneChanged(timeZone: TimeZone?) {
+ time.timeZone = timeZone
+ refreshFormat()
+ }
+
+ @SuppressLint("DrawAllocation")
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ val animator = textAnimator
+ if (animator == null) {
+ textAnimator = TextAnimator(layout) { invalidate() }
+ onTextAnimatorInitialized?.run()
+ onTextAnimatorInitialized = null
+ } else {
+ animator.updateLayout(layout)
+ }
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ textAnimator?.draw(canvas)
+ }
+
+ fun setLineSpacingScale(scale: Float) {
+ lineSpacingScale = scale
+ setLineSpacing(0f, lineSpacingScale)
+ }
+
+ fun setColors(@ColorInt dozingColor: Int, lockScreenColor: Int) {
+ this.dozingColor = dozingColor
+ this.lockScreenColor = lockScreenColor
+ }
+
+ fun animateAppearOnLockscreen() {
+ if (textAnimator == null) {
+ return
+ }
+ setTextStyle(
+ weight = dozingWeight,
+ textSize = -1f,
+ color = lockScreenColor,
+ animate = false,
+ duration = 0,
+ delay = 0,
+ onAnimationEnd = null
+ )
+ setTextStyle(
+ weight = lockScreenWeight,
+ textSize = -1f,
+ color = lockScreenColor,
+ animate = true,
+ duration = APPEAR_ANIM_DURATION,
+ delay = 0,
+ onAnimationEnd = null
+ )
+ }
+
+ fun animateFoldAppear() {
+ if (textAnimator == null) {
+ return
+ }
+ setTextStyle(
+ weight = lockScreenWeightInternal,
+ textSize = -1f,
+ color = lockScreenColor,
+ animate = false,
+ duration = 0,
+ delay = 0,
+ onAnimationEnd = null
+ )
+ setTextStyle(
+ weight = dozingWeightInternal,
+ textSize = -1f,
+ color = dozingColor,
+ animate = true,
+ interpolator = Interpolators.EMPHASIZED_DECELERATE,
+ duration = StackStateAnimator.ANIMATION_DURATION_FOLD_TO_AOD.toLong(),
+ delay = 0,
+ onAnimationEnd = null
+ )
+ }
+
+ fun animateCharge(dozeStateGetter: DozeStateGetter) {
+ if (textAnimator == null || textAnimator!!.isRunning()) {
+ // Skip charge animation if dozing animation is already playing.
+ return
+ }
+ val startAnimPhase2 = Runnable {
+ setTextStyle(
+ weight = if (dozeStateGetter.isDozing) dozingWeight else lockScreenWeight,
+ textSize = -1f,
+ color = null,
+ animate = true,
+ duration = CHARGE_ANIM_DURATION_PHASE_1,
+ delay = 0,
+ onAnimationEnd = null
+ )
+ }
+ setTextStyle(
+ weight = if (dozeStateGetter.isDozing) lockScreenWeight else dozingWeight,
+ textSize = -1f,
+ color = null,
+ animate = true,
+ duration = CHARGE_ANIM_DURATION_PHASE_0,
+ delay = chargeAnimationDelay.toLong(),
+ onAnimationEnd = startAnimPhase2
+ )
+ }
+
+ fun animateDoze(isDozing: Boolean, animate: Boolean) {
+ setTextStyle(
+ weight = if (isDozing) dozingWeight else lockScreenWeight,
+ textSize = -1f,
+ color = if (isDozing) dozingColor else lockScreenColor,
+ animate = animate,
+ duration = DOZE_ANIM_DURATION,
+ delay = 0,
+ onAnimationEnd = null
+ )
+ }
+
+ /**
+ * Set text style with an optional animation.
+ *
+ * By passing -1 to weight, the view preserves its current weight.
+ * By passing -1 to textSize, the view preserves its current text size.
+ *
+ * @param weight text weight.
+ * @param textSize font size.
+ * @param animate true to animate the text style change, otherwise false.
+ */
+ private fun setTextStyle(
+ @IntRange(from = 0, to = 1000) weight: Int,
+ @FloatRange(from = 0.0) textSize: Float,
+ color: Int?,
+ animate: Boolean,
+ interpolator: TimeInterpolator?,
+ duration: Long,
+ delay: Long,
+ onAnimationEnd: Runnable?
+ ) {
+ if (textAnimator != null) {
+ textAnimator?.setTextStyle(
+ weight = weight,
+ textSize = textSize,
+ color = color,
+ animate = animate,
+ duration = duration,
+ interpolator = interpolator,
+ delay = delay,
+ onAnimationEnd = onAnimationEnd
+ )
+ } else {
+ // when the text animator is set, update its start values
+ onTextAnimatorInitialized = Runnable {
+ textAnimator?.setTextStyle(
+ weight = weight,
+ textSize = textSize,
+ color = color,
+ animate = false,
+ duration = duration,
+ interpolator = interpolator,
+ delay = delay,
+ onAnimationEnd = onAnimationEnd
+ )
+ }
+ }
+ }
+
+ private fun setTextStyle(
+ @IntRange(from = 0, to = 1000) weight: Int,
+ @FloatRange(from = 0.0) textSize: Float,
+ color: Int?,
+ animate: Boolean,
+ duration: Long,
+ delay: Long,
+ onAnimationEnd: Runnable?
+ ) {
+ setTextStyle(
+ weight = weight,
+ textSize = textSize,
+ color = color,
+ animate = animate,
+ interpolator = null,
+ duration = duration,
+ delay = delay,
+ onAnimationEnd = onAnimationEnd
+ )
+ }
+
+ fun refreshFormat() {
+ Patterns.update(context)
+ val use24HourFormat = DateFormat.is24HourFormat(context)
+
+ format = when {
+ isSingleLineInternal && use24HourFormat -> Patterns.sClockView24
+ !isSingleLineInternal && use24HourFormat -> DOUBLE_LINE_FORMAT_24_HOUR
+ isSingleLineInternal && !use24HourFormat -> Patterns.sClockView12
+ else -> DOUBLE_LINE_FORMAT_12_HOUR
+ }
+
+ descFormat = if (use24HourFormat) Patterns.sClockView24 else Patterns.sClockView12
+
+ refreshTime()
+ }
+
+ // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
+ // This is an optimization to ensure we only recompute the patterns when the inputs change.
+ private object Patterns {
+ var sClockView12: String? = null
+ var sClockView24: String? = null
+ var sCacheKey: String? = null
+
+ fun update(context: Context) {
+ val locale = Locale.getDefault()
+ val res = context.resources
+ val clockView12Skel = res.getString(R.string.clock_12hr_format)
+ val clockView24Skel = res.getString(R.string.clock_24hr_format)
+ val key = locale.toString() + clockView12Skel + clockView24Skel
+ if (key == sCacheKey) return
+
+ val clockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel)
+ sClockView12 = clockView12
+
+ // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
+ // format. The following code removes the AM/PM indicator if we didn't want it.
+ if (!clockView12Skel.contains("a")) {
+ sClockView12 = clockView12.replace("a".toRegex(), "").trim { it <= ' ' }
+ }
+ sClockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel)
+ sCacheKey = key
+ }
+ }
+
+ interface DozeStateGetter {
+ val isDozing: Boolean
+ }
+}
+
+private const val DOUBLE_LINE_FORMAT_12_HOUR = "hh\nmm"
+private const val DOUBLE_LINE_FORMAT_24_HOUR = "HH\nmm"
+private const val DOZE_ANIM_DURATION: Long = 300
+private const val APPEAR_ANIM_DURATION: Long = 350
+private const val CHARGE_ANIM_DURATION_PHASE_0: Long = 500
+private const val CHARGE_ANIM_DURATION_PHASE_1: Long = 1000
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 9238b82..25dcdf9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -190,11 +190,15 @@
}
}
- private void animateClockChange(boolean useLargeClock) {
+ private void updateClockViews(boolean useLargeClock, boolean animate) {
if (mClockInAnim != null) mClockInAnim.cancel();
if (mClockOutAnim != null) mClockOutAnim.cancel();
if (mStatusAreaAnim != null) mStatusAreaAnim.cancel();
+ mClockInAnim = null;
+ mClockOutAnim = null;
+ mStatusAreaAnim = null;
+
View in, out;
int direction = 1;
float statusAreaYTranslation;
@@ -214,6 +218,14 @@
removeView(out);
}
+ if (!animate) {
+ out.setAlpha(0f);
+ in.setAlpha(1f);
+ in.setVisibility(VISIBLE);
+ mStatusArea.setTranslationY(statusAreaYTranslation);
+ return;
+ }
+
mClockOutAnim = new AnimatorSet();
mClockOutAnim.setDuration(CLOCK_OUT_MILLIS);
mClockOutAnim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
@@ -273,7 +285,7 @@
*
* @return true if desired clock appeared and false if it was already visible
*/
- boolean switchToClock(@ClockSize int clockSize) {
+ boolean switchToClock(@ClockSize int clockSize, boolean animate) {
if (mDisplayedClockSize != null && clockSize == mDisplayedClockSize) {
return false;
}
@@ -281,7 +293,7 @@
// let's make sure clock is changed only after all views were laid out so we can
// translate them properly
if (mChildrenAreLaidOut) {
- animateClockChange(clockSize == LARGE);
+ updateClockViews(clockSize == LARGE, animate);
}
mDisplayedClockSize = clockSize;
@@ -293,7 +305,7 @@
super.onLayout(changed, l, t, r, b);
if (mDisplayedClockSize != null && !mChildrenAreLaidOut) {
- animateClockChange(mDisplayedClockSize == LARGE);
+ updateClockViews(mDisplayedClockSize == LARGE, /* animate */ true);
}
mChildrenAreLaidOut = true;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index c628d44..b7a5aae 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -89,7 +89,6 @@
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final KeyguardBypassController mBypassController;
- private int mLargeClockTopMargin = 0;
private int mKeyguardClockTopMargin = 0;
/**
@@ -276,33 +275,37 @@
}
private void updateClockLayout() {
- if (mSmartspaceController.isEnabled()) {
- RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT,
- MATCH_PARENT);
- mLargeClockTopMargin = getContext().getResources().getDimensionPixelSize(
- R.dimen.keyguard_large_clock_top_margin);
- lp.topMargin = mLargeClockTopMargin;
- mLargeClockFrame.setLayoutParams(lp);
- } else {
- mLargeClockTopMargin = 0;
- }
+ int largeClockTopMargin = getContext().getResources().getDimensionPixelSize(
+ R.dimen.keyguard_large_clock_top_margin);
+
+ RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT,
+ MATCH_PARENT);
+ lp.topMargin = largeClockTopMargin;
+ mLargeClockFrame.setLayoutParams(lp);
}
/**
* Set which clock should be displayed on the keyguard. The other one will be automatically
* hidden.
*/
- public void displayClock(@KeyguardClockSwitch.ClockSize int clockSize) {
+ public void displayClock(@KeyguardClockSwitch.ClockSize int clockSize, boolean animate) {
if (!mCanShowDoubleLineClock && clockSize == KeyguardClockSwitch.LARGE) {
return;
}
- boolean appeared = mView.switchToClock(clockSize);
- if (appeared && clockSize == LARGE) {
+ boolean appeared = mView.switchToClock(clockSize, animate);
+ if (animate && appeared && clockSize == LARGE) {
mLargeClockViewController.animateAppear();
}
}
+ public void animateFoldToAod() {
+ if (mClockViewController != null) {
+ mClockViewController.animateFoldAppear();
+ mLargeClockViewController.animateFoldAppear();
+ }
+ }
+
/**
* If we're presenting a custom clock of just the default one.
*/
@@ -445,7 +448,7 @@
Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, 1) != 0;
if (!mCanShowDoubleLineClock) {
- mUiExecutor.execute(() -> displayClock(KeyguardClockSwitch.SMALL));
+ mUiExecutor.execute(() -> displayClock(KeyguardClockSwitch.SMALL, /* animate */ true));
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
index 40190c1..7eae729 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
@@ -48,10 +48,6 @@
abstract CharSequence getTitle();
- void animateForIme(float interpolatedFraction, boolean appearingAnim) {
- return;
- }
-
boolean disallowInterceptTouch(MotionEvent event) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 3a3d308..bc366ab 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -156,8 +156,7 @@
setAlpha(0f);
animate()
.alpha(1f)
- .setDuration(500)
- .setStartDelay(300)
+ .setDuration(300)
.start();
setTranslationY(0f);
@@ -219,15 +218,6 @@
return true;
}
-
- @Override
- public void animateForIme(float interpolatedFraction, boolean appearingAnim) {
- animate().cancel();
- setAlpha(appearingAnim
- ? Math.max(interpolatedFraction, getAlpha())
- : 1 - interpolatedFraction);
- }
-
@Override
public CharSequence getTitle() {
return getResources().getString(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 172c7f6..b84cb19 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -19,21 +19,29 @@
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
+import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY;
+
import static java.lang.Integer.max;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
import android.util.TypedValue;
import android.view.Gravity;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
@@ -44,7 +52,10 @@
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import android.widget.AdapterView;
import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
@@ -56,12 +67,17 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
+import com.android.internal.util.UserIcons;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdapter;
+import com.android.systemui.statusbar.policy.UserSwitcherController.UserRecord;
import com.android.systemui.util.settings.GlobalSettings;
import java.util.ArrayList;
@@ -110,6 +126,8 @@
@VisibleForTesting
KeyguardSecurityViewFlipper mSecurityViewFlipper;
private GlobalSettings mGlobalSettings;
+ private FalsingManager mFalsingManager;
+ private UserSwitcherController mUserSwitcherController;
private AlertDialog mAlertDialog;
private boolean mSwipeUpToRetry;
@@ -124,7 +142,7 @@
private float mStartTouchY = -1;
private boolean mDisappearAnimRunning;
private SwipeListener mSwipeListener;
- private ModeLogic mModeLogic = new DefaultModeLogic();
+ private ViewMode mViewMode = new DefaultViewMode();
private @Mode int mCurrentMode = MODE_DEFAULT;
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
@@ -173,8 +191,11 @@
interpolatedFraction);
translationY += paddingBottom;
}
- mSecurityViewFlipper.animateForIme(translationY, interpolatedFraction,
- !mDisappearAnimRunning);
+
+ float alpha = mDisappearAnimRunning
+ ? 1 - interpolatedFraction
+ : Math.max(interpolatedFraction, getAlpha());
+ updateChildren(translationY, alpha);
return windowInsets;
}
@@ -183,12 +204,19 @@
public void onEnd(WindowInsetsAnimation animation) {
if (!mDisappearAnimRunning) {
endJankInstrument(InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_APPEAR);
- mSecurityViewFlipper.animateForIme(0, /* interpolatedFraction */ 1f,
- true /* appearingAnim */);
+ updateChildren(0 /* translationY */, 1f /* alpha */);
} else {
endJankInstrument(InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
}
}
+
+ private void updateChildren(int translationY, float alpha) {
+ for (int i = 0; i < KeyguardSecurityContainer.this.getChildCount(); ++i) {
+ View child = KeyguardSecurityContainer.this.getChildAt(i);
+ child.setTranslationY(translationY);
+ child.setAlpha(alpha);
+ }
+ }
};
// Used to notify the container when something interesting happens.
@@ -270,9 +298,12 @@
void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
updateBiometricRetry(securityMode, faceAuthEnabled);
+
+ setupViewMode();
}
- void initMode(@Mode int mode, GlobalSettings globalSettings) {
+ void initMode(@Mode int mode, GlobalSettings globalSettings, FalsingManager falsingManager,
+ UserSwitcherController userSwitcherController) {
if (mCurrentMode == mode) return;
Log.i(TAG, "Switching mode from " + modeToString(mCurrentMode) + " to "
+ modeToString(mode));
@@ -280,16 +311,18 @@
switch (mode) {
case MODE_ONE_HANDED:
- mModeLogic = new OneHandedModeLogic();
+ mViewMode = new OneHandedViewMode();
break;
case MODE_USER_SWITCHER:
- mModeLogic = new UserSwitcherModeLogic();
+ mViewMode = new UserSwitcherViewMode();
break;
default:
- mModeLogic = new DefaultModeLogic();
+ mViewMode = new DefaultViewMode();
}
mGlobalSettings = globalSettings;
- finishSetup();
+ mFalsingManager = falsingManager;
+ mUserSwitcherController = userSwitcherController;
+ setupViewMode();
}
private String modeToString(@Mode int mode) {
@@ -305,10 +338,14 @@
}
}
- private void finishSetup() {
- if (mSecurityViewFlipper == null || mGlobalSettings == null) return;
+ private void setupViewMode() {
+ if (mSecurityViewFlipper == null || mGlobalSettings == null
+ || mFalsingManager == null || mUserSwitcherController == null) {
+ return;
+ }
- mModeLogic.init(this, mGlobalSettings, mSecurityViewFlipper);
+ mViewMode.init(this, mGlobalSettings, mSecurityViewFlipper, mFalsingManager,
+ mUserSwitcherController);
}
@Mode int getMode() {
@@ -321,13 +358,13 @@
* that the user last interacted with.
*/
void updatePositionByTouchX(float x) {
- mModeLogic.updatePositionByTouchX(x);
+ mViewMode.updatePositionByTouchX(x);
}
/** Returns whether the inner SecurityViewFlipper is left-aligned when in one-handed mode. */
public boolean isOneHandedModeLeftAligned() {
return mCurrentMode == MODE_ONE_HANDED
- && ((OneHandedModeLogic) mModeLogic).isLeftAligned();
+ && ((OneHandedViewMode) mViewMode).isLeftAligned();
}
public void onPause() {
@@ -336,6 +373,7 @@
mAlertDialog = null;
}
mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
+ mViewMode.reset();
}
@Override
@@ -428,7 +466,7 @@
}
} else {
if (!mIsDragging) {
- mModeLogic.handleTap(event);
+ mViewMode.handleTap(event);
}
}
}
@@ -453,8 +491,19 @@
.animateToFinalPosition(0);
}
+ /**
+ * Runs after a succsssful authentication only
+ */
public void startDisappearAnimation(SecurityMode securitySelection) {
mDisappearAnimRunning = true;
+ mViewMode.startDisappearAnimation(securitySelection);
+ }
+
+ /**
+ * This will run when the bouncer shows in all cases except when the user drags the bouncer up.
+ */
+ public void startAppearAnimation(SecurityMode securityMode) {
+ mViewMode.startAppearAnimation(securityMode);
}
private void beginJankInstrument(int cuj) {
@@ -490,8 +539,6 @@
public void onFinishInflate() {
super.onFinishInflate();
mSecurityViewFlipper = findViewById(R.id.view_flipper);
-
- finishSetup();
}
@Override
@@ -562,10 +609,7 @@
for (int i = 0; i < getChildCount(); i++) {
final View view = getChildAt(i);
if (view.getVisibility() != GONE) {
- int updatedWidthMeasureSpec = widthMeasureSpec;
- if (view == mSecurityViewFlipper) {
- updatedWidthMeasureSpec = mModeLogic.getChildWidthMeasureSpec(widthMeasureSpec);
- }
+ int updatedWidthMeasureSpec = mViewMode.getChildWidthMeasureSpec(widthMeasureSpec);
measureChildWithMargins(view, updatedWidthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) view.getLayoutParams();
@@ -595,7 +639,13 @@
// After a layout pass, we need to re-place the inner bouncer, as our bounds may have
// changed.
- mModeLogic.updateSecurityViewLocation();
+ mViewMode.updateSecurityViewLocation();
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration config) {
+ super.onConfigurationChanged(config);
+ mViewMode.updateSecurityViewLocation();
}
void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
@@ -643,10 +693,12 @@
/**
* Enscapsulates the differences between bouncer modes for the container.
*/
- private interface ModeLogic {
+ interface ViewMode {
- default void init(ViewGroup v, GlobalSettings globalSettings,
- KeyguardSecurityViewFlipper viewFlipper) {};
+ default void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
+ @NonNull KeyguardSecurityViewFlipper viewFlipper,
+ @NonNull FalsingManager falsingManager,
+ @NonNull UserSwitcherController userSwitcherController) {};
/** Reinitialize the location */
default void updateSecurityViewLocation() {};
@@ -657,19 +709,33 @@
/** A tap on the container, outside of the ViewFlipper */
default void handleTap(MotionEvent event) {};
+ /** Called when the view needs to reset or hides */
+ default void reset() {};
+
+ /** On a successful auth, optionally handle how the view disappears */
+ default void startDisappearAnimation(SecurityMode securityMode) {};
+
+ /** On notif tap, this animation will run */
+ default void startAppearAnimation(SecurityMode securityMode) {};
+
/** Override to alter the width measure spec to perhaps limit the ViewFlipper size */
default int getChildWidthMeasureSpec(int parentWidthMeasureSpec) {
return parentWidthMeasureSpec;
}
}
- private static class DefaultModeLogic implements ModeLogic {
+ /**
+ * Default bouncer is centered within the space
+ */
+ static class DefaultViewMode implements ViewMode {
private ViewGroup mView;
private KeyguardSecurityViewFlipper mViewFlipper;
@Override
- public void init(ViewGroup v, GlobalSettings globalSettings,
- KeyguardSecurityViewFlipper viewFlipper) {
+ public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
+ @NonNull KeyguardSecurityViewFlipper viewFlipper,
+ @NonNull FalsingManager falsingManager,
+ @NonNull UserSwitcherController userSwitcherController) {
mView = v;
mViewFlipper = viewFlipper;
@@ -682,7 +748,6 @@
(FrameLayout.LayoutParams) mViewFlipper.getLayoutParams();
lp.gravity = Gravity.CENTER_HORIZONTAL;
mViewFlipper.setLayoutParams(lp);
-
mViewFlipper.setTranslationX(0);
}
}
@@ -691,13 +756,172 @@
* User switcher mode will display both the current user icon as well as
* a user switcher, in both portrait and landscape modes.
*/
- private static class UserSwitcherModeLogic implements ModeLogic {
+ static class UserSwitcherViewMode implements ViewMode {
private ViewGroup mView;
+ private ViewGroup mUserSwitcherViewGroup;
+ private KeyguardSecurityViewFlipper mViewFlipper;
+ private ImageView mUserIconView;
+ private TextView mUserSwitcher;
+ private FalsingManager mFalsingManager;
+ private UserSwitcherController mUserSwitcherController;
+ private KeyguardUserSwitcherPopupMenu mPopup;
@Override
- public void init(ViewGroup v, GlobalSettings globalSettings,
- KeyguardSecurityViewFlipper viewFlipper) {
+ public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
+ @NonNull KeyguardSecurityViewFlipper viewFlipper,
+ @NonNull FalsingManager falsingManager,
+ @NonNull UserSwitcherController userSwitcherController) {
mView = v;
+ mViewFlipper = viewFlipper;
+ mFalsingManager = falsingManager;
+ mUserSwitcherController = userSwitcherController;
+
+ if (mUserSwitcherViewGroup == null) {
+ LayoutInflater.from(v.getContext()).inflate(
+ R.layout.keyguard_bouncer_user_switcher,
+ mView,
+ true);
+ mUserSwitcherViewGroup = mView.findViewById(R.id.keyguard_bouncer_user_switcher);
+ }
+
+ mUserIconView = mView.findViewById(R.id.user_icon);
+ Drawable icon = UserIcons.getDefaultUserIcon(v.getContext().getResources(), 0, false);
+ mUserIconView.setImageDrawable(icon);
+
+ updateSecurityViewLocation();
+
+ mUserSwitcher = mView.findViewById(R.id.user_switcher_header);
+ setupUserSwitcher();
+ }
+
+ @Override
+ public void reset() {
+ if (mPopup != null) {
+ mPopup.dismiss();
+ mPopup = null;
+ }
+ }
+
+ @Override
+ public void startAppearAnimation(SecurityMode securityMode) {
+ // IME insets animations handle alpha and translation
+ if (securityMode == SecurityMode.Password) {
+ return;
+ }
+
+ mUserSwitcherViewGroup.setAlpha(0f);
+ ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mUserSwitcherViewGroup, View.ALPHA,
+ 1f);
+ alphaAnim.setInterpolator(Interpolators.ALPHA_IN);
+ alphaAnim.setDuration(500);
+ alphaAnim.start();
+ }
+
+ @Override
+ public void startDisappearAnimation(SecurityMode securityMode) {
+ // IME insets animations handle alpha and translation
+ if (securityMode == SecurityMode.Password) {
+ return;
+ }
+
+ int yTranslation = mView.getContext().getResources().getDimensionPixelSize(
+ R.dimen.disappear_y_translation);
+
+ AnimatorSet anims = new AnimatorSet();
+ ObjectAnimator yAnim = ObjectAnimator.ofFloat(mView, View.TRANSLATION_Y, yTranslation);
+ ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mView, View.ALPHA, 0f);
+
+ anims.setInterpolator(Interpolators.STANDARD_ACCELERATE);
+ anims.playTogether(alphaAnim, yAnim);
+ anims.start();
+ }
+
+ private void setupUserSwitcher() {
+ String currentUserName = mUserSwitcherController.getCurrentUserName();
+ mUserSwitcher.setText(currentUserName);
+
+ ViewGroup anchor = mView.findViewById(R.id.user_switcher_anchor);
+ BaseUserAdapter adapter = new BaseUserAdapter(mUserSwitcherController) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ UserRecord item = getItem(position);
+ TextView view = (TextView) convertView;
+ if (view == null) {
+ view = (TextView) LayoutInflater.from(parent.getContext()).inflate(
+ R.layout.keyguard_bouncer_user_switcher_item,
+ parent,
+ false);
+ }
+ view.setText(getName(parent.getContext(), item));
+ return view;
+ }
+ };
+
+ if (adapter.getCount() < 2) {
+ // The drop down arrow is at index 1
+ ((LayerDrawable) mUserSwitcher.getBackground()).getDrawable(1).setAlpha(0);
+ anchor.setClickable(false);
+ return;
+ } else {
+ ((LayerDrawable) mUserSwitcher.getBackground()).getDrawable(1).setAlpha(255);
+ }
+
+ anchor.setOnClickListener((v) -> {
+ if (mFalsingManager.isFalseTap(LOW_PENALTY)) return;
+
+ mPopup = new KeyguardUserSwitcherPopupMenu(v.getContext(), mFalsingManager);
+ mPopup.setAnchorView(anchor);
+ mPopup.setAdapter(adapter);
+ mPopup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ public void onItemClick(AdapterView parent, View view, int pos, long id) {
+ if (mFalsingManager.isFalseTap(LOW_PENALTY)) return;
+
+ UserRecord user = adapter.getItem(pos);
+ if (!user.isCurrent) {
+ adapter.onUserListItemClicked(user);
+ }
+ mPopup.dismiss();
+ mPopup = null;
+ }
+ });
+ mPopup.show();
+ });
+ }
+
+ /**
+ * Each view will get half the width. Yes, it would be easier to use something other than
+ * FrameLayout but it was too disruptive to downstream projects to change.
+ */
+ @Override
+ public int getChildWidthMeasureSpec(int parentWidthMeasureSpec) {
+ return MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(parentWidthMeasureSpec) / 2,
+ MeasureSpec.getMode(parentWidthMeasureSpec));
+ }
+
+ @Override
+ public void updateSecurityViewLocation() {
+ if (mView.getContext().getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_PORTRAIT) {
+ updateViewGravity(mViewFlipper, Gravity.CENTER_HORIZONTAL);
+ updateViewGravity(mUserSwitcherViewGroup, Gravity.CENTER_HORIZONTAL);
+ mUserSwitcherViewGroup.setTranslationY(0);
+ } else {
+ updateViewGravity(mViewFlipper, Gravity.RIGHT | Gravity.BOTTOM);
+ updateViewGravity(mUserSwitcherViewGroup, Gravity.LEFT | Gravity.CENTER_VERTICAL);
+
+ // Attempt to reposition a bit higher to make up for this frame being a bit lower
+ // on the device
+ int yTrans = mView.getContext().getResources().getDimensionPixelSize(
+ R.dimen.status_bar_height);
+ mUserSwitcherViewGroup.setTranslationY(-yTrans);
+ }
+ }
+
+ private void updateViewGravity(View v, int gravity) {
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams();
+ lp.gravity = gravity;
+ v.setLayoutParams(lp);
}
}
@@ -705,7 +929,7 @@
* Logic to enabled one-handed bouncer mode. Supports animating the bouncer
* between alternate sides of the display.
*/
- private static class OneHandedModeLogic implements ModeLogic {
+ static class OneHandedViewMode implements ViewMode {
@Nullable private ValueAnimator mRunningOneHandedAnimator;
private ViewGroup mView;
private KeyguardSecurityViewFlipper mViewFlipper;
@@ -713,7 +937,9 @@
@Override
public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
- @NonNull KeyguardSecurityViewFlipper viewFlipper) {
+ @NonNull KeyguardSecurityViewFlipper viewFlipper,
+ @NonNull FalsingManager falsingManager,
+ @NonNull UserSwitcherController userSwitcherController) {
mView = v;
mViewFlipper = viewFlipper;
mGlobalSettings = globalSettings;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 4035229..2fb2211 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -51,9 +51,11 @@
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.settings.GlobalSettings;
@@ -78,6 +80,8 @@
private final SecurityCallback mSecurityCallback;
private final ConfigurationController mConfigurationController;
private final FalsingCollector mFalsingCollector;
+ private final FalsingManager mFalsingManager;
+ private final UserSwitcherController mUserSwitcherController;
private final GlobalSettings mGlobalSettings;
private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
@@ -232,6 +236,8 @@
KeyguardSecurityViewFlipperController securityViewFlipperController,
ConfigurationController configurationController,
FalsingCollector falsingCollector,
+ FalsingManager falsingManager,
+ UserSwitcherController userSwitcherController,
GlobalSettings globalSettings) {
super(view);
mLockPatternUtils = lockPatternUtils;
@@ -247,6 +253,8 @@
mConfigurationController = configurationController;
mLastOrientation = getResources().getConfiguration().orientation;
mFalsingCollector = falsingCollector;
+ mFalsingManager = falsingManager;
+ mUserSwitcherController = userSwitcherController;
mGlobalSettings = globalSettings;
}
@@ -343,14 +351,14 @@
public void startAppearAnimation() {
if (mCurrentSecurityMode != SecurityMode.None) {
+ mView.startAppearAnimation(mCurrentSecurityMode);
getCurrentSecurityController().startAppearAnimation();
}
}
public boolean startDisappearAnimation(Runnable onFinishRunnable) {
- mView.startDisappearAnimation(getCurrentSecurityMode());
-
if (mCurrentSecurityMode != SecurityMode.None) {
+ mView.startDisappearAnimation(mCurrentSecurityMode);
return getCurrentSecurityController().startDisappearAnimation(onFinishRunnable);
}
@@ -506,15 +514,16 @@
}
private void configureMode() {
- // One-handed mode and user-switcher are currently mutually exclusive, and enforced here
+ boolean useSimSecurity = mCurrentSecurityMode == SecurityMode.SimPin
+ || mCurrentSecurityMode == SecurityMode.SimPuk;
int mode = KeyguardSecurityContainer.MODE_DEFAULT;
- if (canDisplayUserSwitcher()) {
+ if (canDisplayUserSwitcher() && !useSimSecurity) {
mode = KeyguardSecurityContainer.MODE_USER_SWITCHER;
} else if (canUseOneHandedBouncer()) {
mode = KeyguardSecurityContainer.MODE_ONE_HANDED;
}
- mView.initMode(mode, mGlobalSettings);
+ mView.initMode(mode, mGlobalSettings, mFalsingManager, mUserSwitcherController);
}
public void reportFailedUnlockAttempt(int userId, int timeoutMs) {
@@ -604,7 +613,9 @@
private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
private final ConfigurationController mConfigurationController;
private final FalsingCollector mFalsingCollector;
+ private final FalsingManager mFalsingManager;
private final GlobalSettings mGlobalSettings;
+ private final UserSwitcherController mUserSwitcherController;
@Inject
Factory(KeyguardSecurityContainer view,
@@ -619,6 +630,8 @@
KeyguardSecurityViewFlipperController securityViewFlipperController,
ConfigurationController configurationController,
FalsingCollector falsingCollector,
+ FalsingManager falsingManager,
+ UserSwitcherController userSwitcherController,
GlobalSettings globalSettings) {
mView = view;
mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
@@ -631,7 +644,9 @@
mSecurityViewFlipperController = securityViewFlipperController;
mConfigurationController = configurationController;
mFalsingCollector = falsingCollector;
+ mFalsingManager = falsingManager;
mGlobalSettings = globalSettings;
+ mUserSwitcherController = userSwitcherController;
}
public KeyguardSecurityContainerController create(
@@ -640,7 +655,8 @@
mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, securityCallback, mSecurityViewFlipperController,
- mConfigurationController, mFalsingCollector, mGlobalSettings);
+ mConfigurationController, mFalsingCollector, mFalsingManager,
+ mUserSwitcherController, mGlobalSettings);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index e01e17d..4d2391a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -83,16 +83,6 @@
return "";
}
- /**
- * Translate the entire view, and optionally inform the wrapped view of the progress
- * so it can animate with the parent.
- */
- public void animateForIme(int translationY, float interpolatedFraction, boolean appearingAnim) {
- super.setTranslationY(translationY);
- KeyguardInputView v = getSecurityView();
- if (v != null) v.animateForIme(interpolatedFraction, appearingAnim);
- }
-
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 0d72c93..03b647b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -92,11 +92,6 @@
}
@Override
- public boolean startDisappearAnimation(Runnable finishRunnable) {
- return false;
- }
-
- @Override
public CharSequence getTitle() {
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_sim_puk_unlock);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 986d0de..8bf890d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -123,8 +123,17 @@
* Set which clock should be displayed on the keyguard. The other one will be automatically
* hidden.
*/
- public void displayClock(@ClockSize int clockSize) {
- mKeyguardClockSwitchController.displayClock(clockSize);
+ public void displayClock(@ClockSize int clockSize, boolean animate) {
+ mKeyguardClockSwitchController.displayClock(clockSize, animate);
+ }
+
+ /**
+ * Performs fold to aod animation of the clocks (changes font weight from bold to thin).
+ * This animation is played when AOD is enabled and foldable device is fully folded, it is
+ * displayed on the outer screen
+ */
+ public void animateFoldToAod() {
+ mKeyguardClockSwitchController.animateFoldToAod();
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
new file mode 100644
index 0000000..7b6ce3e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+package com.android.keyguard;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.ListPopupWindow;
+import android.widget.ListView;
+
+import com.android.systemui.R;
+import com.android.systemui.plugins.FalsingManager;
+
+/**
+ * Custom user-switcher for use on the bouncer.
+ */
+public class KeyguardUserSwitcherPopupMenu extends ListPopupWindow {
+ private Context mContext;
+ private FalsingManager mFalsingManager;
+ private int mLastHeight = -1;
+ private View.OnLayoutChangeListener mLayoutListener = (v, l, t, r, b, ol, ot, or, ob) -> {
+ int height = -v.getMeasuredHeight() + getAnchorView().getHeight();
+ if (height != mLastHeight) {
+ mLastHeight = height;
+ setVerticalOffset(height);
+ KeyguardUserSwitcherPopupMenu.super.show();
+ }
+ };
+
+ public KeyguardUserSwitcherPopupMenu(@NonNull Context context,
+ @NonNull FalsingManager falsingManager) {
+ super(context);
+ mContext = context;
+ mFalsingManager = falsingManager;
+ Resources res = mContext.getResources();
+ setBackgroundDrawable(
+ res.getDrawable(R.drawable.keyguard_user_switcher_popup_bg, context.getTheme()));
+ setModal(true);
+ setOverlapAnchor(true);
+ }
+
+ /**
+ * Show the dialog.
+ */
+ @Override
+ public void show() {
+ // need to call show() first in order to construct the listView
+ super.show();
+ ListView listView = getListView();
+
+ // This will force the popupwindow to show upward instead of drop down
+ listView.addOnLayoutChangeListener(mLayoutListener);
+
+ listView.setOnTouchListener((v, ev) -> {
+ if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ return mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY);
+ }
+ return false;
+ });
+ }
+
+ @Override
+ public void dismiss() {
+ getListView().removeOnLayoutChangeListener(mLayoutListener);
+ super.dismiss();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 1f5303f..6626f59 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -35,12 +35,10 @@
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Process;
import android.os.VibrationAttributes;
-import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.MathUtils;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
@@ -68,8 +66,6 @@
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
-import com.airbnb.lottie.LottieAnimationView;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Objects;
@@ -101,10 +97,8 @@
@NonNull private final AccessibilityManager mAccessibilityManager;
@NonNull private final ConfigurationController mConfigurationController;
@NonNull private final DelayableExecutor mExecutor;
- @NonNull private final LayoutInflater mLayoutInflater;
private boolean mUdfpsEnrolled;
- @Nullable private LottieAnimationView mAodFp;
@NonNull private final AnimatedStateListDrawable mIcon;
@NonNull private CharSequence mUnlockedLabel;
@@ -116,7 +110,6 @@
private VelocityTracker mVelocityTracker;
// The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active.
private int mActivePointerId = -1;
- private VibrationEffect mTick;
private boolean mIsDozing;
private boolean mIsBouncerShowing;
@@ -140,7 +133,7 @@
// for udfps when strong auth is required or unlocked on AOD
private boolean mShowAodLockIcon;
- private boolean mShowAODFpIcon;
+ private boolean mShowAodUnlockedIcon;
private final int mMaxBurnInOffsetX;
private final int mMaxBurnInOffsetY;
private float mInterpolatedDarkAmount;
@@ -163,8 +156,7 @@
@NonNull @Main DelayableExecutor executor,
@Nullable Vibrator vibrator,
@Nullable AuthRippleController authRippleController,
- @NonNull @Main Resources resources,
- @NonNull LayoutInflater inflater
+ @NonNull @Main Resources resources
) {
super(view);
mStatusBarStateController = statusBarStateController;
@@ -178,7 +170,6 @@
mExecutor = executor;
mVibrator = vibrator;
mAuthRippleController = authRippleController;
- mLayoutInflater = inflater;
mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
@@ -260,11 +251,12 @@
}
boolean wasShowingUnlock = mShowUnlockIcon;
- boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon;
+ boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon
+ && !mShowAodUnlockedIcon && !mShowAodLockIcon;
mShowLockIcon = !mCanDismissLockScreen && !mUserUnlockedWithBiometric && isLockScreen()
&& (!mUdfpsEnrolled || !mRunningFPS);
mShowUnlockIcon = (mCanDismissLockScreen || mUserUnlockedWithBiometric) && isLockScreen();
- mShowAODFpIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen;
+ mShowAodUnlockedIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen;
mShowAodLockIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && !mCanDismissLockScreen;
final CharSequence prevContentDescription = mView.getContentDescription();
@@ -281,20 +273,11 @@
mView.updateIcon(ICON_UNLOCK, false);
mView.setContentDescription(mUnlockedLabel);
mView.setVisibility(View.VISIBLE);
- } else if (mShowAODFpIcon) {
- // AOD fp icon is special cased as a lottie view (it updates for each burn-in offset),
- // this state shows a transparent view
- mView.setContentDescription(null);
- mAodFp.setVisibility(View.VISIBLE);
- mAodFp.setContentDescription(mCanDismissLockScreen ? mUnlockedLabel : mLockedLabel);
-
- mView.updateIcon(ICON_FINGERPRINT, true); // this shows no icon
+ } else if (mShowAodUnlockedIcon) {
+ mView.updateIcon(ICON_UNLOCK, true);
+ mView.setContentDescription(mUnlockedLabel);
mView.setVisibility(View.VISIBLE);
} else if (mShowAodLockIcon) {
- if (wasShowingUnlock) {
- // transition to the unlock icon first
- mView.updateIcon(ICON_LOCK, false);
- }
mView.updateIcon(ICON_LOCK, true);
mView.setContentDescription(mLockedLabel);
mView.setVisibility(View.VISIBLE);
@@ -304,11 +287,6 @@
mView.setContentDescription(null);
}
- if (!mShowAODFpIcon && mAodFp != null) {
- mAodFp.setVisibility(View.INVISIBLE);
- mAodFp.setContentDescription(null);
- }
-
if (!Objects.equals(prevContentDescription, mView.getContentDescription())
&& mView.getContentDescription() != null) {
mView.announceForAccessibility(mView.getContentDescription());
@@ -396,7 +374,7 @@
pw.println();
pw.println(" mShowUnlockIcon: " + mShowUnlockIcon);
pw.println(" mShowLockIcon: " + mShowLockIcon);
- pw.println(" mShowAODFpIcon: " + mShowAODFpIcon);
+ pw.println(" mShowAodUnlockedIcon: " + mShowAodUnlockedIcon);
pw.println(" mIsDozing: " + mIsDozing);
pw.println(" mIsBouncerShowing: " + mIsBouncerShowing);
pw.println(" mUserUnlockedWithBiometric: " + mUserUnlockedWithBiometric);
@@ -425,13 +403,6 @@
- mMaxBurnInOffsetY, mInterpolatedDarkAmount);
float progress = MathUtils.lerp(0f, getBurnInProgressOffset(), mInterpolatedDarkAmount);
- if (mAodFp != null) {
- mAodFp.setTranslationX(offsetX);
- mAodFp.setTranslationY(offsetY);
- mAodFp.setProgress(progress);
- mAodFp.setAlpha(255 * mInterpolatedDarkAmount);
- }
-
mView.setTranslationX(offsetX);
mView.setTranslationY(offsetY);
}
@@ -444,10 +415,6 @@
mView.setUseBackground(mUdfpsSupported);
mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
- if (!wasUdfpsEnrolled && mUdfpsEnrolled && mAodFp == null) {
- mLayoutInflater.inflate(R.layout.udfps_aod_lock_icon, mView);
- mAodFp = mView.findViewById(R.id.lock_udfps_aod_fp);
- }
if (wasUdfpsSupported != mUdfpsSupported || wasUdfpsEnrolled != mUdfpsEnrolled) {
updateVisibility();
}
@@ -594,15 +561,11 @@
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_HOVER_ENTER:
if (mVibrator != null && !mDownDetected) {
- if (mTick == null) {
- mTick = UdfpsController.lowTick(getContext(), true,
- LONG_PRESS_TIMEOUT);
- }
mVibrator.vibrate(
Process.myUid(),
getContext().getOpPackageName(),
- mTick,
- "lock-icon-tick",
+ UdfpsController.EFFECT_CLICK,
+ "lock-icon-down",
TOUCH_VIBRATION_ATTRIBUTES);
}
@@ -715,8 +678,7 @@
private boolean inLockIconArea(MotionEvent event) {
return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
- && (mView.getVisibility() == View.VISIBLE
- || (mAodFp != null && mAodFp.getVisibility() == View.VISIBLE));
+ && mView.getVisibility() == View.VISIBLE;
}
private boolean isActionable() {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 8c405ca..63962fa 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -16,6 +16,7 @@
package com.android.systemui;
+import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.Application;
import android.app.Notification;
@@ -27,6 +28,7 @@
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
@@ -111,6 +113,13 @@
ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
}
+ // Enable binder tracing on system server for calls originating from SysUI
+ try {
+ ActivityManager.getService().enableBinderTracing();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to enable binder tracing", e);
+ }
+
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 251c1e6..2767904 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -121,7 +121,7 @@
.setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
.setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper())
.setRecentTasks(mWMComponent.getRecentTasks())
- .setSizeCompatUI(Optional.of(mWMComponent.getSizeCompatUI()))
+ .setCompatUI(Optional.of(mWMComponent.getCompatUI()))
.setDragAndDrop(Optional.of(mWMComponent.getDragAndDrop()));
} else {
// TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
@@ -141,7 +141,7 @@
.setStartingSurface(Optional.ofNullable(null))
.setTaskSurfaceHelper(Optional.ofNullable(null))
.setRecentTasks(Optional.ofNullable(null))
- .setSizeCompatUI(Optional.ofNullable(null))
+ .setCompatUI(Optional.ofNullable(null))
.setDragAndDrop(Optional.ofNullable(null));
}
mSysUIComponent = builder.build();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index f11dc93..e35b558 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -636,6 +636,7 @@
mIndicatorView.setText(message);
mIndicatorView.setTextColor(mTextColorError);
mIndicatorView.setVisibility(View.VISIBLE);
+ mIndicatorView.setSelected(true);
mHandler.postDelayed(resetMessageRunnable, mInjector.getDelayAfterError());
Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
index fb4616a..07aec69 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -23,6 +23,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.util.ViewController;
@@ -44,6 +45,7 @@
extends ViewController<T> implements Dumpable {
@NonNull final StatusBarStateController mStatusBarStateController;
@NonNull final PanelExpansionStateManager mPanelExpansionStateManager;
+ @NonNull final SystemUIDialogManager mDialogManager;
@NonNull final DumpManager mDumpManger;
boolean mNotificationShadeVisible;
@@ -52,10 +54,12 @@
T view,
@NonNull StatusBarStateController statusBarStateController,
@NonNull PanelExpansionStateManager panelExpansionStateManager,
+ @NonNull SystemUIDialogManager dialogManager,
@NonNull DumpManager dumpManager) {
super(view);
mStatusBarStateController = statusBarStateController;
mPanelExpansionStateManager = panelExpansionStateManager;
+ mDialogManager = dialogManager;
mDumpManger = dumpManager;
}
@@ -64,12 +68,14 @@
@Override
protected void onViewAttached() {
mPanelExpansionStateManager.addExpansionListener(mPanelExpansionListener);
+ mDialogManager.registerListener(mDialogListener);
mDumpManger.registerDumpable(getDumpTag(), this);
}
@Override
protected void onViewDetached() {
mPanelExpansionStateManager.removeExpansionListener(mPanelExpansionListener);
+ mDialogManager.registerListener(mDialogListener);
mDumpManger.unregisterDumpable(getDumpTag());
}
@@ -95,7 +101,8 @@
* authentication.
*/
boolean shouldPauseAuth() {
- return mNotificationShadeVisible;
+ return mNotificationShadeVisible
+ || mDialogManager.shouldHideAffordance();
}
/**
@@ -189,4 +196,7 @@
updatePauseAuth();
}
};
+
+ private final SystemUIDialogManager.Listener mDialogListener =
+ (shouldHide) -> updatePauseAuth();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
index 894b295..3732100 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
@@ -20,6 +20,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
/**
@@ -30,8 +31,10 @@
@NonNull UdfpsBpView view,
@NonNull StatusBarStateController statusBarStateController,
@NonNull PanelExpansionStateManager panelExpansionStateManager,
+ @NonNull SystemUIDialogManager systemUIDialogManager,
@NonNull DumpManager dumpManager) {
- super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
+ super(view, statusBarStateController, panelExpansionStateManager,
+ systemUIDialogManager, dumpManager);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index c0dcbb0..1da9f21 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -47,7 +47,6 @@
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
-import android.provider.Settings;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -71,6 +70,7 @@
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -119,6 +119,7 @@
@NonNull private final KeyguardStateController mKeyguardStateController;
@NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
@NonNull private final DumpManager mDumpManager;
+ @NonNull private final SystemUIDialogManager mDialogManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Nullable private final Vibrator mVibrator;
@NonNull private final FalsingManager mFalsingManager;
@@ -165,9 +166,6 @@
private boolean mAttemptedToDismissKeyguard;
private Set<Callback> mCallbacks = new HashSet<>();
- private static final int DEFAULT_TICK = VibrationEffect.Composition.PRIMITIVE_LOW_TICK;
- private final VibrationEffect mTick;
-
@VisibleForTesting
public static final VibrationAttributes VIBRATION_ATTRIBUTES =
new VibrationAttributes.Builder()
@@ -281,9 +279,6 @@
return;
}
mGoodCaptureReceived = true;
- if (mVibrator != null) {
- mVibrator.cancel();
- }
mView.stopIllumination();
if (mServerRequest != null) {
mServerRequest.onAcquiredGood();
@@ -558,7 +553,8 @@
@Main Handler mainHandler,
@NonNull ConfigurationController configurationController,
@NonNull SystemClock systemClock,
- @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
+ @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+ @NonNull SystemUIDialogManager dialogManager) {
mContext = context;
mExecution = execution;
mVibrator = vibrator;
@@ -573,6 +569,7 @@
mKeyguardStateController = keyguardStateController;
mKeyguardViewManager = statusBarKeyguardViewManager;
mDumpManager = dumpManager;
+ mDialogManager = dialogManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mFalsingManager = falsingManager;
mPowerManager = powerManager;
@@ -585,7 +582,6 @@
mConfigurationController = configurationController;
mSystemClock = systemClock;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
- mTick = lowTick(context, false /* useShortRampup */, DEFAULT_VIBRATION_DURATION);
mSensorProps = findFirstUdfps();
// At least one UDFPS sensor exists
@@ -621,47 +617,6 @@
}
/**
- * Returns the continuous low tick effect that starts playing on the udfps finger-down event.
- */
- public static VibrationEffect lowTick(
- Context context,
- boolean useShortRampUp,
- long duration
- ) {
- boolean useLowTickDefault = context.getResources()
- .getBoolean(R.bool.config_udfpsUseLowTick);
- int primitiveTick = DEFAULT_TICK;
- if (Settings.Global.getFloat(
- context.getContentResolver(),
- "tick-low", useLowTickDefault ? 1 : 0) == 0) {
- primitiveTick = VibrationEffect.Composition.PRIMITIVE_TICK;
- }
- float tickIntensity = Settings.Global.getFloat(
- context.getContentResolver(),
- "tick-intensity",
- context.getResources().getFloat(R.dimen.config_udfpsTickIntensity));
- int tickDelay = Settings.Global.getInt(
- context.getContentResolver(),
- "tick-delay",
- context.getResources().getInteger(R.integer.config_udfpsTickDelay));
-
- VibrationEffect.Composition composition = VibrationEffect.startComposition();
- composition.addPrimitive(primitiveTick, tickIntensity, 0);
- int primitives = (int) (duration / tickDelay);
- float[] rampUp = new float[]{.48f, .58f, .69f, .83f};
- if (useShortRampUp) {
- rampUp = new float[]{.5f, .7f};
- }
- for (int i = 0; i < rampUp.length; i++) {
- composition.addPrimitive(primitiveTick, tickIntensity * rampUp[i], tickDelay);
- }
- for (int i = rampUp.length; i < primitives; i++) {
- composition.addPrimitive(primitiveTick, tickIntensity, tickDelay);
- }
- return composition.compose();
- }
-
- /**
* Play haptic to signal udfps scanning started.
*/
@VisibleForTesting
@@ -670,8 +625,8 @@
mVibrator.vibrate(
Process.myUid(),
mContext.getOpPackageName(),
- mTick,
- "udfps-onStart-tick",
+ EFFECT_CLICK,
+ "udfps-onStart-click",
VIBRATION_ATTRIBUTES);
}
}
@@ -863,6 +818,7 @@
mServerRequest.mEnrollHelper,
mStatusBarStateController,
mPanelExpansionStateManager,
+ mDialogManager,
mDumpManager
);
case BiometricOverlayConstants.REASON_AUTH_KEYGUARD:
@@ -881,6 +837,7 @@
mSystemClock,
mKeyguardStateController,
mUnlockedScreenOffAnimationController,
+ mDialogManager,
this
);
case BiometricOverlayConstants.REASON_AUTH_BP:
@@ -891,6 +848,7 @@
bpView,
mStatusBarStateController,
mPanelExpansionStateManager,
+ mDialogManager,
mDumpManager
);
case BiometricOverlayConstants.REASON_AUTH_OTHER:
@@ -902,6 +860,7 @@
authOtherView,
mStatusBarStateController,
mPanelExpansionStateManager,
+ mDialogManager,
mDumpManager
);
default:
@@ -1058,7 +1017,6 @@
}
}
mOnFingerDown = false;
- mVibrator.cancel();
if (mView.isIlluminationRequested()) {
mView.stopIllumination();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index 1f01fc5..ac38b13 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -20,9 +20,7 @@
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.Context;
-import android.content.res.TypedArray;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -30,7 +28,6 @@
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
-import android.util.TypedValue;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.LinearInterpolator;
@@ -38,7 +35,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.internal.graphics.ColorUtils;
import com.android.systemui.R;
/**
@@ -106,9 +102,8 @@
mSensorOutlinePaint = new Paint(0 /* flags */);
mSensorOutlinePaint.setAntiAlias(true);
- mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_enroll_icon));
- mSensorOutlinePaint.setStyle(Paint.Style.STROKE);
- mSensorOutlinePaint.setStrokeWidth(2.f);
+ mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_moving_target_fill));
+ mSensorOutlinePaint.setStyle(Paint.Style.FILL);
mBlueFill = new Paint(0 /* flags */);
mBlueFill.setAntiAlias(true);
@@ -117,12 +112,12 @@
mMovingTargetFpIcon = context.getResources()
.getDrawable(R.drawable.ic_kg_fingerprint, null);
- mMovingTargetFpIcon.setTint(Color.WHITE);
+ mMovingTargetFpIcon.setTint(mContext.getColor(R.color.udfps_enroll_icon));
mMovingTargetFpIcon.mutate();
mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon));
- mHintColorFaded = getHintColorFaded(context);
+ mHintColorFaded = context.getColor(R.color.udfps_moving_target_fill);
mHintColorHighlight = context.getColor(R.color.udfps_enroll_progress);
mHintMaxWidthPx = Utils.dpToPixels(context, HINT_MAX_WIDTH_DP);
mHintPaddingPx = Utils.dpToPixels(context, HINT_PADDING_DP);
@@ -218,22 +213,6 @@
};
}
- @ColorInt
- private static int getHintColorFaded(@NonNull Context context) {
- final TypedValue tv = new TypedValue();
- context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, tv, true);
- final int alpha = (int) (tv.getFloat() * 255f);
-
- final int[] attrs = new int[] {android.R.attr.colorControlNormal};
- final TypedArray ta = context.obtainStyledAttributes(attrs);
- try {
- @ColorInt final int color = ta.getColor(0, context.getColor(R.color.white_disabled));
- return ColorUtils.setAlphaComponent(color, alpha);
- } finally {
- ta.recycle();
- }
- }
-
void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
mEnrollHelper = helper;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
index 79c7e66..631a461 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
@@ -18,12 +18,10 @@
import android.animation.ValueAnimator;
import android.content.Context;
-import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
-import android.util.TypedValue;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
@@ -80,24 +78,11 @@
mBackgroundPaint = new Paint();
mBackgroundPaint.setStrokeWidth(mStrokeWidthPx);
- mBackgroundPaint.setColor(context.getColor(R.color.white_disabled));
+ mBackgroundPaint.setColor(context.getColor(R.color.udfps_moving_target_fill));
mBackgroundPaint.setAntiAlias(true);
mBackgroundPaint.setStyle(Paint.Style.STROKE);
mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
- // Set background paint color and alpha.
- final int[] attrs = new int[] {android.R.attr.colorControlNormal};
- final TypedArray typedArray = context.obtainStyledAttributes(attrs);
- try {
- @ColorInt final int tintColor = typedArray.getColor(0, mBackgroundPaint.getColor());
- mBackgroundPaint.setColor(tintColor);
- } finally {
- typedArray.recycle();
- }
- TypedValue alpha = new TypedValue();
- context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, alpha, true);
- mBackgroundPaint.setAlpha((int) (alpha.getFloat() * 255f));
-
// Progress fill should *not* use the extracted system color.
mFillPaint = new Paint();
mFillPaint.setStrokeWidth(mStrokeWidthPx);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
index 292a904..ac9e92e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
@@ -22,6 +22,7 @@
import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
/**
@@ -54,8 +55,10 @@
@NonNull UdfpsEnrollHelper enrollHelper,
@NonNull StatusBarStateController statusBarStateController,
@NonNull PanelExpansionStateManager panelExpansionStateManager,
+ @NonNull SystemUIDialogManager systemUIDialogManager,
@NonNull DumpManager dumpManager) {
- super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
+ super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
+ dumpManager);
mEnrollProgressBarRadius = getContext().getResources()
.getInteger(R.integer.config_udfpsEnrollProgressBar);
mEnrollHelper = enrollHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
index 6198733..97dca0f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
@@ -20,6 +20,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
/**
@@ -33,8 +34,10 @@
@NonNull UdfpsFpmOtherView view,
@NonNull StatusBarStateController statusBarStateController,
@NonNull PanelExpansionStateManager panelExpansionStateManager,
+ @NonNull SystemUIDialogManager systemUIDialogManager,
@NonNull DumpManager dumpManager) {
- super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
+ super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
+ dumpManager);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 8f4d6f6..3e8a568 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -31,6 +31,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
@@ -86,8 +87,10 @@
@NonNull SystemClock systemClock,
@NonNull KeyguardStateController keyguardStateController,
@NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+ @NonNull SystemUIDialogManager systemUIDialogManager,
@NonNull UdfpsController udfpsController) {
- super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
+ super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
+ dumpManager);
mKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockScreenShadeTransitionController = transitionController;
@@ -217,6 +220,10 @@
return false;
}
+ if (mDialogManager.shouldHideAffordance()) {
+ return true;
+ }
+
if (mLaunchTransitionFadingAway) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java
index 53586f5..42ecd5c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java
@@ -35,7 +35,23 @@
* {@link Connector} defines an interface for {@link CommunalSource} instances to be generated.
*/
interface Connector {
- ListenableFuture<Optional<CommunalSource>> connect();
+ Connection connect(Connection.Callback callback);
+ }
+
+ /**
+ * {@link Connection} defines an interface for an entity which holds the necessary components
+ * for establishing and maintaining a connection to the communal source.
+ */
+ interface Connection {
+ /**
+ * {@link Callback} defines an interface for clients to be notified when a source is ready
+ */
+ interface Callback {
+ void onSourceEstablished(Optional<CommunalSource> source);
+ void onDisconnected();
+ }
+
+ void disconnect();
}
/**
@@ -86,29 +102,4 @@
* value will be {@code null} in case of a failure.
*/
ListenableFuture<CommunalViewResult> requestCommunalView(Context context);
-
- /**
- * Adds a {@link Callback} to receive future status updates regarding this
- * {@link CommunalSource}.
- *
- * @param callback The {@link Callback} to be added.
- */
- void addCallback(Callback callback);
-
- /**
- * Removes a {@link Callback} from receiving future updates.
- *
- * @param callback The {@link Callback} to be removed.
- */
- void removeCallback(Callback callback);
-
- /**
- * An interface for receiving updates on the state of the {@link CommunalSource}.
- */
- interface Callback {
- /**
- * Invoked when the {@link CommunalSource} is no longer available for use.
- */
- void onDisconnected();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
index d3018e3..58cf35f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
@@ -16,19 +16,24 @@
package com.android.systemui.communal;
+import static com.android.systemui.communal.dagger.CommunalModule.COMMUNAL_CONDITIONS;
+
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.condition.Monitor;
import com.google.android.collect.Lists;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
+import javax.inject.Named;
/**
* A Monitor for reporting a {@link CommunalSource} presence.
@@ -40,7 +45,8 @@
// A list of {@link Callback} that have registered to receive updates.
private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
- private final CommunalConditionsMonitor mConditionsMonitor;
+ private final Monitor mConditionsMonitor;
+ private final Executor mExecutor;
private CommunalSource mCurrentSource;
@@ -50,15 +56,7 @@
// Whether the class is currently listening for condition changes.
private boolean mListeningForConditions = false;
- private CommunalSource.Callback mSourceCallback = new CommunalSource.Callback() {
- @Override
- public void onDisconnected() {
- // Clear source reference.
- setSource(null /* source */);
- }
- };
-
- private final CommunalConditionsMonitor.Callback mConditionsCallback =
+ private final Monitor.Callback mConditionsCallback =
allConditionsMet -> {
if (mAllCommunalConditionsMet != allConditionsMet) {
if (DEBUG) Log.d(TAG, "communal conditions changed: " + allConditionsMet);
@@ -70,7 +68,9 @@
@VisibleForTesting
@Inject
- public CommunalSourceMonitor(CommunalConditionsMonitor communalConditionsMonitor) {
+ public CommunalSourceMonitor(@Main Executor executor,
+ @Named(COMMUNAL_CONDITIONS) Monitor communalConditionsMonitor) {
+ mExecutor = executor;
mConditionsMonitor = communalConditionsMonitor;
}
@@ -81,35 +81,28 @@
* @param source The new {@link CommunalSource}.
*/
public void setSource(CommunalSource source) {
- if (mCurrentSource != null) {
- mCurrentSource.removeCallback(mSourceCallback);
- }
-
mCurrentSource = source;
if (mAllCommunalConditionsMet) {
executeOnSourceAvailableCallbacks();
}
-
- // Add callback to be informed when the source disconnects.
- if (mCurrentSource != null) {
- mCurrentSource.addCallback(mSourceCallback);
- }
}
private void executeOnSourceAvailableCallbacks() {
- // If the new source is valid, inform registered Callbacks of its presence.
- Iterator<WeakReference<Callback>> itr = mCallbacks.iterator();
- while (itr.hasNext()) {
- Callback cb = itr.next().get();
- if (cb == null) {
- itr.remove();
- } else {
- cb.onSourceAvailable(
- (mAllCommunalConditionsMet && mCurrentSource != null) ? new WeakReference<>(
- mCurrentSource) : null);
+ mExecutor.execute(() -> {
+ // If the new source is valid, inform registered Callbacks of its presence.
+ Iterator<WeakReference<Callback>> itr = mCallbacks.iterator();
+ while (itr.hasNext()) {
+ Callback cb = itr.next().get();
+ if (cb == null) {
+ itr.remove();
+ } else {
+ cb.onSourceAvailable(
+ (mAllCommunalConditionsMet && mCurrentSource != null)
+ ? new WeakReference<>(mCurrentSource) : null);
+ }
}
- }
+ });
}
/**
@@ -118,17 +111,19 @@
* @param callback The {@link Callback} to add.
*/
public void addCallback(Callback callback) {
- mCallbacks.add(new WeakReference<>(callback));
+ mExecutor.execute(() -> {
+ mCallbacks.add(new WeakReference<>(callback));
- // Inform the callback of any already present CommunalSource.
- if (mAllCommunalConditionsMet && mCurrentSource != null) {
- callback.onSourceAvailable(new WeakReference<>(mCurrentSource));
- }
+ // Inform the callback of any already present CommunalSource.
+ if (mAllCommunalConditionsMet && mCurrentSource != null) {
+ callback.onSourceAvailable(new WeakReference<>(mCurrentSource));
+ }
- if (!mListeningForConditions) {
- mConditionsMonitor.addCallback(mConditionsCallback);
- mListeningForConditions = true;
- }
+ if (!mListeningForConditions) {
+ mConditionsMonitor.addCallback(mConditionsCallback);
+ mListeningForConditions = true;
+ }
+ });
}
/**
@@ -137,12 +132,14 @@
* @param callback The {@link Callback} to add.
*/
public void removeCallback(Callback callback) {
- mCallbacks.removeIf(el -> el.get() == callback);
+ mExecutor.execute(() -> {
+ mCallbacks.removeIf(el -> el.get() == callback);
- if (mCallbacks.isEmpty() && mListeningForConditions) {
- mConditionsMonitor.removeCallback(mConditionsCallback);
- mListeningForConditions = false;
- }
+ if (mCallbacks.isEmpty() && mListeningForConditions) {
+ mConditionsMonitor.removeCallback(mConditionsCallback);
+ mListeningForConditions = false;
+ }
+ });
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java
index 1044239..13b1dc0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java
@@ -27,8 +27,6 @@
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
-import com.google.common.util.concurrent.ListenableFuture;
-
import java.util.Optional;
import javax.inject.Inject;
@@ -53,10 +51,11 @@
private int mReconnectAttempts = 0;
private Runnable mCurrentReconnectCancelable;
- private ListenableFuture<Optional<CommunalSource>> mGetSourceFuture;
- private final Optional<CommunalSource.Connector> mConnector;
private final Optional<CommunalSource.Observer> mObserver;
+ private final Optional<CommunalSource.Connector> mConnector;
+
+ private CommunalSource.Connection mCurrentConnection;
private final Runnable mConnectRunnable = new Runnable() {
@Override
@@ -66,6 +65,10 @@
}
};
+ private final CommunalSource.Observer.Callback mObserverCallback = () -> {
+ initiateConnectionAttempt();
+ };
+
@Inject
public CommunalSourcePrimer(Context context, @Main Resources resources,
SystemClock clock,
@@ -132,7 +135,7 @@
@Override
protected void onBootCompleted() {
if (mObserver.isPresent()) {
- mObserver.get().addCallback(() -> initiateConnectionAttempt());
+ mObserver.get().addCallback(mObserverCallback);
}
initiateConnectionAttempt();
}
@@ -142,34 +145,36 @@
Log.d(TAG, "attempting to communal to communal source");
}
- if (mGetSourceFuture != null) {
+ if (mCurrentConnection != null) {
if (DEBUG) {
Log.d(TAG, "canceling in-flight connection");
}
- mGetSourceFuture.cancel(true);
+ mCurrentConnection.disconnect();
}
- mGetSourceFuture = mConnector.get().connect();
- mGetSourceFuture.addListener(() -> {
- try {
- final long startTime = mSystemClock.currentTimeMillis();
- Optional<CommunalSource> result = mGetSourceFuture.get();
- if (result.isPresent()) {
- final CommunalSource source = result.get();
- source.addCallback(() -> {
- if (mSystemClock.currentTimeMillis() - startTime > mMinConnectionDuration) {
- initiateConnectionAttempt();
- } else {
- scheduleConnectionAttempt();
- }
- });
+ mCurrentConnection = mConnector.get().connect(new CommunalSource.Connection.Callback() {
+ private long mStartTime;
+
+ @Override
+ public void onSourceEstablished(Optional<CommunalSource> optionalSource) {
+ mStartTime = mSystemClock.currentTimeMillis();
+
+ if (optionalSource.isPresent()) {
+ final CommunalSource source = optionalSource.get();
mMonitor.setSource(source);
} else {
scheduleConnectionAttempt();
}
- } catch (Exception e) {
- e.printStackTrace();
}
- }, mMainExecutor);
+
+ @Override
+ public void onDisconnected() {
+ if (mSystemClock.currentTimeMillis() - mStartTime > mMinConnectionDuration) {
+ initiateConnectionAttempt();
+ } else {
+ scheduleConnectionAttempt();
+ }
+ }
+ });
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java
deleted file mode 100644
index 1197816..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java
+++ /dev/null
@@ -1,42 +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.
- */
-
-package com.android.systemui.communal.conditions;
-
-
-import static com.android.systemui.communal.dagger.CommunalModule.COMMUNAL_CONDITIONS;
-
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.util.condition.Condition;
-import com.android.systemui.util.condition.Monitor;
-
-import java.util.Set;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-/**
- * A concrete implementation of {@Monitor} with conditions for monitoring when communal mode should
- * be enabled.
- */
-@SysUISingleton
-public class CommunalConditionsMonitor extends Monitor {
- @Inject
- public CommunalConditionsMonitor(
- @Named(COMMUNAL_CONDITIONS) Set<Condition> communalConditions) {
- super(communalConditions);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
index f27ae34..e1f1ac4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
@@ -34,6 +34,8 @@
import com.android.systemui.idle.LightSensorEventsDebounceAlgorithm;
import com.android.systemui.idle.dagger.IdleViewComponent;
import com.android.systemui.util.condition.Condition;
+import com.android.systemui.util.condition.Monitor;
+import com.android.systemui.util.condition.dagger.MonitorComponent;
import java.util.Collections;
import java.util.HashSet;
@@ -135,4 +137,14 @@
return Optional.empty();
}
}
+
+ /** */
+ @Provides
+ @Named(COMMUNAL_CONDITIONS)
+ static Monitor provideCommunalSourceMonitor(
+ @Named(COMMUNAL_CONDITIONS) Set<Condition> communalConditions,
+ MonitorComponent.Factory factory) {
+ final MonitorComponent component = factory.create(communalConditions, new HashSet<>());
+ return component.getMonitor();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 38e4d78..e337678 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -24,6 +24,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.media.taptotransfer.MediaTttChipController;
+import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
import com.android.systemui.people.PeopleProvider;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
@@ -32,6 +33,7 @@
import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.compatui.CompatUI;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
import com.android.wm.shell.draganddrop.DragAndDrop;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
@@ -39,7 +41,6 @@
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.recents.RecentTasks;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
@@ -111,7 +112,7 @@
Builder setRecentTasks(Optional<RecentTasks> r);
@BindsInstance
- Builder setSizeCompatUI(Optional<SizeCompatUI> s);
+ Builder setCompatUI(Optional<CompatUI> s);
@BindsInstance
Builder setDragAndDrop(Optional<DragAndDrop> d);
@@ -133,6 +134,7 @@
getNaturalRotationUnfoldProgressProvider().ifPresent(o -> o.init());
// No init method needed, just needs to be gotten so that it's created.
getMediaTttChipController();
+ getMediaTttCommandLineHelper();
}
/**
@@ -182,6 +184,9 @@
/** */
Optional<MediaTttChipController> getMediaTttChipController();
+ /** */
+ Optional<MediaTttCommandLineHelper> getMediaTttCommandLineHelper();
+
/**
* Member injection into the supplied argument.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 90a3ad2..b815d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -25,6 +25,7 @@
import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.compatui.CompatUI;
import com.android.wm.shell.dagger.TvWMShellModule;
import com.android.wm.shell.dagger.WMShellModule;
import com.android.wm.shell.dagger.WMSingleton;
@@ -35,7 +36,6 @@
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.recents.RecentTasks;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
@@ -119,7 +119,7 @@
Optional<RecentTasks> getRecentTasks();
@WMSingleton
- SizeCompatUI getSizeCompatUI();
+ CompatUI getCompatUI();
@WMSingleton
DragAndDrop getDragAndDrop();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 471a327..b2fe3bb 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -37,10 +37,14 @@
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.unfold.FoldAodAnimationController;
+import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.util.AlarmTimeout;
import com.android.systemui.util.wakelock.WakeLock;
import java.util.Calendar;
+import java.util.Optional;
import javax.inject.Inject;
@@ -49,7 +53,8 @@
*/
@DozeScope
public class DozeUi implements DozeMachine.Part, TunerService.Tunable,
- ConfigurationController.ConfigurationListener, StatusBarStateController.StateListener {
+ ConfigurationController.ConfigurationListener, FoldAodAnimationStatus,
+ StatusBarStateController.StateListener {
// if enabled, calls dozeTimeTick() whenever the time changes:
private static final boolean BURN_IN_TESTING_ENABLED = false;
private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min
@@ -57,6 +62,7 @@
private final DozeHost mHost;
private final Handler mHandler;
private final WakeLock mWakeLock;
+ private final FoldAodAnimationController mFoldAodAnimationController;
private DozeMachine mMachine;
private final AlarmTimeout mTimeTicker;
private final boolean mCanAnimateTransition;
@@ -100,6 +106,7 @@
DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor,
DozeLog dozeLog, TunerService tunerService,
StatusBarStateController statusBarStateController,
+ Optional<SysUIUnfoldComponent> sysUiUnfoldComponent,
ConfigurationController configurationController) {
mContext = context;
mWakeLock = wakeLock;
@@ -118,12 +125,23 @@
mConfigurationController = configurationController;
mConfigurationController.addCallback(this);
+
+ mFoldAodAnimationController = sysUiUnfoldComponent
+ .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
+
+ if (mFoldAodAnimationController != null) {
+ mFoldAodAnimationController.addCallback(this);
+ }
}
@Override
public void destroy() {
mTunerService.removeTunable(this);
mConfigurationController.removeCallback(this);
+
+ if (mFoldAodAnimationController != null) {
+ mFoldAodAnimationController.removeCallback(this);
+ }
}
@Override
@@ -142,7 +160,8 @@
&& (mKeyguardShowing || mDozeParameters.shouldControlUnlockedScreenOff())
&& !mHost.isPowerSaveActive();
mDozeParameters.setControlScreenOffAnimation(controlScreenOff);
- mHost.setAnimateScreenOff(controlScreenOff);
+ mHost.setAnimateScreenOff(controlScreenOff
+ && mDozeParameters.shouldAnimateDozingChange());
}
}
@@ -299,4 +318,9 @@
public void onStatePostChange() {
updateAnimateScreenOff();
}
+
+ @Override
+ public void onFoldToAodAnimationChanged() {
+ updateAnimateScreenOff();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerView.java
new file mode 100644
index 0000000..bc1f772
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerView.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.dreams;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+/**
+ * {@link DreamOverlayContainerView} contains a dream overlay and its status bar.
+ */
+public class DreamOverlayContainerView extends ConstraintLayout {
+ public DreamOverlayContainerView(Context context) {
+ this(context, null);
+ }
+
+ public DreamOverlayContainerView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DreamOverlayContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public DreamOverlayContainerView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 8f0ea2f..393f039 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -29,11 +29,11 @@
import android.view.WindowManager;
import androidx.annotation.NonNull;
-import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.PhoneWindow;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import java.util.concurrent.Executor;
@@ -54,57 +54,75 @@
private final Executor mExecutor;
// The state controller informs the service of updates to the overlays present.
private final DreamOverlayStateController mStateController;
+ // The component used to resolve dream overlay dependencies.
+ private final DreamOverlayComponent mDreamOverlayComponent;
- // The window is populated once the dream informs the service it has begun dreaming.
- private Window mWindow;
- private ConstraintLayout mLayout;
+ // The dream overlay's content view, which is located below the status bar (in z-order) and is
+ // the space into which widgets are placed.
+ private ViewGroup mDreamOverlayContentView;
private final DreamOverlayStateController.Callback mOverlayStateCallback =
new DreamOverlayStateController.Callback() {
- @Override
- public void onOverlayChanged() {
- mExecutor.execute(() -> reloadOverlaysLocked());
- }
- };
+ @Override
+ public void onOverlayChanged() {
+ mExecutor.execute(() -> reloadOverlaysLocked());
+ }
+ };
// The service listens to view changes in order to declare that input occurring in areas outside
// the overlay should be passed through to the dream underneath.
- private View.OnAttachStateChangeListener mRootViewAttachListener =
+ private final View.OnAttachStateChangeListener mRootViewAttachListener =
new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- v.getViewTreeObserver()
- .addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
- }
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ v.getViewTreeObserver()
+ .addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
+ }
- @Override
- public void onViewDetachedFromWindow(View v) {
- v.getViewTreeObserver()
- .removeOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
- }
- };
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ v.getViewTreeObserver()
+ .removeOnComputeInternalInsetsListener(
+ mOnComputeInternalInsetsListener);
+ }
+ };
// A hook into the internal inset calculation where we declare the overlays as the only
// touchable regions.
- private ViewTreeObserver.OnComputeInternalInsetsListener mOnComputeInternalInsetsListener =
+ private final ViewTreeObserver.OnComputeInternalInsetsListener
+ mOnComputeInternalInsetsListener =
new ViewTreeObserver.OnComputeInternalInsetsListener() {
- @Override
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
- if (mLayout != null) {
- inoutInfo.setTouchableInsets(
- ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- final Region region = new Region();
- for (int i = 0; i < mLayout.getChildCount(); i++) {
- View child = mLayout.getChildAt(i);
- final Rect rect = new Rect();
- child.getGlobalVisibleRect(rect);
- region.op(rect, Region.Op.UNION);
- }
+ @Override
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
+ if (mDreamOverlayContentView != null) {
+ inoutInfo.setTouchableInsets(
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ final Region region = new Region();
+ for (int i = 0; i < mDreamOverlayContentView.getChildCount(); i++) {
+ View child = mDreamOverlayContentView.getChildAt(i);
+ final Rect rect = new Rect();
+ child.getGlobalVisibleRect(rect);
+ region.op(rect, Region.Op.UNION);
+ }
- inoutInfo.touchableRegion.set(region);
- }
- }
- };
+ inoutInfo.touchableRegion.set(region);
+ }
+ }
+ };
+
+ @Inject
+ public DreamOverlayService(
+ Context context,
+ @Main Executor executor,
+ DreamOverlayStateController overlayStateController,
+ DreamOverlayComponent.Factory dreamOverlayComponentFactory) {
+ mContext = context;
+ mExecutor = executor;
+ mStateController = overlayStateController;
+ mDreamOverlayComponent = dreamOverlayComponentFactory.create();
+
+ mStateController.addCallback(mOverlayStateCallback);
+ }
@Override
public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) {
@@ -112,10 +130,10 @@
}
private void reloadOverlaysLocked() {
- if (mLayout == null) {
+ if (mDreamOverlayContentView == null) {
return;
}
- mLayout.removeAllViews();
+ mDreamOverlayContentView.removeAllViews();
for (OverlayProvider overlayProvider : mStateController.getOverlays()) {
addOverlay(overlayProvider);
}
@@ -129,31 +147,32 @@
* into the dream window.
*/
private void addOverlayWindowLocked(WindowManager.LayoutParams layoutParams) {
- mWindow = new PhoneWindow(mContext);
- mWindow.setAttributes(layoutParams);
- mWindow.setWindowManager(null, layoutParams.token, "DreamOverlay", true);
+ final PhoneWindow window = new PhoneWindow(mContext);
+ window.setAttributes(layoutParams);
+ window.setWindowManager(null, layoutParams.token, "DreamOverlay", true);
- mWindow.setBackgroundDrawable(new ColorDrawable(0));
+ window.setBackgroundDrawable(new ColorDrawable(0));
- mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
- mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+ window.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+ window.requestFeature(Window.FEATURE_NO_TITLE);
// Hide all insets when the dream is showing
- mWindow.getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars());
- mWindow.setDecorFitsSystemWindows(false);
+ window.getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars());
+ window.setDecorFitsSystemWindows(false);
if (DEBUG) {
Log.d(TAG, "adding overlay window to dream");
}
- mLayout = new ConstraintLayout(mContext);
- mLayout.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
- mLayout.addOnAttachStateChangeListener(mRootViewAttachListener);
- mWindow.setContentView(mLayout);
+ window.setContentView(mDreamOverlayComponent.getDreamOverlayContainerView());
+
+ mDreamOverlayContentView = mDreamOverlayComponent.getDreamOverlayContentView();
+ mDreamOverlayContentView.addOnAttachStateChangeListener(mRootViewAttachListener);
+
+ mDreamOverlayComponent.getDreamOverlayStatusBarViewController().init();
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
- windowManager.addView(mWindow.getDecorView(), mWindow.getAttributes());
+ windowManager.addView(window.getDecorView(), window.getAttributes());
mExecutor.execute(this::reloadOverlaysLocked);
}
@@ -163,11 +182,11 @@
(view, layoutParams) -> {
// Always move UI related work to the main thread.
mExecutor.execute(() -> {
- if (mLayout == null) {
+ if (mDreamOverlayContentView == null) {
return;
}
- mLayout.addView(view, layoutParams);
+ mDreamOverlayContentView.addView(view, layoutParams);
});
},
() -> {
@@ -178,15 +197,6 @@
});
}
- @Inject
- public DreamOverlayService(Context context, @Main Executor executor,
- DreamOverlayStateController overlayStateController) {
- mContext = context;
- mExecutor = executor;
- mStateController = overlayStateController;
- mStateController.addCallback(mOverlayStateCallback);
- }
-
@Override
public void onDestroy() {
mStateController.removeCallback(mOverlayStateCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
new file mode 100644
index 0000000..9847ef6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.dreams;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.internal.util.Preconditions;
+import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterView;
+import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
+
+/**
+ * {@link DreamOverlayStatusBarView} is the view responsible for displaying the status bar in a
+ * dream. The status bar includes status icons such as battery and wifi.
+ */
+public class DreamOverlayStatusBarView extends ConstraintLayout implements
+ BatteryStateChangeCallback {
+
+ private BatteryMeterView mBatteryView;
+ private ImageView mWifiStatusView;
+
+ public DreamOverlayStatusBarView(Context context) {
+ this(context, null);
+ }
+
+ public DreamOverlayStatusBarView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DreamOverlayStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public DreamOverlayStatusBarView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mBatteryView = Preconditions.checkNotNull(findViewById(R.id.dream_overlay_battery),
+ "R.id.dream_overlay_battery must not be null");
+ mWifiStatusView = Preconditions.checkNotNull(findViewById(R.id.dream_overlay_wifi_status),
+ "R.id.dream_overlay_wifi_status must not be null");
+
+ mWifiStatusView.setImageDrawable(getContext().getDrawable(R.drawable.ic_signal_wifi_off));
+ }
+
+ /**
+ * Whether to show the battery percent text next to the battery status icons.
+ * @param show True if the battery percent text should be shown.
+ */
+ void showBatteryPercentText(boolean show) {
+ mBatteryView.setForceShowPercent(show);
+ }
+
+ /**
+ * Whether to show the wifi status icon.
+ * @param show True if the wifi status icon should be shown.
+ */
+ void showWifiStatus(boolean show) {
+ // Only show the wifi status icon when wifi isn't available.
+ mWifiStatusView.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
new file mode 100644
index 0000000..5674b9f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.dreams;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+
+import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.dreams.dagger.DreamOverlayComponent;
+import com.android.systemui.dreams.dagger.DreamOverlayModule;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.util.ViewController;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * View controller for {@link DreamOverlayStatusBarView}.
+ */
+@DreamOverlayComponent.DreamOverlayScope
+public class DreamOverlayStatusBarViewController extends ViewController<DreamOverlayStatusBarView> {
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "WIFI_STATUS_" }, value = {
+ WIFI_STATUS_UNKNOWN,
+ WIFI_STATUS_UNAVAILABLE,
+ WIFI_STATUS_AVAILABLE
+ })
+ private @interface WifiStatus {}
+ private static final int WIFI_STATUS_UNKNOWN = 0;
+ private static final int WIFI_STATUS_UNAVAILABLE = 1;
+ private static final int WIFI_STATUS_AVAILABLE = 2;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "BATTERY_STATUS_" }, value = {
+ BATTERY_STATUS_UNKNOWN,
+ BATTERY_STATUS_NOT_CHARGING,
+ BATTERY_STATUS_CHARGING
+ })
+ private @interface BatteryStatus {}
+ private static final int BATTERY_STATUS_UNKNOWN = 0;
+ private static final int BATTERY_STATUS_NOT_CHARGING = 1;
+ private static final int BATTERY_STATUS_CHARGING = 2;
+
+ private final BatteryController mBatteryController;
+ private final BatteryMeterViewController mBatteryMeterViewController;
+ private final ConnectivityManager mConnectivityManager;
+ private final boolean mShowPercentAvailable;
+
+ private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
+
+ private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+ @Override
+ public void onCapabilitiesChanged(
+ Network network, NetworkCapabilities networkCapabilities) {
+ onWifiAvailabilityChanged(
+ networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
+ }
+
+ @Override
+ public void onAvailable(Network network) {
+ onWifiAvailabilityChanged(true);
+ }
+
+ @Override
+ public void onLost(Network network) {
+ onWifiAvailabilityChanged(false);
+ }
+ };
+
+ private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback =
+ new BatteryController.BatteryStateChangeCallback() {
+ @Override
+ public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+ DreamOverlayStatusBarViewController.this.onBatteryLevelChanged(charging);
+ }
+ };
+
+ private @WifiStatus int mWifiStatus = WIFI_STATUS_UNKNOWN;
+ private @BatteryStatus int mBatteryStatus = BATTERY_STATUS_UNKNOWN;
+
+ @Inject
+ public DreamOverlayStatusBarViewController(
+ Context context,
+ DreamOverlayStatusBarView view,
+ BatteryController batteryController,
+ @Named(DreamOverlayModule.DREAM_OVERLAY_BATTERY_CONTROLLER)
+ BatteryMeterViewController batteryMeterViewController,
+ ConnectivityManager connectivityManager) {
+ super(view);
+ mBatteryController = batteryController;
+ mBatteryMeterViewController = batteryMeterViewController;
+ mConnectivityManager = connectivityManager;
+
+ mShowPercentAvailable = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_battery_percentage_setting_available);
+ }
+
+ @Override
+ protected void onInit() {
+ super.onInit();
+ mBatteryMeterViewController.init();
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mBatteryController.addCallback(mBatteryStateChangeCallback);
+ mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
+
+ NetworkCapabilities capabilities =
+ mConnectivityManager.getNetworkCapabilities(
+ mConnectivityManager.getActiveNetwork());
+ onWifiAvailabilityChanged(
+ capabilities != null
+ && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mBatteryController.removeCallback(mBatteryStateChangeCallback);
+ mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+ }
+
+ /**
+ * Wifi availability has changed. Update the wifi status icon as appropriate.
+ * @param available Whether wifi is available.
+ */
+ private void onWifiAvailabilityChanged(boolean available) {
+ final int newWifiStatus = available ? WIFI_STATUS_AVAILABLE : WIFI_STATUS_UNAVAILABLE;
+ if (mWifiStatus != newWifiStatus) {
+ mWifiStatus = newWifiStatus;
+ mView.showWifiStatus(mWifiStatus == WIFI_STATUS_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * The battery level has changed. Update the battery status icon as appropriate.
+ * @param charging Whether the battery is currently charging.
+ */
+ private void onBatteryLevelChanged(boolean charging) {
+ final int newBatteryStatus =
+ charging ? BATTERY_STATUS_CHARGING : BATTERY_STATUS_NOT_CHARGING;
+ if (mBatteryStatus != newBatteryStatus) {
+ mBatteryStatus = newBatteryStatus;
+ mView.showBatteryPercentText(
+ mBatteryStatus == BATTERY_STATUS_CHARGING && mShowPercentAvailable);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index 7bf2361..ff5beb5 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -21,8 +21,6 @@
/**
* Dagger Module providing Communal-related functionality.
*/
-@Module(subcomponents = {
- AppWidgetOverlayComponent.class,
-})
+@Module(subcomponents = {AppWidgetOverlayComponent.class, DreamOverlayComponent.class})
public interface DreamModule {
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
new file mode 100644
index 0000000..a3a446a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.dreams.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.view.ViewGroup;
+
+import com.android.systemui.dreams.DreamOverlayContainerView;
+import com.android.systemui.dreams.DreamOverlayStatusBarViewController;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Scope;
+
+import dagger.Subcomponent;
+
+/**
+ * Dagger subcomponent for {@link DreamOverlayModule}.
+ */
+@Subcomponent(modules = {DreamOverlayModule.class})
+@DreamOverlayComponent.DreamOverlayScope
+public interface DreamOverlayComponent {
+ /** Simple factory for {@link DreamOverlayComponent}. */
+ @Subcomponent.Factory
+ interface Factory {
+ DreamOverlayComponent create();
+ }
+
+ /** Scope annotation for singleton items within the {@link DreamOverlayComponent}. */
+ @Documented
+ @Retention(RUNTIME)
+ @Scope
+ @interface DreamOverlayScope {}
+
+ /** Builds a {@link DreamOverlayContainerView} */
+ @DreamOverlayScope
+ DreamOverlayContainerView getDreamOverlayContainerView();
+
+ /** Builds a content view for dream overlays */
+ @DreamOverlayScope
+ ViewGroup getDreamOverlayContentView();
+
+ /** Builds a {@link DreamOverlayStatusBarViewController}. */
+ @DreamOverlayScope
+ DreamOverlayStatusBarViewController getDreamOverlayStatusBarViewController();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
new file mode 100644
index 0000000..d0a8fad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.dreams.dagger;
+
+import android.content.ContentResolver;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import com.android.internal.util.Preconditions;
+import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterView;
+import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.DreamOverlayContainerView;
+import com.android.systemui.dreams.DreamOverlayStatusBarView;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.tuner.TunerService;
+
+import javax.inject.Named;
+
+import dagger.Module;
+import dagger.Provides;
+
+/** Dagger module for {@link DreamOverlayComponent}. */
+@Module
+public abstract class DreamOverlayModule {
+ private static final String DREAM_OVERLAY_BATTERY_VIEW = "dream_overlay_battery_view";
+ public static final String DREAM_OVERLAY_BATTERY_CONTROLLER =
+ "dream_overlay_battery_controller";
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ public static DreamOverlayContainerView providesDreamOverlayContainerView(
+ LayoutInflater layoutInflater) {
+ return Preconditions.checkNotNull((DreamOverlayContainerView)
+ layoutInflater.inflate(R.layout.dream_overlay_container, null),
+ "R.layout.dream_layout_container could not be properly inflated");
+ }
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ public static ViewGroup providesDreamOverlayContentView(DreamOverlayContainerView view) {
+ return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_content),
+ "R.id.dream_overlay_content must not be null");
+ }
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ public static DreamOverlayStatusBarView providesDreamOverlayStatusBarView(
+ DreamOverlayContainerView view) {
+ return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_status_bar),
+ "R.id.status_bar must not be null");
+ }
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ @Named(DREAM_OVERLAY_BATTERY_VIEW)
+ static BatteryMeterView providesBatteryMeterView(DreamOverlayContainerView view) {
+ return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_battery),
+ "R.id.battery must not be null");
+ }
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ @Named(DREAM_OVERLAY_BATTERY_CONTROLLER)
+ static BatteryMeterViewController providesBatteryMeterViewController(
+ @Named(DREAM_OVERLAY_BATTERY_VIEW) BatteryMeterView batteryMeterView,
+ ConfigurationController configurationController,
+ TunerService tunerService,
+ BroadcastDispatcher broadcastDispatcher,
+ @Main Handler mainHandler,
+ ContentResolver contentResolver,
+ BatteryController batteryController) {
+ return new BatteryMeterViewController(
+ batteryMeterView,
+ configurationController,
+ tunerService,
+ broadcastDispatcher,
+ mainHandler,
+ contentResolver,
+ batteryController);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
new file mode 100644
index 0000000..96a90df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.flags
+
+/**
+ * Class to manage simple DeviceConfig-based feature flags.
+ *
+ * See [Flags] for instructions on defining new flags.
+ */
+interface FeatureFlags : FlagListenable {
+ /** Returns a boolean value for the given flag. */
+ fun isEnabled(flag: BooleanFlag): Boolean
+
+ /** Returns a boolean value for the given flag. */
+ fun isEnabled(flag: ResourceBooleanFlag): Boolean
+
+ /** Returns a string value for the given flag. */
+ fun getString(flag: StringFlag): String
+
+ /** Returns a string value for the given flag. */
+ fun getString(flag: ResourceStringFlag): String
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
index 3f00b87..89623f4 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
@@ -18,11 +18,12 @@
import static com.android.systemui.flags.FlagManager.ACTION_GET_FLAGS;
import static com.android.systemui.flags.FlagManager.ACTION_SET_FLAG;
-import static com.android.systemui.flags.FlagManager.FIELD_FLAGS;
-import static com.android.systemui.flags.FlagManager.FIELD_ID;
-import static com.android.systemui.flags.FlagManager.FIELD_VALUE;
+import static com.android.systemui.flags.FlagManager.EXTRA_FLAGS;
+import static com.android.systemui.flags.FlagManager.EXTRA_ID;
+import static com.android.systemui.flags.FlagManager.EXTRA_VALUE;
-import android.annotation.Nullable;
+import static java.util.Objects.requireNonNull;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -32,6 +33,7 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
@@ -39,14 +41,13 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.settings.SecureSettings;
-import org.json.JSONException;
-import org.json.JSONObject;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
+import java.util.TreeMap;
+import java.util.function.Supplier;
import javax.inject.Inject;
@@ -66,7 +67,9 @@
private final FlagManager mFlagManager;
private final SecureSettings mSecureSettings;
private final Resources mResources;
- private final Map<Integer, Boolean> mBooleanFlagCache = new HashMap<>();
+ private final Supplier<Map<Integer, Flag<?>>> mFlagsCollector;
+ private final Map<Integer, Boolean> mBooleanFlagCache = new TreeMap<>();
+ private final Map<Integer, String> mStringFlagCache = new TreeMap<>();
@Inject
public FeatureFlagsDebug(
@@ -74,99 +77,135 @@
Context context,
SecureSettings secureSettings,
@Main Resources resources,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ @Nullable Supplier<Map<Integer, Flag<?>>> flagsCollector) {
mFlagManager = flagManager;
mSecureSettings = secureSettings;
mResources = resources;
+ mFlagsCollector = flagsCollector != null ? flagsCollector : Flags::collectFlags;
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_SET_FLAG);
filter.addAction(ACTION_GET_FLAGS);
+ flagManager.setRestartAction(this::restartSystemUI);
+ flagManager.setClearCacheAction(this::removeFromCache);
context.registerReceiver(mReceiver, filter, null, null);
dumpManager.registerDumpable(TAG, this);
}
@Override
- public boolean isEnabled(BooleanFlag flag) {
+ public boolean isEnabled(@NonNull BooleanFlag flag) {
int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
- mBooleanFlagCache.put(id, isEnabled(id, flag.getDefault()));
+ mBooleanFlagCache.put(id,
+ readFlagValue(id, flag.getDefault(), BooleanFlagSerializer.INSTANCE));
}
return mBooleanFlagCache.get(id);
}
@Override
- public boolean isEnabled(ResourceBooleanFlag flag) {
+ public boolean isEnabled(@NonNull ResourceBooleanFlag flag) {
int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
- mBooleanFlagCache.put(
- id, isEnabled(id, mResources.getBoolean(flag.getResourceId())));
+ mBooleanFlagCache.put(id,
+ readFlagValue(id, mResources.getBoolean(flag.getResourceId()),
+ BooleanFlagSerializer.INSTANCE));
}
return mBooleanFlagCache.get(id);
}
- /** Return a {@link BooleanFlag}'s value. */
+ @NonNull
@Override
- public boolean isEnabled(int id, boolean defaultValue) {
- Boolean result = isEnabledInternal(id);
+ public String getString(@NonNull StringFlag flag) {
+ int id = flag.getId();
+ if (!mStringFlagCache.containsKey(id)) {
+ mStringFlagCache.put(id,
+ readFlagValue(id, flag.getDefault(), StringFlagSerializer.INSTANCE));
+ }
+
+ return mStringFlagCache.get(id);
+ }
+
+ @NonNull
+ @Override
+ public String getString(@NonNull ResourceStringFlag flag) {
+ int id = flag.getId();
+ if (!mStringFlagCache.containsKey(id)) {
+ mStringFlagCache.put(id,
+ readFlagValue(id, mResources.getString(flag.getResourceId()),
+ StringFlagSerializer.INSTANCE));
+ }
+
+ return mStringFlagCache.get(id);
+ }
+
+ @NonNull
+ private <T> T readFlagValue(int id, @NonNull T defaultValue, FlagSerializer<T> serializer) {
+ requireNonNull(defaultValue, "defaultValue");
+ T result = readFlagValueInternal(id, serializer);
return result == null ? defaultValue : result;
}
/** Returns the stored value or null if not set. */
- private Boolean isEnabledInternal(int id) {
+ @Nullable
+ private <T> T readFlagValueInternal(int id, FlagSerializer<T> serializer) {
try {
- return mFlagManager.isEnabled(id);
+ return mFlagManager.readFlagValue(id, serializer);
} catch (Exception e) {
eraseInternal(id);
}
return null;
}
- /** Set whether a given {@link BooleanFlag} is enabled or not. */
- public void setEnabled(int id, boolean value) {
- Boolean currentValue = isEnabledInternal(id);
- if (currentValue != null && currentValue == value) {
+ private <T> void setFlagValue(int id, @NonNull T value, FlagSerializer<T> serializer) {
+ requireNonNull(value, "Cannot set a null value");
+ T currentValue = readFlagValueInternal(id, serializer);
+ if (Objects.equals(currentValue, value)) {
+ Log.i(TAG, "Flag id " + id + " is already " + value);
return;
}
-
- JSONObject json = new JSONObject();
- try {
- json.put(FlagManager.FIELD_TYPE, FlagManager.TYPE_BOOLEAN);
- json.put(FIELD_VALUE, value);
- mSecureSettings.putString(mFlagManager.keyToSettingsPrefix(id), json.toString());
- Log.i(TAG, "Set id " + id + " to " + value);
- restartSystemUI();
- } catch (JSONException e) {
- // no-op
+ final String data = serializer.toSettingsData(value);
+ if (data == null) {
+ Log.w(TAG, "Failed to set id " + id + " to " + value);
+ return;
}
+ mSecureSettings.putString(mFlagManager.idToSettingsKey(id), data);
+ Log.i(TAG, "Set id " + id + " to " + value);
+ removeFromCache(id);
+ mFlagManager.dispatchListenersAndMaybeRestart(id);
}
/** Erase a flag's overridden value if there is one. */
public void eraseFlag(int id) {
eraseInternal(id);
- restartSystemUI();
+ removeFromCache(id);
+ mFlagManager.dispatchListenersAndMaybeRestart(id);
}
/** Works just like {@link #eraseFlag(int)} except that it doesn't restart SystemUI. */
private void eraseInternal(int id) {
// We can't actually "erase" things from sysprops, but we can set them to empty!
- mSecureSettings.putString(mFlagManager.keyToSettingsPrefix(id), "");
+ mSecureSettings.putString(mFlagManager.idToSettingsKey(id), "");
Log.i(TAG, "Erase id " + id);
}
@Override
- public void addListener(Listener run) {
- mFlagManager.addListener(run);
+ public void addListener(@NonNull Flag<?> flag, @NonNull Listener listener) {
+ mFlagManager.addListener(flag, listener);
}
@Override
- public void removeListener(Listener run) {
- mFlagManager.removeListener(run);
+ public void removeListener(@NonNull Listener listener) {
+ mFlagManager.removeListener(listener);
}
- private void restartSystemUI() {
+ private void restartSystemUI(boolean requestSuppress) {
+ if (requestSuppress) {
+ Log.i(TAG, "SystemUI Restart Suppressed");
+ return;
+ }
Log.i(TAG, "Restarting SystemUI");
// SysUI starts back when up exited. Is there a better way to do this?
System.exit(0);
@@ -175,14 +214,14 @@
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
+ String action = intent == null ? null : intent.getAction();
if (action == null) {
return;
}
if (ACTION_SET_FLAG.equals(action)) {
handleSetFlag(intent.getExtras());
} else if (ACTION_GET_FLAGS.equals(action)) {
- Map<Integer, Flag<?>> knownFlagMap = Flags.collectFlags();
+ Map<Integer, Flag<?>> knownFlagMap = mFlagsCollector.get();
ArrayList<Flag<?>> flags = new ArrayList<>(knownFlagMap.values());
// Convert all flags to parcelable flags.
@@ -196,32 +235,47 @@
Bundle extras = getResultExtras(true);
if (extras != null) {
- extras.putParcelableArrayList(FIELD_FLAGS, pFlags);
+ extras.putParcelableArrayList(EXTRA_FLAGS, pFlags);
}
}
}
private void handleSetFlag(Bundle extras) {
- int id = extras.getInt(FIELD_ID);
+ if (extras == null) {
+ Log.w(TAG, "No extras");
+ return;
+ }
+ int id = extras.getInt(EXTRA_ID);
if (id <= 0) {
Log.w(TAG, "ID not set or less than or equal to 0: " + id);
return;
}
- Map<Integer, Flag<?>> flagMap = Flags.collectFlags();
+ Map<Integer, Flag<?>> flagMap = mFlagsCollector.get();
if (!flagMap.containsKey(id)) {
Log.w(TAG, "Tried to set unknown id: " + id);
return;
}
Flag<?> flag = flagMap.get(id);
- if (!extras.containsKey(FIELD_VALUE)) {
+ if (!extras.containsKey(EXTRA_VALUE)) {
eraseFlag(id);
return;
}
- if (flag instanceof BooleanFlag) {
- setEnabled(id, extras.getBoolean(FIELD_VALUE));
+ Object value = extras.get(EXTRA_VALUE);
+ if (flag instanceof BooleanFlag && value instanceof Boolean) {
+ setFlagValue(id, (Boolean) value, BooleanFlagSerializer.INSTANCE);
+ } else if (flag instanceof ResourceBooleanFlag && value instanceof Boolean) {
+ setFlagValue(id, (Boolean) value, BooleanFlagSerializer.INSTANCE);
+ } else if (flag instanceof StringFlag && value instanceof String) {
+ setFlagValue(id, (String) value, StringFlagSerializer.INSTANCE);
+ } else if (flag instanceof ResourceStringFlag && value instanceof String) {
+ setFlagValue(id, (String) value, StringFlagSerializer.INSTANCE);
+ } else {
+ Log.w(TAG,
+ "Unable to set " + id + " of type " + flag.getClass() + " to value of type "
+ + (value == null ? null : value.getClass()));
}
}
@@ -245,16 +299,18 @@
}
};
+ private void removeFromCache(int id) {
+ mBooleanFlagCache.remove(id);
+ mStringFlagCache.remove(id);
+ }
+
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("can override: true");
- ArrayList<String> flagStrings = new ArrayList<>(mBooleanFlagCache.size());
- for (Map.Entry<Integer, Boolean> entry : mBooleanFlagCache.entrySet()) {
- flagStrings.add(" sysui_flag_" + entry.getKey() + ": " + entry.getValue());
- }
- flagStrings.sort(String.CASE_INSENSITIVE_ORDER);
- for (String flagString : flagStrings) {
- pw.println(flagString);
- }
+ pw.println("booleans: " + mBooleanFlagCache.size());
+ mBooleanFlagCache.forEach((key, value) -> pw.println(" sysui_flag_" + key + ": " + value));
+ pw.println("Strings: " + mStringFlagCache.size());
+ mStringFlagCache.forEach((key, value) -> pw.println(" sysui_flag_" + key
+ + ": [length=" + value.length() + "] \"" + value + "\""));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
index 5b6404f..348a8e2 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
@@ -16,7 +16,10 @@
package com.android.systemui.flags;
+import static java.util.Objects.requireNonNull;
+
import android.content.res.Resources;
+import android.util.SparseArray;
import android.util.SparseBooleanArray;
import androidx.annotation.NonNull;
@@ -40,7 +43,8 @@
@SysUISingleton
public class FeatureFlagsRelease implements FeatureFlags, Dumpable {
private final Resources mResources;
- SparseBooleanArray mFlagCache = new SparseBooleanArray();
+ SparseBooleanArray mBooleanCache = new SparseBooleanArray();
+ SparseArray<String> mStringCache = new SparseArray<>();
@Inject
public FeatureFlagsRelease(@Main Resources resources, DumpManager dumpManager) {
mResources = resources;
@@ -48,10 +52,10 @@
}
@Override
- public void addListener(Listener run) {}
+ public void addListener(@NonNull Flag<?> flag, @NonNull Listener listener) {}
@Override
- public void removeListener(Listener run) {}
+ public void removeListener(@NonNull Listener listener) {}
@Override
public boolean isEnabled(BooleanFlag flag) {
@@ -60,27 +64,57 @@
@Override
public boolean isEnabled(ResourceBooleanFlag flag) {
- int cacheIndex = mFlagCache.indexOfKey(flag.getId());
+ int cacheIndex = mBooleanCache.indexOfKey(flag.getId());
if (cacheIndex < 0) {
return isEnabled(flag.getId(), mResources.getBoolean(flag.getResourceId()));
}
- return mFlagCache.valueAt(cacheIndex);
+ return mBooleanCache.valueAt(cacheIndex);
}
+ private boolean isEnabled(int key, boolean defaultValue) {
+ mBooleanCache.append(key, defaultValue);
+ return defaultValue;
+ }
+
+ @NonNull
@Override
- public boolean isEnabled(int key, boolean defaultValue) {
- mFlagCache.append(key, defaultValue);
+ public String getString(@NonNull StringFlag flag) {
+ return getString(flag.getId(), flag.getDefault());
+ }
+
+ @NonNull
+ @Override
+ public String getString(@NonNull ResourceStringFlag flag) {
+ int cacheIndex = mStringCache.indexOfKey(flag.getId());
+ if (cacheIndex < 0) {
+ return getString(flag.getId(),
+ requireNonNull(mResources.getString(flag.getResourceId())));
+ }
+
+ return mStringCache.valueAt(cacheIndex);
+ }
+
+ private String getString(int key, String defaultValue) {
+ mStringCache.append(key, defaultValue);
return defaultValue;
}
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("can override: false");
- int size = mFlagCache.size();
- for (int i = 0; i < size; i++) {
- pw.println(" sysui_flag_" + mFlagCache.keyAt(i)
- + ": " + mFlagCache.valueAt(i));
+ int numBooleans = mBooleanCache.size();
+ pw.println("booleans: " + numBooleans);
+ for (int i = 0; i < numBooleans; i++) {
+ pw.println(" sysui_flag_" + mBooleanCache.keyAt(i) + ": " + mBooleanCache.valueAt(i));
+ }
+ int numStrings = mStringCache.size();
+ pw.println("Strings: " + numStrings);
+ for (int i = 0; i < numStrings; i++) {
+ final int id = mStringCache.keyAt(i);
+ final String value = mStringCache.valueAt(i);
+ final int length = value.length();
+ pw.println(" sysui_flag_" + id + ": [length=" + length + "] \"" + value + "\"");
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index ff14064..2ebcd853 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -121,6 +121,7 @@
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -236,6 +237,7 @@
protected Handler mMainHandler;
private int mSmallestScreenWidthDp;
private final Optional<StatusBar> mStatusBarOptional;
+ private final SystemUIDialogManager mDialogManager;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final DialogLaunchAnimator mDialogLaunchAnimator;
@@ -346,7 +348,8 @@
PackageManager packageManager,
Optional<StatusBar> statusBarOptional,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- DialogLaunchAnimator dialogLaunchAnimator) {
+ DialogLaunchAnimator dialogLaunchAnimator,
+ SystemUIDialogManager dialogManager) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -378,6 +381,7 @@
mStatusBarOptional = statusBarOptional;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDialogLaunchAnimator = dialogLaunchAnimator;
+ mDialogManager = dialogManager;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -677,7 +681,8 @@
mAdapter, mOverflowAdapter, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
mSysUiState, this::onRefresh, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
- mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils);
+ mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils,
+ mDialogManager);
dialog.setOnDismissListener(this);
dialog.setOnShowListener(this);
@@ -2219,10 +2224,12 @@
SysUiState sysuiState, Runnable onRefreshCallback, boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
Optional<StatusBar> statusBarOptional,
- KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) {
+ KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils,
+ SystemUIDialogManager systemUiDialogManager) {
// We set dismissOnDeviceLock to false because we have a custom broadcast receiver to
// dismiss this dialog when the device is locked.
- super(context, themeRes, false /* dismissOnDeviceLock */);
+ super(context, themeRes, false /* dismissOnDeviceLock */,
+ systemUiDialogManager);
mContext = context;
mAdapter = adapter;
mOverflowAdapter = overflowAdapter;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c14d32e..0c9e315 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -122,6 +122,7 @@
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.unfold.FoldAodAnimationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
import com.android.systemui.util.DeviceConfigProxy;
@@ -131,7 +132,6 @@
import java.util.ArrayList;
import java.util.Optional;
import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicInteger;
import dagger.Lazy;
@@ -439,7 +439,7 @@
private boolean mInGestureNavigationMode;
private boolean mWakeAndUnlocking;
- private IKeyguardDrawnCallback mDrawnCallback;
+ private Runnable mWakeAndUnlockingDrawnCallback;
private CharSequence mCustomMessage;
/**
@@ -817,7 +817,8 @@
private DozeParameters mDozeParameters;
private final Optional<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealAnimation;
- private final AtomicInteger mPendingDrawnTasks = new AtomicInteger();
+ private final Optional<FoldAodAnimationController> mFoldAodAnimationController;
+ private final PendingDrawnTasksContainer mPendingDrawnTasks = new PendingDrawnTasksContainer();
private final KeyguardStateController mKeyguardStateController;
private final Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy;
@@ -877,8 +878,12 @@
mInGestureNavigationMode = QuickStepContract.isGesturalMode(mode);
}));
mDozeParameters = dozeParameters;
- mUnfoldLightRevealAnimation = unfoldComponent.map(
- c -> c.getUnfoldLightRevealOverlayAnimation());
+
+ mUnfoldLightRevealAnimation = unfoldComponent
+ .map(SysUIUnfoldComponent::getUnfoldLightRevealOverlayAnimation);
+ mFoldAodAnimationController = unfoldComponent
+ .map(SysUIUnfoldComponent::getFoldAodAnimationController);
+
mStatusBarStateController = statusBarStateController;
statusBarStateController.addCallback(this);
@@ -1069,7 +1074,7 @@
mDeviceInteractive = false;
mGoingToSleep = false;
mWakeAndUnlocking = false;
- mAnimatingScreenOff = mDozeParameters.shouldControlUnlockedScreenOff();
+ mAnimatingScreenOff = mDozeParameters.shouldAnimateDozingChange();
resetKeyguardDonePendingLocked();
mHideAnimationRun = false;
@@ -2099,6 +2104,15 @@
private final Runnable mKeyguardGoingAwayRunnable = new Runnable() {
@Override
public void run() {
+ // If the keyguard is already going away, or it's about to because we are going to
+ // trigger the going-away remote animation to show the surface behind, don't do it
+ // again. That will cause the current animation to be cancelled unnecessarily.
+ if (mKeyguardStateController.isKeyguardGoingAway()
+ || mSurfaceBehindRemoteAnimationRequested
+ || mSurfaceBehindRemoteAnimationRunning) {
+ return;
+ }
+
Trace.beginSection("KeyguardViewMediator.mKeyGuardGoingAwayRunnable");
if (DEBUG) Log.d(TAG, "keyguardGoingAway");
mKeyguardViewControllerLazy.get().keyguardGoingAway();
@@ -2221,14 +2235,14 @@
IRemoteAnimationRunner runner = mKeyguardExitAnimationRunner;
mKeyguardExitAnimationRunner = null;
- if (mWakeAndUnlocking && mDrawnCallback != null) {
+ if (mWakeAndUnlocking && mWakeAndUnlockingDrawnCallback != null) {
// Hack level over 9000: To speed up wake-and-unlock sequence, force it to report
// the next draw from here so we don't have to wait for window manager to signal
// this to our ViewRootImpl.
mKeyguardViewControllerLazy.get().getViewRootImpl().setReportNextDraw();
- notifyDrawn(mDrawnCallback);
- mDrawnCallback = null;
+ mWakeAndUnlockingDrawnCallback.run();
+ mWakeAndUnlockingDrawnCallback = null;
}
LatencyTracker.getInstance(mContext)
@@ -2457,9 +2471,7 @@
if (mSurfaceBehindRemoteAnimationFinishedCallback != null) {
try {
- if (!cancelled) {
- mSurfaceBehindRemoteAnimationFinishedCallback.onAnimationFinished();
- }
+ mSurfaceBehindRemoteAnimationFinishedCallback.onAnimationFinished();
mSurfaceBehindRemoteAnimationFinishedCallback = null;
} catch (RemoteException e) {
e.printStackTrace();
@@ -2566,31 +2578,27 @@
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurningOn");
- if (mUnfoldLightRevealAnimation.isPresent()) {
- mPendingDrawnTasks.set(2); // unfold overlay and keyguard drawn
+ mPendingDrawnTasks.reset();
+ if (mUnfoldLightRevealAnimation.isPresent()) {
mUnfoldLightRevealAnimation.get()
- .onScreenTurningOn(() -> {
- if (mPendingDrawnTasks.decrementAndGet() == 0) {
- try {
- callback.onDrawn();
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception calling onDrawn():", e);
- }
- }
- });
- } else {
- mPendingDrawnTasks.set(1); // only keyguard drawn
+ .onScreenTurningOn(mPendingDrawnTasks.registerTask("unfold-reveal"));
+ }
+
+ if (mFoldAodAnimationController.isPresent()) {
+ mFoldAodAnimationController.get()
+ .onScreenTurningOn(mPendingDrawnTasks.registerTask("fold-to-aod"));
}
mKeyguardViewControllerLazy.get().onScreenTurningOn();
if (callback != null) {
if (mWakeAndUnlocking) {
- mDrawnCallback = callback;
- } else {
- notifyDrawn(callback);
+ mWakeAndUnlockingDrawnCallback =
+ mPendingDrawnTasks.registerTask("wake-and-unlocking");
}
}
+
+ mPendingDrawnTasks.onTasksComplete(() -> notifyDrawn(callback));
}
Trace.endSection();
}
@@ -2599,6 +2607,8 @@
Trace.beginSection("KeyguardViewMediator#handleNotifyScreenTurnedOn");
synchronized (this) {
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOn");
+
+ mPendingDrawnTasks.reset();
mKeyguardViewControllerLazy.get().onScreenTurnedOn();
}
Trace.endSection();
@@ -2607,18 +2617,18 @@
private void handleNotifyScreenTurnedOff() {
synchronized (this) {
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOff");
- mDrawnCallback = null;
+ mWakeAndUnlockingDrawnCallback = null;
}
}
private void notifyDrawn(final IKeyguardDrawnCallback callback) {
Trace.beginSection("KeyguardViewMediator#notifyDrawn");
- if (mPendingDrawnTasks.decrementAndGet() == 0) {
- try {
+ try {
+ if (callback != null) {
callback.onDrawn();
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception calling onDrawn():", e);
}
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception calling onDrawn():", e);
}
Trace.endSection();
}
@@ -2777,9 +2787,9 @@
pw.print(" mHideAnimationRun: "); pw.println(mHideAnimationRun);
pw.print(" mPendingReset: "); pw.println(mPendingReset);
pw.print(" mPendingLock: "); pw.println(mPendingLock);
- pw.print(" mPendingDrawnTasks: "); pw.println(mPendingDrawnTasks.get());
+ pw.print(" mPendingDrawnTasks: "); pw.println(mPendingDrawnTasks.getPendingCount());
pw.print(" mWakeAndUnlocking: "); pw.println(mWakeAndUnlocking);
- pw.print(" mDrawnCallback: "); pw.println(mDrawnCallback);
+ pw.print(" mDrawnCallback: "); pw.println(mWakeAndUnlockingDrawnCallback);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/PendingDrawnTasksContainer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/PendingDrawnTasksContainer.kt
new file mode 100644
index 0000000..bccd106
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/PendingDrawnTasksContainer.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.keyguard
+
+import android.os.Trace
+import java.util.concurrent.atomic.AtomicInteger
+import java.util.concurrent.atomic.AtomicReference
+
+/**
+ * Allows to wait for multiple callbacks and notify when the last one is executed
+ */
+class PendingDrawnTasksContainer {
+
+ private lateinit var pendingDrawnTasksCount: AtomicInteger
+ private var completionCallback: AtomicReference<Runnable> = AtomicReference()
+
+ /**
+ * Registers a task that we should wait for
+ * @return a runnable that should be invoked when the task is finished
+ */
+ fun registerTask(name: String): Runnable {
+ pendingDrawnTasksCount.incrementAndGet()
+
+ if (ENABLE_TRACE) {
+ Trace.beginAsyncSection("PendingDrawnTasksContainer#$name", 0)
+ }
+
+ return Runnable {
+ if (pendingDrawnTasksCount.decrementAndGet() == 0) {
+ val onComplete = completionCallback.getAndSet(null)
+ onComplete?.run()
+
+ if (ENABLE_TRACE) {
+ Trace.endAsyncSection("PendingDrawnTasksContainer#$name", 0)
+ }
+ }
+ }
+ }
+
+ /**
+ * Clears state and initializes the container
+ */
+ fun reset() {
+ // Create new objects in case if there are pending callbacks from the previous invocations
+ completionCallback = AtomicReference()
+ pendingDrawnTasksCount = AtomicInteger(0)
+ }
+
+ /**
+ * Starts waiting for all tasks to be completed
+ * When all registered tasks complete it will invoke the [onComplete] callback
+ */
+ fun onTasksComplete(onComplete: Runnable) {
+ completionCallback.set(onComplete)
+
+ if (pendingDrawnTasksCount.get() == 0) {
+ val currentOnComplete = completionCallback.getAndSet(null)
+ currentOnComplete?.run()
+ }
+ }
+
+ /**
+ * Returns current pending tasks count
+ */
+ fun getPendingCount(): Int = pendingDrawnTasksCount.get()
+}
+
+private const val ENABLE_TRACE = false
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 49a63c3..d926e7d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -728,9 +728,9 @@
PlaybackState.ACTION_SKIP_TO_NEXT)
// Then, check for custom actions
- val customActions = MutableList<MediaAction?>(4) { null }
+ val customActions = MutableList<MediaAction?>(MAX_CUSTOM_ACTIONS) { null }
var customCount = 0
- for (i in 0..MAX_CUSTOM_ACTIONS) {
+ for (i in 0..(MAX_CUSTOM_ACTIONS - 1)) {
getCustomAction(state, packageName, controller, customCount)?.let {
customActions[customCount++] = it
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index cf679f0..a1f8455 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -25,6 +25,7 @@
import com.android.systemui.media.MediaHost;
import com.android.systemui.media.MediaHostStatesManager;
import com.android.systemui.media.taptotransfer.MediaTttChipController;
+import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
import com.android.systemui.media.taptotransfer.MediaTttFlags;
import com.android.systemui.statusbar.commandline.CommandRegistry;
@@ -78,11 +79,23 @@
static Optional<MediaTttChipController> providesMediaTttChipController(
MediaTttFlags mediaTttFlags,
Context context,
- CommandRegistry commandRegistry,
WindowManager windowManager) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
- return Optional.of(new MediaTttChipController(commandRegistry, context, windowManager));
+ return Optional.of(new MediaTttChipController(context, windowManager));
+ }
+
+ /** */
+ @Provides
+ @SysUISingleton
+ static Optional<MediaTttCommandLineHelper> providesMediaTttCommandLineHelper(
+ MediaTttFlags mediaTttFlags,
+ CommandRegistry commandRegistry,
+ MediaTttChipController mediaTttChipController) {
+ if (!mediaTttFlags.isMediaTttEnabled()) {
+ return Optional.empty();
+ }
+ return Optional.of(new MediaTttCommandLineHelper(commandRegistry, mediaTttChipController));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 7f5744c..a9e9f0f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -45,6 +45,7 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
/**
* Base dialog for media output UI
@@ -53,6 +54,7 @@
MediaOutputController.Callback, Window.Callback {
private static final String TAG = "MediaOutputDialog";
+ private static final String EMPTY_TITLE = " ";
private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private final RecyclerView.LayoutManager mLayoutManager;
@@ -83,8 +85,9 @@
}
};
- public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController) {
- super(context, R.style.Theme_SystemUI_Dialog_Media);
+ public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController,
+ SystemUIDialogManager dialogManager) {
+ super(context, R.style.Theme_SystemUI_Dialog_Media, dialogManager);
// Save the context that is wrapped with our theme.
mContext = getContext();
@@ -108,6 +111,9 @@
lp.setFitInsetsIgnoringVisibility(true);
window.setAttributes(lp);
window.setContentView(mDialogView);
+ // Sets window to a blank string to avoid talkback announce app label first when pop up,
+ // which doesn't make sense.
+ window.setTitle(EMPTY_TITLE);
mHeaderTitle = mDialogView.requireViewById(R.id.header_title);
mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 56317c6..4eee60c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -61,6 +61,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import java.util.ArrayList;
import java.util.Collection;
@@ -85,6 +86,7 @@
private final ShadeController mShadeController;
private final ActivityStarter mActivityStarter;
private final DialogLaunchAnimator mDialogLaunchAnimator;
+ private final SystemUIDialogManager mDialogManager;
private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
private final boolean mAboveStatusbar;
private final boolean mVolumeAdjustmentForRemoteGroupSessions;
@@ -106,7 +108,7 @@
boolean aboveStatusbar, MediaSessionManager mediaSessionManager, LocalBluetoothManager
lbm, ShadeController shadeController, ActivityStarter starter,
NotificationEntryManager notificationEntryManager, UiEventLogger uiEventLogger,
- DialogLaunchAnimator dialogLaunchAnimator) {
+ DialogLaunchAnimator dialogLaunchAnimator, SystemUIDialogManager dialogManager) {
mContext = context;
mPackageName = packageName;
mMediaSessionManager = mediaSessionManager;
@@ -122,6 +124,7 @@
mDialogLaunchAnimator = dialogLaunchAnimator;
mVolumeAdjustmentForRemoteGroupSessions = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
+ mDialogManager = dialogManager;
}
void start(@NonNull Callback cb) {
@@ -512,9 +515,10 @@
// We show the output group dialog from the output dialog.
MediaOutputController controller = new MediaOutputController(mContext, mPackageName,
mAboveStatusbar, mMediaSessionManager, mLocalBluetoothManager, mShadeController,
- mActivityStarter, mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mActivityStarter, mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator,
+ mDialogManager);
MediaOutputGroupDialog dialog = new MediaOutputGroupDialog(mContext, mAboveStatusbar,
- controller);
+ controller, mDialogManager);
mDialogLaunchAnimator.showFromView(dialog, mediaOutputDialog);
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index 7696a1f..4e9da55 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -29,6 +29,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
/**
* Dialog for media output transferring.
@@ -38,8 +39,9 @@
final UiEventLogger mUiEventLogger;
MediaOutputDialog(Context context, boolean aboveStatusbar, MediaOutputController
- mediaOutputController, UiEventLogger uiEventLogger) {
- super(context, mediaOutputController);
+ mediaOutputController, UiEventLogger uiEventLogger,
+ SystemUIDialogManager dialogManager) {
+ super(context, mediaOutputController, dialogManager);
mUiEventLogger = uiEventLogger;
mAdapter = new MediaOutputAdapter(mMediaOutputController, this);
if (!aboveStatusbar) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index b91901d..a7bc852 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -25,6 +25,7 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.phone.ShadeController
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
import javax.inject.Inject
/**
@@ -38,7 +39,8 @@
private val starter: ActivityStarter,
private val notificationEntryManager: NotificationEntryManager,
private val uiEventLogger: UiEventLogger,
- private val dialogLaunchAnimator: DialogLaunchAnimator
+ private val dialogLaunchAnimator: DialogLaunchAnimator,
+ private val dialogManager: SystemUIDialogManager
) {
companion object {
var mediaOutputDialog: MediaOutputDialog? = null
@@ -51,8 +53,9 @@
val controller = MediaOutputController(context, packageName, aboveStatusBar,
mediaSessionManager, lbm, shadeController, starter, notificationEntryManager,
- uiEventLogger, dialogLaunchAnimator)
- val dialog = MediaOutputDialog(context, aboveStatusBar, controller, uiEventLogger)
+ uiEventLogger, dialogLaunchAnimator, dialogManager)
+ val dialog = MediaOutputDialog(context, aboveStatusBar, controller, uiEventLogger,
+ dialogManager)
mediaOutputDialog = dialog
// Show the dialog.
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
index f1c6601..9f752b9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
@@ -25,6 +25,7 @@
import androidx.core.graphics.drawable.IconCompat;
import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
/**
* Dialog for media output group.
@@ -33,8 +34,8 @@
public class MediaOutputGroupDialog extends MediaOutputBaseDialog {
MediaOutputGroupDialog(Context context, boolean aboveStatusbar, MediaOutputController
- mediaOutputController) {
- super(context, mediaOutputController);
+ mediaOutputController, SystemUIDialogManager dialogManager) {
+ super(context, mediaOutputController, dialogManager);
mMediaOutputController.resetGroupMediaDevices();
mAdapter = new MediaOutputGroupAdapter(mMediaOutputController);
if (!aboveStatusbar) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
index af8fc0e..1ec6a39 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
@@ -24,13 +24,8 @@
import android.view.WindowManager
import android.widget.LinearLayout
import android.widget.TextView
-import androidx.annotation.StringRes
-import androidx.annotation.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.commandline.Command
-import com.android.systemui.statusbar.commandline.CommandRegistry
-import java.io.PrintWriter
import javax.inject.Inject
/**
@@ -41,14 +36,9 @@
*/
@SysUISingleton
class MediaTttChipController @Inject constructor(
- commandRegistry: CommandRegistry,
private val context: Context,
private val windowManager: WindowManager,
) {
- init {
- commandRegistry.registerCommand(ADD_CHIP_COMMAND_TAG) { AddChipCommand() }
- commandRegistry.registerCommand(REMOVE_CHIP_COMMAND_TAG) { RemoveChipCommand() }
- }
private val windowLayoutParams = WindowManager.LayoutParams().apply {
width = WindowManager.LayoutParams.WRAP_CONTENT
@@ -65,7 +55,8 @@
/** The chip view currently being displayed. Null if the chip is not being displayed. */
private var chipView: LinearLayout? = null
- private fun displayChip(chipType: ChipType, otherDeviceName: String) {
+ /** Displays the chip view for the given state. */
+ fun displayChip(chipState: MediaTttChipState) {
val oldChipView = chipView
if (chipView == null) {
chipView = LayoutInflater
@@ -76,16 +67,16 @@
// Text
currentChipView.requireViewById<TextView>(R.id.text).apply {
- text = context.getString(chipType.chipText, otherDeviceName)
+ text = context.getString(chipState.chipText, chipState.otherDeviceName)
}
// Loading
- val showLoading = chipType == ChipType.TRANSFER_INITIATED
+ val showLoading = chipState is TransferInitiated
currentChipView.requireViewById<View>(R.id.loading).visibility =
if (showLoading) { View.VISIBLE } else { View.GONE }
// Undo
- val showUndo = chipType == ChipType.TRANSFER_SUCCEEDED
+ val showUndo = chipState is TransferSucceeded
currentChipView.requireViewById<View>(R.id.undo).visibility =
if (showUndo) { View.VISIBLE } else { View.GONE }
@@ -94,53 +85,10 @@
}
}
- private fun removeChip() {
+ /** Hides the chip. */
+ fun removeChip() {
if (chipView == null) { return }
windowManager.removeView(chipView)
chipView = null
}
-
- @VisibleForTesting
- enum class ChipType(
- @StringRes internal val chipText: Int
- ) {
- MOVE_CLOSER_TO_TRANSFER(R.string.media_move_closer_to_transfer),
- TRANSFER_INITIATED(R.string.media_transfer_playing),
- TRANSFER_SUCCEEDED(R.string.media_transfer_playing),
- }
-
- inner class AddChipCommand : Command {
- override fun execute(pw: PrintWriter, args: List<String>) {
- val chipTypeArg = args[1]
- ChipType.values().forEach {
- if (it.name == chipTypeArg) {
- displayChip(it, otherDeviceName = args[0])
- return
- }
- }
-
- pw.println("Chip type must be one of " +
- ChipType.values().map { it.name }.reduce { acc, s -> "$acc, $s" })
- }
-
- override fun help(pw: PrintWriter) {
- pw.println(
- "Usage: adb shell cmd statusbar $ADD_CHIP_COMMAND_TAG <deviceName> <chipType>"
- )
- }
- }
-
- inner class RemoveChipCommand : Command {
- override fun execute(pw: PrintWriter, args: List<String>) = removeChip()
- override fun help(pw: PrintWriter) {
- pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_TAG")
- }
- }
-
- companion object {
- @VisibleForTesting
- const val ADD_CHIP_COMMAND_TAG = "media-ttt-chip-add"
- @VisibleForTesting
- const val REMOVE_CHIP_COMMAND_TAG = "media-ttt-chip-remove"
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt
new file mode 100644
index 0000000..05b9552
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.media.taptotransfer
+
+import androidx.annotation.StringRes
+import com.android.systemui.R
+
+/**
+ * A class that stores all the information necessary to display the media tap-to-transfer chip in
+ * certain states.
+ *
+ * This is a sealed class where each subclass represents a specific chip state. Each subclass can
+ * contain additional information that is necessary for only that state.
+ */
+sealed class MediaTttChipState(
+ /** A string resource for the text that the chip should display. */
+ @StringRes internal val chipText: Int,
+ /** The name of the other device involved in the transfer. */
+ internal val otherDeviceName: String
+)
+
+/**
+ * A state representing that the two devices are close but not close enough to initiate a transfer.
+ * The chip will instruct the user to move closer in order to initiate the transfer.
+ */
+class MoveCloserToTransfer(
+ otherDeviceName: String
+) : MediaTttChipState(R.string.media_move_closer_to_transfer, otherDeviceName)
+
+/**
+ * A state representing that a transfer has been initiated (but not completed).
+ */
+class TransferInitiated(
+ otherDeviceName: String
+) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName)
+
+/**
+ * A state representing that a transfer has been successfully completed.
+ */
+class TransferSucceeded(
+ otherDeviceName: String
+) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
new file mode 100644
index 0000000..9f05c92
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.media.taptotransfer
+
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * A helper class to test the media tap-to-transfer chip via the command line. See inner classes for
+ * command usages.
+ */
+@SysUISingleton
+class MediaTttCommandLineHelper @Inject constructor(
+ commandRegistry: CommandRegistry,
+ private val mediaTttChipController: MediaTttChipController
+) {
+ init {
+ commandRegistry.registerCommand(ADD_CHIP_COMMAND_TAG) { AddChipCommand() }
+ commandRegistry.registerCommand(REMOVE_CHIP_COMMAND_TAG) { RemoveChipCommand() }
+ }
+
+ inner class AddChipCommand : Command {
+ override fun execute(pw: PrintWriter, args: List<String>) {
+ val otherDeviceName = args[0]
+ when (args[1]) {
+ MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME -> {
+ mediaTttChipController.displayChip(MoveCloserToTransfer(otherDeviceName))
+ }
+ TRANSFER_INITIATED_COMMAND_NAME -> {
+ mediaTttChipController.displayChip(TransferInitiated(otherDeviceName))
+ }
+ TRANSFER_SUCCEEDED_COMMAND_NAME -> {
+ mediaTttChipController.displayChip(TransferSucceeded(otherDeviceName))
+ }
+ else -> {
+ pw.println("Chip type must be one of " +
+ "$MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME, " +
+ "$TRANSFER_INITIATED_COMMAND_NAME, " +
+ TRANSFER_SUCCEEDED_COMMAND_NAME
+ )
+ }
+ }
+ }
+
+ override fun help(pw: PrintWriter) {
+ pw.println(
+ "Usage: adb shell cmd statusbar $ADD_CHIP_COMMAND_TAG <deviceName> <chipStatus>"
+ )
+ }
+ }
+
+ inner class RemoveChipCommand : Command {
+ override fun execute(pw: PrintWriter, args: List<String>) {
+ mediaTttChipController.removeChip()
+ }
+ override fun help(pw: PrintWriter) {
+ pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_TAG")
+ }
+ }
+}
+
+@VisibleForTesting
+const val ADD_CHIP_COMMAND_TAG = "media-ttt-chip-add"
+@VisibleForTesting
+const val REMOVE_CHIP_COMMAND_TAG = "media-ttt-chip-remove"
+@VisibleForTesting
+val MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME = MoveCloserToTransfer::class.simpleName!!
+@VisibleForTesting
+val TRANSFER_INITIATED_COMMAND_NAME = TransferInitiated::class.simpleName!!
+@VisibleForTesting
+val TRANSFER_SUCCEEDED_COMMAND_NAME = TransferSucceeded::class.simpleName!!
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 8026df7..cc91384 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -1251,6 +1251,7 @@
private void onImeSwitcherClick(View v) {
mInputMethodManager.showInputMethodPickerFromSystem(
true /* showAuxiliarySubtypes */, mDisplayId);
+ mUiEventLogger.log(KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP);
};
private boolean onLongPressBackHome(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
index debd2eb..d27b716 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
@@ -96,6 +96,9 @@
@UiEvent(doc = "The overview button was pressed in the navigation bar.")
NAVBAR_OVERVIEW_BUTTON_TAP(535),
+ @UiEvent(doc = "The ime switcher button was pressed in the navigation bar.")
+ NAVBAR_IME_SWITCHER_BUTTON_TAP(923),
+
@UiEvent(doc = "The home button was long-pressed in the navigation bar.")
NAVBAR_HOME_BUTTON_LONGPRESS(536),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
index dab0efe..e93c349 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
@@ -259,6 +259,12 @@
}
public static class Item {
+ public Item(int iconResId, CharSequence line1, Object tag) {
+ this.iconResId = iconResId;
+ this.line1 = line1;
+ this.tag = tag;
+ }
+
public int iconResId;
public QSTile.Icon icon;
public Drawable overlay;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index b904505..9acd3eb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -56,6 +56,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import javax.inject.Inject;
@@ -364,11 +365,13 @@
}
info.state.expandedAccessibilityClassName = "";
- // The holder has a tileView, therefore this call is not null
- holder.getTileAsCustomizeView().changeState(info.state);
- holder.getTileAsCustomizeView().setShowAppLabel(position > mEditIndex && !info.isSystem);
+ CustomizeTileView tileView =
+ Objects.requireNonNull(
+ holder.getTileAsCustomizeView(), "The holder must have a tileView");
+ tileView.changeState(info.state);
+ tileView.setShowAppLabel(position > mEditIndex && !info.isSystem);
// Don't show the side view for third party tiles, as we don't have the actual state.
- holder.getTileAsCustomizeView().setShowSideView(position < mEditIndex || info.isSystem);
+ tileView.setShowSideView(position < mEditIndex || info.isSystem);
holder.mTileView.setSelected(true);
holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
holder.mTileView.setClickable(true);
@@ -448,7 +451,12 @@
mFocusIndex = mEditIndex - 1;
mNeedsFocus = true;
if (mRecyclerView != null) {
- mRecyclerView.post(() -> mRecyclerView.smoothScrollToPosition(mFocusIndex));
+ mRecyclerView.post(() -> {
+ final RecyclerView recyclerView = mRecyclerView;
+ if (recyclerView != null) {
+ recyclerView.smoothScrollToPosition(mFocusIndex);
+ }
+ });
}
notifyDataSetChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 7e410d0..6c072f1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -146,6 +146,10 @@
}
private static class TilePair {
+ private TilePair(QSTile tile) {
+ mTile = tile;
+ }
+
QSTile mTile;
boolean mReady = false;
}
@@ -157,8 +161,7 @@
TileCollector(List<QSTile> tilesToAdd, QSTileHost host) {
for (QSTile tile: tilesToAdd) {
- TilePair pair = new TilePair();
- pair.mTile = tile;
+ TilePair pair = new TilePair(tile);
mQSTileList.add(pair);
}
mQSTileHost = host;
@@ -288,15 +291,11 @@
if (mSpecs.contains(spec)) {
return;
}
- TileInfo info = new TileInfo();
- info.state = state;
- info.state.dualTarget = false; // No dual targets in edit.
- info.state.expandedAccessibilityClassName =
- Button.class.getName();
- info.spec = spec;
- info.state.secondaryLabel = (isSystem || TextUtils.equals(state.label, appLabel))
+ state.dualTarget = false; // No dual targets in edit.
+ state.expandedAccessibilityClassName = Button.class.getName();
+ state.secondaryLabel = (isSystem || TextUtils.equals(state.label, appLabel))
? null : appLabel;
- info.isSystem = isSystem;
+ TileInfo info = new TileInfo(spec, state, isSystem);
mTiles.add(info);
mSpecs.add(spec);
}
@@ -312,6 +311,12 @@
}
public static class TileInfo {
+ public TileInfo(String spec, QSTile.State state, boolean isSystem) {
+ this.spec = spec;
+ this.state = state;
+ this.isSystem = isSystem;
+ }
+
public String spec;
public QSTile.State state;
public boolean isSystem;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 5bd02cc..10efec3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -48,6 +48,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
@@ -562,7 +563,8 @@
return this;
}
- CustomTile build() {
+ @VisibleForTesting
+ public CustomTile build() {
if (mUserContext == null) {
throw new NullPointerException("UserContext cannot be null");
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 0f2db33..bfa2aaa4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -46,6 +46,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Objects;
/**
* Runs the day-to-day operations of which tiles should be bound and when.
@@ -178,6 +179,13 @@
return;
}
TileServiceManager service = mServices.get(customTile);
+ if (service == null) {
+ Log.e(
+ TAG,
+ "No TileServiceManager found in requestListening for tile "
+ + customTile.getTileSpec());
+ return;
+ }
if (!service.isActiveTile()) {
return;
}
@@ -236,7 +244,7 @@
verifyCaller(customTile);
customTile.onDialogShown();
mHost.forceCollapsePanels();
- mServices.get(customTile).setShowingDialog(true);
+ Objects.requireNonNull(mServices.get(customTile)).setShowingDialog(true);
}
}
@@ -245,7 +253,7 @@
CustomTile customTile = getTileForToken(token);
if (customTile != null) {
verifyCaller(customTile);
- mServices.get(customTile).setShowingDialog(false);
+ Objects.requireNonNull(mServices.get(customTile)).setShowingDialog(false);
customTile.onDialogHidden();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index ac95bf5..c2a9e3a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -169,7 +169,7 @@
mFgsManagerTileProvider = fgsManagerTileProvider;
}
- public QSTile createTile(String tileSpec) {
+ public final QSTile createTile(String tileSpec) {
QSTileImpl tile = createTileInternal(tileSpec);
if (tile != null) {
tile.initialize();
@@ -178,7 +178,7 @@
return tile;
}
- private QSTileImpl createTileInternal(String tileSpec) {
+ protected QSTileImpl createTileInternal(String tileSpec) {
// Stock tiles.
switch (tileSpec) {
case "wifi":
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 65b6617..6fcfc3a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -394,10 +394,11 @@
int count = 0;
for (CachedBluetoothDevice device : devices) {
if (mController.getBondState(device) == BluetoothDevice.BOND_NONE) continue;
- final Item item = new Item();
- item.iconResId = com.android.internal.R.drawable.ic_qs_bluetooth;
- item.line1 = device.getName();
- item.tag = device;
+ final Item item =
+ new Item(
+ com.android.internal.R.drawable.ic_qs_bluetooth,
+ device.getName(),
+ device);
int state = device.getMaxConnectionState();
if (state == BluetoothProfile.STATE_CONNECTED) {
item.iconResId = R.drawable.ic_bluetooth_connected;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index b83dc52..1fb608a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -402,11 +402,12 @@
// if we are connected, simply show that device
for (CastDevice device : devices) {
if (device.state == CastDevice.STATE_CONNECTED) {
- final Item item = new Item();
- item.iconResId = R.drawable.ic_cast_connected;
- item.line1 = getDeviceName(device);
+ final Item item =
+ new Item(
+ R.drawable.ic_cast_connected,
+ getDeviceName(device),
+ device);
item.line2 = mContext.getString(R.string.quick_settings_connected);
- item.tag = device;
item.canDisconnect = true;
items = new Item[] { item };
break;
@@ -422,13 +423,11 @@
for (String id : mVisibleOrder.keySet()) {
final CastDevice device = mVisibleOrder.get(id);
if (!devices.contains(device)) continue;
- final Item item = new Item();
- item.iconResId = R.drawable.ic_cast;
- item.line1 = getDeviceName(device);
+ final Item item =
+ new Item(R.drawable.ic_cast, getDeviceName(device), device);
if (device.state == CastDevice.STATE_CONNECTING) {
item.line2 = mContext.getString(R.string.quick_settings_connecting);
}
- item.tag = device;
items[i++] = item;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FgsManagerTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FgsManagerTile.kt
index 75cf4d1..939a297 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FgsManagerTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FgsManagerTile.kt
@@ -54,7 +54,7 @@
qsLogger: QSLogger?,
private val fgsManagerDialogFactory: FgsManagerDialogFactory,
private val runningFgsController: RunningFgsController
-) : QSTileImpl<QSTile.State?>(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+) : QSTileImpl<QSTile.State>(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger), RunningFgsController.Callback {
override fun handleInitialize() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index e79ca0c..da0069f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -378,26 +378,26 @@
@Override
public void onAccessPointsChanged(final List<WifiEntry> accessPoints) {
- mAccessPoints = accessPoints.toArray(new WifiEntry[accessPoints.size()]);
- filterUnreachableAPs();
+ mAccessPoints = filterUnreachableAPs(accessPoints);
updateItems();
}
/** Filter unreachable APs from mAccessPoints */
- private void filterUnreachableAPs() {
+ private WifiEntry[] filterUnreachableAPs(List<WifiEntry> unfiltered) {
int numReachable = 0;
- for (WifiEntry ap : mAccessPoints) {
+ for (WifiEntry ap : unfiltered) {
if (isWifiEntryReachable(ap)) numReachable++;
}
- if (numReachable != mAccessPoints.length) {
- WifiEntry[] unfiltered = mAccessPoints;
- mAccessPoints = new WifiEntry[numReachable];
+ if (numReachable != unfiltered.size()) {
+ WifiEntry[] accessPoints = new WifiEntry[numReachable];
int i = 0;
for (WifiEntry ap : unfiltered) {
- if (isWifiEntryReachable(ap)) mAccessPoints[i++] = ap;
+ if (isWifiEntryReachable(ap)) accessPoints[i++] = ap;
}
+ return accessPoints;
}
+ return unfiltered.toArray(new WifiEntry[0]);
}
@Override
@@ -454,10 +454,7 @@
items = new Item[mAccessPoints.length];
for (int i = 0; i < mAccessPoints.length; i++) {
final WifiEntry ap = mAccessPoints[i];
- final Item item = new Item();
- item.tag = ap;
- item.iconResId = mWifiController.getIcon(ap);
- item.line1 = ap.getSsid();
+ final Item item = new Item(mWifiController.getIcon(ap), ap.getSsid(), ap);
item.line2 = ap.getSummary();
item.icon2 = ap.getSecurity() != AccessPoint.SECURITY_NONE
? R.drawable.qs_ic_wifi_lock
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index e3f085c..2a39849 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -303,15 +303,11 @@
if (DEBUG) {
Log.d(TAG, "updateDialog");
}
- if (mInternetDialogController.isAirplaneModeEnabled()) {
- mInternetDialogSubTitle.setVisibility(View.GONE);
- mAirplaneModeLayout.setVisibility(View.VISIBLE);
- } else {
- mInternetDialogTitle.setText(getDialogTitleText());
- mInternetDialogSubTitle.setVisibility(View.VISIBLE);
- mInternetDialogSubTitle.setText(getSubtitleText());
- mAirplaneModeLayout.setVisibility(View.GONE);
- }
+ mInternetDialogTitle.setText(getDialogTitleText());
+ mInternetDialogSubTitle.setText(getSubtitleText());
+ mAirplaneModeLayout.setVisibility(
+ mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
+
updateEthernet();
if (shouldUpdateMobileNetwork) {
setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular(),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 1fee1b4..dd742b8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -90,6 +90,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
@@ -316,15 +317,11 @@
}
CharSequence getSubtitleText(boolean isProgressBarVisible) {
- if (isAirplaneModeEnabled()) {
- return null;
- }
-
if (mCanConfigWifi && !mWifiManager.isWifiEnabled()) {
- // When the airplane mode is off and Wi-Fi is disabled.
+ // When Wi-Fi is disabled.
// Sub-Title: Wi-Fi is off
if (DEBUG) {
- Log.d(TAG, "Airplane mode off + Wi-Fi off.");
+ Log.d(TAG, "Wi-Fi off.");
}
return mContext.getText(SUBTITLE_TEXT_WIFI_IS_OFF);
}
@@ -489,6 +486,11 @@
private Map<Integer, CharSequence> getUniqueSubscriptionDisplayNames(Context context) {
class DisplayInfo {
+ DisplayInfo(SubscriptionInfo subscriptionInfo, CharSequence originalName) {
+ this.subscriptionInfo = subscriptionInfo;
+ this.originalName = originalName;
+ }
+
public SubscriptionInfo subscriptionInfo;
public CharSequence originalName;
public CharSequence uniqueName;
@@ -502,12 +504,7 @@
// Filter out null values.
return (i != null && i.getDisplayName() != null);
})
- .map(i -> {
- DisplayInfo info = new DisplayInfo();
- info.subscriptionInfo = i;
- info.originalName = i.getDisplayName().toString().trim();
- return info;
- });
+ .map(i -> new DisplayInfo(i, i.getDisplayName().toString().trim()));
// A Unique set of display names
Set<CharSequence> uniqueNames = new HashSet<>();
@@ -586,7 +583,7 @@
return "";
}
- int resId = mapIconSets(config).get(iconKey).dataContentDescription;
+ int resId = Objects.requireNonNull(mapIconSets(config).get(iconKey)).dataContentDescription;
if (isCarrierNetworkActive()) {
SignalIcon.MobileIconGroup carrierMergedWifiIconGroup =
TelephonyIcons.CARRIER_MERGED_WIFI;
@@ -882,10 +879,8 @@
if (accessPoints == null || accessPoints.size() == 0) {
mConnectedEntry = null;
mWifiEntriesCount = 0;
- if (mCallback != null) {
- mCallback.onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry */,
- false /* hasMoreEntry */);
- }
+ mCallback.onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry */,
+ false /* hasMoreEntry */);
return;
}
@@ -917,9 +912,7 @@
mConnectedEntry = connectedEntry;
mWifiEntriesCount = wifiEntries.size();
- if (mCallback != null) {
- mCallback.onAccessPointsChanged(wifiEntries, mConnectedEntry, hasMoreEntry);
- }
+ mCallback.onAccessPointsChanged(wifiEntries, mConnectedEntry, hasMoreEntry);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 3ed7e84..e7cd1e2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -76,6 +76,7 @@
import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.Dumpable;
@@ -88,6 +89,7 @@
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.navigationbar.buttons.KeyButtonView;
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.shared.recents.IOverviewProxy;
@@ -161,6 +163,7 @@
private final Optional<StartingSurface> mStartingSurface;
private final SmartspaceTransitionController mSmartspaceTransitionController;
private final Optional<RecentTasks> mRecentTasks;
+ private final UiEventLogger mUiEventLogger;
private Region mActiveNavBarRegion;
@@ -248,6 +251,7 @@
mContext.getSystemService(InputMethodManager.class)
.showInputMethodPickerFromSystem(true /* showAuxiliarySubtypes */,
DEFAULT_DISPLAY);
+ mUiEventLogger.log(KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP);
}
@Override
@@ -560,6 +564,7 @@
ShellTransitions shellTransitions,
ScreenLifecycle screenLifecycle,
SmartspaceTransitionController smartspaceTransitionController,
+ UiEventLogger uiEventLogger,
DumpManager dumpManager) {
super(broadcastDispatcher);
mContext = context;
@@ -581,6 +586,7 @@
mOneHandedOptional = oneHandedOptional;
mShellTransitions = shellTransitions;
mRecentTasks = recentTasks;
+ mUiEventLogger = uiEventLogger;
// Assumes device always starts with back button until launcher tells it that it does not
mNavBarButtonAlpha = 1.0f;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 85bf98c..7f130cb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -16,6 +16,7 @@
package com.android.systemui.recents;
+import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
@@ -248,8 +249,8 @@
.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
View buttons = mLayout.findViewById(R.id.screen_pinning_buttons);
WindowManagerWrapper wm = WindowManagerWrapper.getInstance();
- if (!QuickStepContract.isGesturalMode(mNavBarMode)
- && wm.hasSoftNavigationBar(mContext.getDisplayId())) {
+ if (!QuickStepContract.isGesturalMode(mNavBarMode)
+ && wm.hasSoftNavigationBar(mContext.getDisplayId()) && !isTablet(mContext)) {
buttons.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
swapChildrenIfRtlAndVertical(buttons);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index f43d9c3..d7b4738 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -887,7 +887,13 @@
mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
}
} else {
- showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+ if (mKeyguardUpdateMonitor.isUdfpsSupported()
+ && mKeyguardUpdateMonitor.getUserCanSkipBouncer(
+ KeyguardUpdateMonitor.getCurrentUser())) {
+ showBiometricMessage(mContext.getString(R.string.keyguard_unlock_press));
+ } else {
+ showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt
new file mode 100644
index 0000000..a1d086b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.charging
+
+import android.graphics.Color
+import android.graphics.PointF
+import android.graphics.RuntimeShader
+import android.util.MathUtils
+
+/**
+ * Shader class that renders a distorted ripple for the UDFPS dwell effect.
+ * Adjustable shader parameters:
+ * - progress
+ * - origin
+ * - color
+ * - time
+ * - maxRadius
+ * - distortionStrength.
+ * See per field documentation for more details.
+ *
+ * Modeled after frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java.
+ */
+class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) {
+ companion object {
+ private const val SHADER_UNIFORMS = """uniform vec2 in_origin;
+ uniform float in_time;
+ uniform float in_radius;
+ uniform float in_blur;
+ uniform vec4 in_color;
+ uniform float in_phase1;
+ uniform float in_phase2;
+ uniform float in_distortion_strength;"""
+ private const val SHADER_LIB = """
+ float softCircle(vec2 uv, vec2 xy, float radius, float blur) {
+ float blurHalf = blur * 0.5;
+ float d = distance(uv, xy);
+ return 1. - smoothstep(1. - blurHalf, 1. + blurHalf, d / radius);
+ }
+
+ float softRing(vec2 uv, vec2 xy, float radius, float blur) {
+ float thickness_half = radius * 0.25;
+ float circle_outer = softCircle(uv, xy, radius + thickness_half, blur);
+ float circle_inner = softCircle(uv, xy, radius - thickness_half, blur);
+ return circle_outer - circle_inner;
+ }
+
+ vec2 distort(vec2 p, float time, float distort_amount_xy, float frequency) {
+ return p + vec2(sin(p.x * frequency + in_phase1),
+ cos(p.y * frequency * 1.23 + in_phase2)) * distort_amount_xy;
+ }
+
+ vec4 ripple(vec2 p, float distort_xy, float frequency) {
+ vec2 p_distorted = distort(p, in_time, distort_xy, frequency);
+ float circle = softCircle(p_distorted, in_origin, in_radius * 1.2, in_blur);
+ float rippleAlpha = max(circle,
+ softRing(p_distorted, in_origin, in_radius, in_blur)) * 0.25;
+ return in_color * rippleAlpha;
+ }
+ """
+ private const val SHADER_MAIN = """vec4 main(vec2 p) {
+ vec4 color1 = ripple(p,
+ 12 * in_distortion_strength, // distort_xy
+ 0.012 // frequency
+ );
+ vec4 color2 = ripple(p,
+ 17.5 * in_distortion_strength, // distort_xy
+ 0.018 // frequency
+ );
+ // Alpha blend between two layers.
+ return vec4(color1.xyz + color2.xyz
+ * (1 - color1.w), color1.w + color2.w * (1-color1.w));
+ }"""
+ private const val SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN
+ }
+
+ /**
+ * Maximum radius of the ripple.
+ */
+ var maxRadius: Float = 0.0f
+
+ /**
+ * Origin coordinate of the ripple.
+ */
+ var origin: PointF = PointF()
+ set(value) {
+ field = value
+ setUniform("in_origin", floatArrayOf(value.x, value.y))
+ }
+
+ /**
+ * Progress of the ripple. Float value between [0, 1].
+ */
+ var progress: Float = 0.0f
+ set(value) {
+ field = value
+ setUniform("in_radius",
+ (1 - (1 - value) * (1 - value) * (1 - value))* maxRadius)
+ setUniform("in_blur", MathUtils.lerp(1f, 0.7f, value))
+ }
+
+ /**
+ * Distortion strength between [0, 1], with 0 being no distortion and 1 being full distortion.
+ */
+ var distortionStrength: Float = 0.0f
+ set(value) {
+ field = value
+ setUniform("in_distortion_strength", value)
+ }
+
+ /**
+ * Play time since the start of the effect in seconds.
+ */
+ var time: Float = 0.0f
+ set(value) {
+ field = value * 0.001f
+ setUniform("in_time", field)
+ setUniform("in_phase1", field * 2f + 0.367f)
+ setUniform("in_phase2", field * 5.2f * 1.531f)
+ }
+
+ /**
+ * A hex value representing the ripple color, in the format of ARGB
+ */
+ var color: Int = 0xffffff.toInt()
+ set(value) {
+ field = value
+ val color = Color.valueOf(value)
+ setUniform("in_color", floatArrayOf(color.red(),
+ color.green(), color.blue(), color.alpha()))
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index ce86953..962c7fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -102,12 +102,17 @@
configurationController.addCallback(object : ConfigurationController.ConfigurationListener {
override fun onLayoutDirectionChanged(isRtl: Boolean) {
- synchronized(this) {
- val corner = selectDesignatedCorner(nextViewState.rotation, isRtl)
- nextViewState = nextViewState.copy(
- layoutRtl = isRtl,
- designatedCorner = corner
- )
+ uiExecutor?.execute {
+ // If rtl changed, hide all dotes until the next state resolves
+ setCornerVisibilities(View.INVISIBLE)
+
+ synchronized(this) {
+ val corner = selectDesignatedCorner(nextViewState.rotation, isRtl)
+ nextViewState = nextViewState.copy(
+ layoutRtl = isRtl,
+ designatedCorner = corner
+ )
+ }
}
}
})
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 0389a7b..f500d39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -45,6 +45,7 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.NotificationUiAdjustment;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationRanker;
@@ -107,6 +108,7 @@
private final LeakDetector mLeakDetector;
private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
private final IStatusBarService mStatusBarService;
+ private final NotifLiveDataStoreImpl mNotifLiveDataStore;
private final DumpManager mDumpManager;
private final Set<NotificationEntry> mAllNotifications = new ArraySet<>();
@@ -154,6 +156,7 @@
LeakDetector leakDetector,
ForegroundServiceDismissalFeatureController fgsFeatureController,
IStatusBarService statusBarService,
+ NotifLiveDataStoreImpl notifLiveDataStore,
DumpManager dumpManager
) {
mLogger = logger;
@@ -164,6 +167,7 @@
mLeakDetector = leakDetector;
mFgsFeatureController = fgsFeatureController;
mStatusBarService = statusBarService;
+ mNotifLiveDataStore = notifLiveDataStore;
mDumpManager = dumpManager;
}
@@ -725,9 +729,10 @@
return;
}
reapplyFilterAndSort(reason);
- if (mPresenter != null && !mNotifPipelineFlags.isNewPipelineEnabled()) {
+ if (mPresenter != null) {
mPresenter.updateNotificationViews(reason);
}
+ mNotifLiveDataStore.setActiveNotifList(getVisibleNotifications());
}
public void updateNotificationRanking(RankingMap rankingMap) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
index 1940cb2..54f1380 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -34,6 +34,7 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.provider.DebugModeFilterProvider;
import javax.inject.Inject;
@@ -44,6 +45,7 @@
@SysUISingleton
public class NotificationFilter {
+ private final DebugModeFilterProvider mDebugNotificationFilter;
private final StatusBarStateController mStatusBarStateController;
private final KeyguardEnvironment mKeyguardEnvironment;
private final ForegroundServiceController mForegroundServiceController;
@@ -52,11 +54,13 @@
@Inject
public NotificationFilter(
+ DebugModeFilterProvider debugNotificationFilter,
StatusBarStateController statusBarStateController,
KeyguardEnvironment keyguardEnvironment,
ForegroundServiceController foregroundServiceController,
NotificationLockscreenUserManager userManager,
MediaFeatureFlag mediaFeatureFlag) {
+ mDebugNotificationFilter = debugNotificationFilter;
mStatusBarStateController = statusBarStateController;
mKeyguardEnvironment = keyguardEnvironment;
mForegroundServiceController = foregroundServiceController;
@@ -69,6 +73,10 @@
*/
public boolean shouldFilterOut(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
+ if (mDebugNotificationFilter.shouldFilterOut(entry)) {
+ return true;
+ }
+
if (!(mKeyguardEnvironment.isDeviceProvisioned()
|| showNotificationEvenIfUnprovisioned(sbn))) {
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStore.kt
new file mode 100644
index 0000000..ef00627
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStore.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import androidx.lifecycle.Observer
+
+/**
+ * An object which provides pieces of information about the notification shade.
+ *
+ * Note that individual fields of this object are updated together before synchronous observers are
+ * notified, so synchronous observers of two fields can be assured that they will see consistent
+ * results: e.g. if [hasActiveNotifs] is false then [activeNotifList] will be empty, and vice versa.
+ *
+ * This interface is read-only.
+ */
+interface NotifLiveDataStore {
+ val hasActiveNotifs: NotifLiveData<Boolean>
+ val activeNotifCount: NotifLiveData<Int>
+ val activeNotifList: NotifLiveData<List<NotificationEntry>>
+}
+
+/**
+ * An individual value which can be accessed directly, or observed for changes either synchronously
+ * or asynchronously.
+ *
+ * This interface is read-only.
+ */
+interface NotifLiveData<T> {
+ /** Access the current value */
+ val value: T
+ /** Add an observer which will be invoked synchronously when the value is changed. */
+ fun addSyncObserver(observer: Observer<T>)
+ /** Add an observer which will be invoked asynchronously after the value has changed */
+ fun addAsyncObserver(observer: Observer<T>)
+ /** Remove an observer previously added with [addSyncObserver] or [addAsyncObserver]. */
+ fun removeObserver(observer: Observer<T>)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImpl.kt
new file mode 100644
index 0000000..8aa6b81
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImpl.kt
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import androidx.lifecycle.Observer
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.util.Assert
+import com.android.systemui.util.ListenerSet
+import com.android.systemui.util.isNotEmpty
+import com.android.systemui.util.traceSection
+import java.util.Collections.unmodifiableList
+import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicReference
+import javax.inject.Inject
+
+/** Writeable implementation of [NotifLiveDataStore] */
+@SysUISingleton
+class NotifLiveDataStoreImpl @Inject constructor(
+ @Main private val mainExecutor: Executor
+) : NotifLiveDataStore {
+ private val hasActiveNotifsPrivate = NotifLiveDataImpl(
+ name = "hasActiveNotifs",
+ initialValue = false,
+ mainExecutor
+ )
+ private val activeNotifCountPrivate = NotifLiveDataImpl(
+ name = "activeNotifCount",
+ initialValue = 0,
+ mainExecutor
+ )
+ private val activeNotifListPrivate = NotifLiveDataImpl(
+ name = "activeNotifList",
+ initialValue = listOf<NotificationEntry>(),
+ mainExecutor
+ )
+
+ override val hasActiveNotifs: NotifLiveData<Boolean> = hasActiveNotifsPrivate
+ override val activeNotifCount: NotifLiveData<Int> = activeNotifCountPrivate
+ override val activeNotifList: NotifLiveData<List<NotificationEntry>> = activeNotifListPrivate
+
+ /** Set the latest flattened list of notification entries. */
+ fun setActiveNotifList(flatEntryList: List<NotificationEntry>) {
+ traceSection("NotifLiveDataStore.setActiveNotifList") {
+ Assert.isMainThread()
+ val unmodifiableCopy = unmodifiableList(flatEntryList.toList())
+ // This ensures we set all values before dispatching to any observers
+ listOf(
+ activeNotifListPrivate.setValueAndProvideDispatcher(unmodifiableCopy),
+ activeNotifCountPrivate.setValueAndProvideDispatcher(unmodifiableCopy.size),
+ hasActiveNotifsPrivate.setValueAndProvideDispatcher(unmodifiableCopy.isNotEmpty())
+ ).forEach { dispatcher -> dispatcher.invoke() }
+ }
+ }
+}
+
+/** Read-write implementation of [NotifLiveData] */
+class NotifLiveDataImpl<T>(
+ private val name: String,
+ initialValue: T,
+ @Main private val mainExecutor: Executor
+) : NotifLiveData<T> {
+ private val syncObservers = ListenerSet<Observer<T>>()
+ private val asyncObservers = ListenerSet<Observer<T>>()
+ private val atomicValue = AtomicReference(initialValue)
+ private var lastAsyncValue: T? = null
+
+ private fun dispatchToAsyncObservers() {
+ val value = atomicValue.get()
+ if (lastAsyncValue != value) {
+ lastAsyncValue = value
+ traceSection("NotifLiveData($name).dispatchToAsyncObservers") {
+ asyncObservers.forEach { it.onChanged(value) }
+ }
+ }
+ }
+
+ /**
+ * Access or set the current value.
+ *
+ * When setting, sync observers will be dispatched synchronously, and a task will be posted to
+ * dispatch the value to async observers.
+ */
+ override var value: T
+ get() = atomicValue.get()
+ set(value) = setValueAndProvideDispatcher(value).invoke()
+
+ /**
+ * Set the value, and return a function that when invoked will dispatch to the observers.
+ *
+ * This is intended to allow multiple instances with related data to be updated together and
+ * have their dispatchers invoked after all data has been updated.
+ */
+ fun setValueAndProvideDispatcher(value: T): () -> Unit {
+ val oldValue = atomicValue.getAndSet(value)
+ if (oldValue != value) {
+ return {
+ if (syncObservers.isNotEmpty()) {
+ traceSection("NotifLiveData($name).dispatchToSyncObservers") {
+ syncObservers.forEach { it.onChanged(value) }
+ }
+ }
+ if (asyncObservers.isNotEmpty()) {
+ mainExecutor.execute(::dispatchToAsyncObservers)
+ }
+ }
+ }
+ return {}
+ }
+
+ override fun addSyncObserver(observer: Observer<T>) {
+ syncObservers.addIfAbsent(observer)
+ }
+
+ override fun addAsyncObserver(observer: Observer<T>) {
+ asyncObservers.addIfAbsent(observer)
+ }
+
+ override fun removeObserver(observer: Observer<T>) {
+ syncObservers.remove(observer)
+ asyncObservers.remove(observer)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
index 6fbed9a8..5ada7a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
@@ -245,36 +245,4 @@
fun getInternalNotifUpdater(name: String?): InternalNotifUpdater {
return mNotifCollection.getInternalNotifUpdater(name)
}
-
- /**
- * Returns a read-only view in to the current shade list, i.e. the list of notifications that
- * are currently present in the shade.
- * @throws IllegalStateException if called during pipeline execution.
- */
- val shadeList: List<ListEntry>
- get() = mShadeListBuilder.shadeList
-
- /**
- * Constructs a flattened representation of the notification tree, where each group will have
- * the summary (if present) followed by the children.
- * @throws IllegalStateException if called during pipeline execution.
- */
- fun getFlatShadeList(): List<NotificationEntry> = shadeList.flatMap { entry ->
- when (entry) {
- is NotificationEntry -> sequenceOf(entry)
- is GroupEntry -> sequenceOf(entry.summary).filterNotNull() + entry.children
- else -> throw RuntimeException("Unexpected entry $entry")
- }
- }
-
- /**
- * Returns the number of notifications currently shown in the shade. This includes all
- * children and summary notifications.
- * @throws IllegalStateException if called during pipeline execution.
- */
- fun getShadeListCount(): Int = shadeList.sumOf { entry ->
- // include the summary in the count
- if (entry is GroupEntry) 1 + entry.children.size
- else 1
- }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt
new file mode 100644
index 0000000..8e307ec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.render.requireSummary
+import javax.inject.Inject
+
+/**
+ * A small coordinator which updates the notif stack (the view layer which holds notifications)
+ * with high-level data after the stack is populated with the final entries.
+ */
+@CoordinatorScope
+class DataStoreCoordinator @Inject internal constructor(
+ private val notifLiveDataStoreImpl: NotifLiveDataStoreImpl
+) : Coordinator {
+
+ override fun attach(pipeline: NotifPipeline) {
+ pipeline.addOnAfterRenderListListener { entries, _ -> onAfterRenderList(entries) }
+ }
+
+ fun onAfterRenderList(entries: List<ListEntry>) {
+ val flatEntryList = flattenedEntryList(entries)
+ notifLiveDataStoreImpl.setActiveNotifList(flatEntryList)
+ }
+
+ private fun flattenedEntryList(entries: List<ListEntry>) =
+ mutableListOf<NotificationEntry>().also { list ->
+ entries.forEach { entry ->
+ when (entry) {
+ is NotificationEntry -> list.add(entry)
+ is GroupEntry -> {
+ list.add(entry.requireSummary)
+ list.addAll(entry.children)
+ }
+ else -> error("Unexpected entry $entry")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DebugModeCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DebugModeCoordinator.kt
new file mode 100644
index 0000000..df54ccd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DebugModeCoordinator.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
+import com.android.systemui.statusbar.notification.collection.provider.DebugModeFilterProvider
+import javax.inject.Inject
+
+/** A small coordinator which filters out notifications from non-allowed apps. */
+@CoordinatorScope
+class DebugModeCoordinator @Inject constructor(
+ private val debugModeFilterProvider: DebugModeFilterProvider
+) : Coordinator {
+
+ override fun attach(pipeline: NotifPipeline) {
+ pipeline.addPreGroupFilter(preGroupFilter)
+ debugModeFilterProvider.registerInvalidationListener(preGroupFilter::invalidateList)
+ }
+
+ private val preGroupFilter = object : NotifFilter("DebugModeCoordinator") {
+ override fun shouldFilterOut(entry: NotificationEntry, now: Long) =
+ debugModeFilterProvider.shouldFilterOut(entry)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index a16b565..d21d5a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -35,6 +35,7 @@
class NotifCoordinatorsImpl @Inject constructor(
dumpManager: DumpManager,
notifPipelineFlags: NotifPipelineFlags,
+ dataStoreCoordinator: DataStoreCoordinator,
hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator,
hideNotifsForOtherUsersCoordinator: HideNotifsForOtherUsersCoordinator,
keyguardCoordinator: KeyguardCoordinator,
@@ -44,10 +45,12 @@
bubbleCoordinator: BubbleCoordinator,
headsUpCoordinator: HeadsUpCoordinator,
gutsCoordinator: GutsCoordinator,
+ communalCoordinator: CommunalCoordinator,
conversationCoordinator: ConversationCoordinator,
- preparationCoordinator: PreparationCoordinator,
+ debugModeCoordinator: DebugModeCoordinator,
groupCountCoordinator: GroupCountCoordinator,
mediaCoordinator: MediaCoordinator,
+ preparationCoordinator: PreparationCoordinator,
remoteInputCoordinator: RemoteInputCoordinator,
rowAppearanceCoordinator: RowAppearanceCoordinator,
stackCoordinator: StackCoordinator,
@@ -55,7 +58,6 @@
smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator,
viewConfigCoordinator: ViewConfigCoordinator,
visualStabilityCoordinator: VisualStabilityCoordinator,
- communalCoordinator: CommunalCoordinator,
sensitiveContentCoordinator: SensitiveContentCoordinator
) : NotifCoordinators {
@@ -67,6 +69,16 @@
*/
init {
dumpManager.registerDumpable(TAG, this)
+
+ // TODO(b/208866714): formalize the system by which some coordinators may be required by the
+ // pipeline, such as this DataStoreCoordinator which cannot be removed, as it's a critical
+ // glue between the pipeline and parts of SystemUI which depend on pipeline output via the
+ // NotifLiveDataStore.
+ if (notifPipelineFlags.isNewPipelineEnabled()) {
+ mCoordinators.add(dataStoreCoordinator)
+ }
+
+ // Attach normal coordinators.
mCoordinators.add(hideLocallyDismissedNotifsCoordinator)
mCoordinators.add(hideNotifsForOtherUsersCoordinator)
mCoordinators.add(keyguardCoordinator)
@@ -74,6 +86,8 @@
mCoordinators.add(appOpsCoordinator)
mCoordinators.add(deviceProvisionedCoordinator)
mCoordinators.add(bubbleCoordinator)
+ mCoordinators.add(communalCoordinator)
+ mCoordinators.add(debugModeCoordinator)
mCoordinators.add(conversationCoordinator)
mCoordinators.add(groupCountCoordinator)
mCoordinators.add(mediaCoordinator)
@@ -83,7 +97,6 @@
mCoordinators.add(shadeEventCoordinator)
mCoordinators.add(viewConfigCoordinator)
mCoordinators.add(visualStabilityCoordinator)
- mCoordinators.add(communalCoordinator)
mCoordinators.add(sensitiveContentCoordinator)
if (notifPipelineFlags.isSmartspaceDedupingEnabled()) {
mCoordinators.add(smartspaceDedupingCoordinator)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index 38f11fc..c6a8a69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -19,8 +19,8 @@
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
-import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
+import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import javax.inject.Inject
@@ -49,10 +49,12 @@
var hasNonClearableSilentNotifs = false
var hasClearableSilentNotifs = false
entries.forEach {
- val isSilent = it.section!!.bucket == BUCKET_SILENT
+ val section = checkNotNull(it.section) { "Null section for ${it.key}" }
+ val entry = checkNotNull(it.representativeEntry) { "Null notif entry for ${it.key}" }
+ val isSilent = section.bucket == BUCKET_SILENT
// NOTE: NotificationEntry.isClearable will internally check group children to ensure
// the group itself definitively clearable.
- val isClearable = it.representativeEntry!!.isClearable
+ val isClearable = entry.isClearable
when {
isSilent && isClearable -> hasClearableSilentNotifs = true
isSilent && !isClearable -> hasNonClearableSilentNotifs = true
@@ -60,13 +62,12 @@
!isSilent && !isClearable -> hasNonClearableAlertingNotifs = true
}
}
- val stats = NotifStats(
+ return NotifStats(
numActiveNotifs = entries.size,
hasNonClearableAlertingNotifs = hasNonClearableAlertingNotifs,
hasClearableAlertingNotifs = hasClearableAlertingNotifs,
hasNonClearableSilentNotifs = hasNonClearableSilentNotifs,
hasClearableSilentNotifs = hasClearableSilentNotifs
)
- return stats
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationVisibilityProvider.kt
deleted file mode 100644
index 5c70f32..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationVisibilityProvider.kt
+++ /dev/null
@@ -1,50 +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.
- */
-
-package com.android.systemui.statusbar.notification.collection.legacy
-
-import com.android.internal.statusbar.NotificationVisibility
-import com.android.systemui.statusbar.notification.NotificationEntryManager
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider
-import com.android.systemui.statusbar.notification.logging.NotificationLogger
-import javax.inject.Inject
-
-/** Legacy pipeline implementation for getting [NotificationVisibility]. */
-class LegacyNotificationVisibilityProvider @Inject constructor(
- private val notifEntryManager: NotificationEntryManager
-) : NotificationVisibilityProvider {
- override fun obtain(entry: NotificationEntry, visible: Boolean): NotificationVisibility {
- val count: Int = notifEntryManager.activeNotificationsCount
- val rank = entry.ranking.rank
- val hasRow = entry.row != null
- val location = NotificationLogger.getNotificationLocation(entry)
- return NotificationVisibility.obtain(entry.key, rank, count, visible && hasRow, location)
- }
-
- override fun obtain(key: String, visible: Boolean): NotificationVisibility {
- val entry: NotificationEntry? = notifEntryManager.getActiveNotificationUnfiltered(key)
- val count: Int = notifEntryManager.activeNotificationsCount
- val rank = entry?.ranking?.rank ?: -1
- val hasRow = entry?.row != null
- val location = NotificationLogger.getNotificationLocation(entry)
- return NotificationVisibility.obtain(key, rank, count, visible && hasRow, location)
- }
-
- override fun getLocation(key: String): NotificationVisibility.NotificationLocation =
- NotificationLogger.getNotificationLocation(
- notifEntryManager.getActiveNotificationUnfiltered(key))
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DebugModeFilterProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DebugModeFilterProvider.kt
new file mode 100644
index 0000000..d16d76a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DebugModeFilterProvider.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.notification.collection.provider
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Build
+import android.util.Log
+import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.util.Assert
+import com.android.systemui.util.ListenerSet
+import com.android.systemui.util.isNotEmpty
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * A debug mode provider which is used by both the legacy and new notification pipelines to
+ * block unwanted notifications from appearing to the user, primarily for integration testing.
+ *
+ * The only configuration is a list of allowed packages. When this list is empty, the feature is
+ * disabled. When SystemUI starts up, this feature is disabled.
+ *
+ * To enabled filtering, provide the list of packages in a comma-separated list using the command:
+ *
+ * `$ adb shell am broadcast -a com.android.systemui.action.SET_NOTIF_DEBUG_MODE
+ * --esal allowed_packages <comma-separated-packages>`
+ *
+ * To disable filtering, send the action without a list:
+ *
+ * `$ adb shell am broadcast -a com.android.systemui.action.SET_NOTIF_DEBUG_MODE`
+ *
+ * NOTE: this feature only works on debug builds, and when the broadcaster is root.
+ */
+@SysUISingleton
+class DebugModeFilterProvider @Inject constructor(
+ private val context: Context,
+ dumpManager: DumpManager
+) : Dumpable {
+ private var allowedPackages: List<String> = emptyList()
+ private val listeners = ListenerSet<Runnable>()
+
+ init {
+ dumpManager.registerDumpable(this)
+ }
+
+ /**
+ * Register a runnable to be invoked when the allowed packages changes, which would mean the
+ * result of [shouldFilterOut] may have changed for some entries.
+ */
+ fun registerInvalidationListener(listener: Runnable) {
+ Assert.isMainThread()
+ if (!Build.isDebuggable()) {
+ return
+ }
+ val needsInitialization = listeners.isEmpty()
+ listeners.addIfAbsent(listener)
+ if (needsInitialization) {
+ val filter = IntentFilter().apply { addAction(ACTION_SET_NOTIF_DEBUG_MODE) }
+ val permission = NOTIF_DEBUG_MODE_PERMISSION
+ context.registerReceiver(mReceiver, filter, permission, null)
+ Log.d(TAG, "Registered: $mReceiver")
+ }
+ }
+
+ /**
+ * Determine if the given entry should be hidden from the user in debug mode.
+ * Will always return false in release.
+ */
+ fun shouldFilterOut(entry: NotificationEntry): Boolean {
+ if (allowedPackages.isEmpty()) {
+ return false
+ }
+ return entry.sbn.packageName !in allowedPackages
+ }
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+ pw.println("initialized: ${listeners.isNotEmpty()}")
+ pw.println("allowedPackages: ${allowedPackages.size}")
+ allowedPackages.forEachIndexed { i, pkg ->
+ pw.println(" [$i]: $pkg")
+ }
+ }
+
+ private val mReceiver: BroadcastReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent?) {
+ val action = intent?.action
+ if (ACTION_SET_NOTIF_DEBUG_MODE == action) {
+ allowedPackages = intent.extras?.getStringArrayList(EXTRA_ALLOWED_PACKAGES)
+ ?: emptyList()
+ Log.d(TAG, "Updated allowedPackages: $allowedPackages")
+ listeners.forEach(Runnable::run)
+ } else {
+ Log.d(TAG, "Malformed intent: $intent")
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "DebugModeFilterProvider"
+ private const val ACTION_SET_NOTIF_DEBUG_MODE =
+ "com.android.systemui.action.SET_NOTIF_DEBUG_MODE"
+ private const val NOTIF_DEBUG_MODE_PERMISSION =
+ "com.android.systemui.permission.NOTIF_DEBUG_MODE"
+ private const val EXTRA_ALLOWED_PACKAGES = "allowed_packages"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotificationVisibilityProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt
similarity index 65%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotificationVisibilityProviderImpl.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt
index 51de08d..6a1e36f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotificationVisibilityProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt
@@ -14,20 +14,24 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.collection.render
+package com.android.systemui.statusbar.notification.collection.provider
import com.android.internal.statusbar.NotificationVisibility
-import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider
import com.android.systemui.statusbar.notification.logging.NotificationLogger
import javax.inject.Inject
-/** New pipeline implementation for getting [NotificationVisibility]. */
+/** pipeline-agnostic implementation for getting [NotificationVisibility]. */
class NotificationVisibilityProviderImpl @Inject constructor(
- private val notifPipeline: NotifPipeline
+ private val notifDataStore: NotifLiveDataStore,
+ private val notifCollection: CommonNotifCollection
) : NotificationVisibilityProvider {
+
override fun obtain(entry: NotificationEntry, visible: Boolean): NotificationVisibility {
- val count: Int = notifPipeline.getShadeListCount()
+ val count: Int = getCount()
val rank = entry.ranking.rank
val hasRow = entry.row != null
val location = NotificationLogger.getNotificationLocation(entry)
@@ -35,9 +39,11 @@
}
override fun obtain(key: String, visible: Boolean): NotificationVisibility =
- notifPipeline.getEntry(key)?.let { return obtain(it, visible) }
- ?: NotificationVisibility.obtain(key, -1, notifPipeline.getShadeListCount(), false)
+ notifCollection.getEntry(key)?.let { return obtain(it, visible) }
+ ?: NotificationVisibility.obtain(key, -1, getCount(), false)
override fun getLocation(key: String): NotificationVisibility.NotificationLocation =
- NotificationLogger.getNotificationLocation(notifPipeline.getEntry(key))
+ NotificationLogger.getNotificationLocation(notifCollection.getEntry(key))
+
+ private fun getCount() = notifDataStore.activeNotifCount.value
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index d25a2d3..f1cba34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -44,6 +44,8 @@
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.coordinator.ShadeEventCoordinator;
import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
@@ -52,12 +54,12 @@
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl;
import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationPresenterExtensions;
-import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.OnUserInteractionCallbackImplLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.collection.provider.NotificationVisibilityProviderImpl;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -65,7 +67,6 @@
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProviderImpl;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
@@ -122,6 +123,7 @@
LeakDetector leakDetector,
ForegroundServiceDismissalFeatureController fgsFeatureController,
IStatusBarService statusBarService,
+ NotifLiveDataStoreImpl notifLiveDataStore,
DumpManager dumpManager) {
return new NotificationEntryManager(
logger,
@@ -132,6 +134,7 @@
leakDetector,
fgsFeatureController,
statusBarService,
+ notifLiveDataStore,
dumpManager);
}
@@ -212,6 +215,7 @@
NotificationListener notificationListener,
@UiBackground Executor uiBgExecutor,
NotifPipelineFlags notifPipelineFlags,
+ NotifLiveDataStore notifLiveDataStore,
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
@@ -222,6 +226,7 @@
notificationListener,
uiBgExecutor,
notifPipelineFlags,
+ notifLiveDataStore,
visibilityProvider,
entryManager,
notifPipeline,
@@ -290,16 +295,10 @@
/**
* Provide the object which can be used to obtain NotificationVisibility objects.
*/
- @Provides
+ @Binds
@SysUISingleton
- static NotificationVisibilityProvider provideNotificationVisibilityProvider(
- NotifPipelineFlags notifPipelineFlags,
- Lazy<NotificationVisibilityProviderImpl> newProvider,
- Lazy<LegacyNotificationVisibilityProvider> legacyProvider) {
- return notifPipelineFlags.isNewPipelineEnabled()
- ? newProvider.get()
- : legacyProvider.get();
- }
+ NotificationVisibilityProvider provideNotificationVisibilityProvider(
+ NotificationVisibilityProviderImpl newProvider);
/**
* Provide the active implementation for presenting notifications.
@@ -356,4 +355,8 @@
/** */
@Binds
NotifInflater bindNotifInflater(NotifInflaterImpl notifInflaterImpl);
+
+ /** */
+ @Binds
+ NotifLiveDataStore bindNotifLiveDataStore(NotifLiveDataStoreImpl notifLiveDataStoreImpl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 212c342e..38f3c39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -28,12 +28,15 @@
import com.android.systemui.statusbar.notification.NotificationClicker
import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.NotificationListController
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager
import com.android.systemui.statusbar.notification.collection.TargetSdkResolver
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
+import com.android.systemui.statusbar.notification.collection.provider.DebugModeFilterProvider
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.interruption.HeadsUpController
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
@@ -63,10 +66,13 @@
private val notifPipelineFlags: NotifPipelineFlags,
private val notificationListener: NotificationListener,
private val entryManager: NotificationEntryManager,
+ private val debugModeFilterProvider: DebugModeFilterProvider,
private val legacyRanker: NotificationRankingManager,
+ private val commonNotifCollection: Lazy<CommonNotifCollection>,
private val notifPipeline: Lazy<NotifPipeline>,
+ private val notifLiveDataStore: NotifLiveDataStore,
private val targetSdkResolver: TargetSdkResolver,
- private val newNotifPipeline: Lazy<NotifPipelineInitializer>,
+ private val newNotifPipelineInitializer: Lazy<NotifPipelineInitializer>,
private val notifBindPipelineInitializer: NotifBindPipelineInitializer,
private val deviceProvisionedController: DeviceProvisionedController,
private val notificationRowBinder: NotificationRowBinderImpl,
@@ -111,7 +117,7 @@
animatedImageNotificationManager.bind()
if (INITIALIZE_NEW_PIPELINE) {
- newNotifPipeline.get().initialize(
+ newNotifPipelineInitializer.get().initialize(
notificationListener,
notificationRowBinder,
listContainer,
@@ -130,6 +136,9 @@
headsUpController.attach(entryManager, headsUpManager)
groupManagerLegacy.get().setHeadsUpManager(headsUpManager)
groupAlertTransferHelper.setHeadsUpManager(headsUpManager)
+ debugModeFilterProvider.registerInvalidationListener {
+ entryManager.updateNotifications("debug mode filter changed")
+ }
entryManager.initialize(notificationListener, legacyRanker)
}
@@ -155,14 +164,10 @@
}
override fun resetUserExpandedStates() {
- if (notifPipelineFlags.isNewPipelineEnabled()) {
- for (entry in notifPipeline.get().allNotifs) {
- entry.resetUserExpansion()
- }
- } else {
- for (entry in entryManager.visibleNotifications) {
- entry.resetUserExpansion()
- }
+ // TODO: this is a view thing that should be done through the views, but that means doing it
+ // both when this event is fired and any time a row is attached.
+ for (entry in commonNotifCollection.get().allNotifs) {
+ entry.resetUserExpansion()
}
}
@@ -177,11 +182,7 @@
}
override fun getActiveNotificationsCount(): Int =
- if (notifPipelineFlags.isNewPipelineEnabled()) {
- notifPipeline.get().getShadeListCount()
- } else {
- entryManager.activeNotificationsCount
- }
+ notifLiveDataStore.activeNotifCount.value
companion object {
// NOTE: The new pipeline is always active, even if the old pipeline is *rendering*.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
index f8d6c6d..b2e15f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
@@ -25,8 +25,8 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationMediaManager
import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.tuner.TunerService
@@ -44,7 +44,7 @@
private val headsUpManager: HeadsUpManagerPhone,
private val notificationLockscreenUserManager: NotificationLockscreenUserManager,
private val mediaManager: NotificationMediaManager,
- private val entryManager: NotificationEntryManager,
+ private val commonNotifCollection: CommonNotifCollection,
tunerService: TunerService
) : StatusBarStateController.StateListener, NotificationMediaManager.MediaListener {
@@ -77,8 +77,7 @@
override fun onPrimaryMetadataOrStateChanged(metadata: MediaMetadata?, state: Int) {
val previous = currentMediaEntry
- var newEntry = entryManager
- .getActiveNotificationUnfiltered(mediaManager.mediaNotificationKey)
+ var newEntry = commonNotifCollection.getEntry(mediaManager.mediaNotificationKey)
if (!NotificationMediaManager.isPlayingState(state)) {
newEntry = null
}
@@ -112,7 +111,7 @@
// filter notifications invisible on Keyguard
return false
}
- if (entryManager.getActiveNotificationUnfiltered(entry.key) != null) {
+ if (commonNotifCollection.getEntry(entry.key) != null) {
// filter notifications not the active list currently
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 52488da..9e8200b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -42,6 +42,7 @@
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
@@ -76,7 +77,7 @@
// Dependencies:
private final NotificationListenerService mNotificationListener;
private final Executor mUiBgExecutor;
- private final NotifPipelineFlags mNotifPipelineFlags;
+ private final NotifLiveDataStore mNotifLiveDataStore;
private final NotificationVisibilityProvider mVisibilityProvider;
private final NotificationEntryManager mEntryManager;
private final NotifPipeline mNotifPipeline;
@@ -179,11 +180,7 @@
};
private List<NotificationEntry> getVisibleNotifications() {
- if (mNotifPipelineFlags.isNewPipelineEnabled()) {
- return mNotifPipeline.getFlatShadeList();
- } else {
- return mEntryManager.getVisibleNotifications();
- }
+ return mNotifLiveDataStore.getActiveNotifList().getValue();
}
/**
@@ -223,6 +220,7 @@
public NotificationLogger(NotificationListener notificationListener,
@UiBackground Executor uiBgExecutor,
NotifPipelineFlags notifPipelineFlags,
+ NotifLiveDataStore notifLiveDataStore,
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
@@ -231,7 +229,7 @@
NotificationPanelLogger notificationPanelLogger) {
mNotificationListener = notificationListener;
mUiBgExecutor = uiBgExecutor;
- mNotifPipelineFlags = notifPipelineFlags;
+ mNotifLiveDataStore = notifLiveDataStore;
mVisibilityProvider = visibilityProvider;
mEntryManager = entryManager;
mNotifPipeline = notifPipeline;
@@ -242,7 +240,7 @@
// Not expected to be destroyed, don't need to unsubscribe
statusBarStateController.addCallback(this);
- if (mNotifPipelineFlags.isNewPipelineEnabled()) {
+ if (notifPipelineFlags.isNewPipelineEnabled()) {
registerNewPipelineListener();
} else {
registerLegacyListener();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 2eb2065..63cb4ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -579,14 +579,24 @@
if (contentView.hasOverlappingRendering()) {
int layerType = contentAlpha == 0.0f || contentAlpha == 1.0f ? LAYER_TYPE_NONE
: LAYER_TYPE_HARDWARE;
- int currentLayerType = contentView.getLayerType();
- if (currentLayerType != layerType) {
- contentView.setLayerType(layerType, null);
- }
+ contentView.setLayerType(layerType, null);
}
contentView.setAlpha(contentAlpha);
+ // After updating the current view, reset all views.
+ if (contentAlpha == 1f) {
+ resetAllContentAlphas();
+ }
}
+ /**
+ * If a subclass's {@link #getContentView()} returns different views depending on state,
+ * this method is an opportunity to reset the alpha of ALL content views, not just the
+ * current one, which may prevent a content view that is temporarily hidden from being reset.
+ *
+ * This should setAlpha(1.0f) and setLayerType(LAYER_TYPE_NONE) for all content views.
+ */
+ protected void resetAllContentAlphas() {}
+
@Override
protected void applyRoundness() {
super.applyRoundness();
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 82ed34c..f898470 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
@@ -2160,15 +2160,6 @@
}
public void setExpandAnimationRunning(boolean expandAnimationRunning) {
- View contentView;
- if (mIsSummaryWithChildren) {
- contentView = mChildrenContainer;
- } else {
- contentView = getShowingLayout();
- }
- if (mGuts != null && mGuts.isExposed()) {
- contentView = mGuts;
- }
if (expandAnimationRunning) {
setAboveShelf(true);
mExpandAnimationRunning = true;
@@ -2181,9 +2172,7 @@
if (mGuts != null) {
mGuts.setAlpha(1.0f);
}
- if (contentView != null) {
- contentView.setAlpha(1.0f);
- }
+ resetAllContentAlphas();
setExtraWidthForClipping(0.0f);
if (mNotificationParent != null) {
mNotificationParent.setExtraWidthForClipping(0.0f);
@@ -2632,10 +2621,8 @@
mPrivateLayout.animate().cancel();
if (mChildrenContainer != null) {
mChildrenContainer.animate().cancel();
- mChildrenContainer.setAlpha(1f);
}
- mPublicLayout.setAlpha(1f);
- mPrivateLayout.setAlpha(1f);
+ resetAllContentAlphas();
mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
updateChildrenVisibility();
} else {
@@ -2662,7 +2649,10 @@
.alpha(0f)
.setStartDelay(delay)
.setDuration(duration)
- .withEndAction(() -> hiddenView.setVisibility(View.INVISIBLE));
+ .withEndAction(() -> {
+ hiddenView.setVisibility(View.INVISIBLE);
+ resetAllContentAlphas();
+ });
}
for (View showView : shownChildren) {
showView.setVisibility(View.VISIBLE);
@@ -2785,12 +2775,7 @@
if (wasAppearing) {
// During the animation the visible view might have changed, so let's make sure all
// alphas are reset
- if (mChildrenContainer != null) {
- mChildrenContainer.setAlpha(1.0f);
- }
- for (NotificationContentView l : mLayouts) {
- l.setAlpha(1.0f);
- }
+ resetAllContentAlphas();
if (FADE_LAYER_OPTIMIZATION_ENABLED) {
setNotificationFaded(false);
} else {
@@ -2801,6 +2786,18 @@
}
}
+ @Override
+ protected void resetAllContentAlphas() {
+ mPrivateLayout.setAlpha(1f);
+ mPrivateLayout.setLayerType(LAYER_TYPE_NONE, null);
+ mPublicLayout.setAlpha(1f);
+ mPublicLayout.setLayerType(LAYER_TYPE_NONE, null);
+ if (mChildrenContainer != null) {
+ mChildrenContainer.setAlpha(1f);
+ mChildrenContainer.setLayerType(LAYER_TYPE_NONE, null);
+ }
+ }
+
/** Gets the last value set with {@link #setNotificationFaded(boolean)} */
@Override
public boolean isNotificationFaded() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index a9cc3237..e658468 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -59,6 +59,7 @@
private float mMaxHeadsUpTranslation;
private boolean mDismissAllInProgress;
private int mLayoutMinHeight;
+ private int mLayoutMaxHeight;
private NotificationShelf mShelf;
private int mZDistanceBetweenElements;
private int mBaseZHeight;
@@ -326,6 +327,14 @@
mLayoutHeight = layoutHeight;
}
+ public void setLayoutMaxHeight(int maxLayoutHeight) {
+ mLayoutMaxHeight = maxLayoutHeight;
+ }
+
+ public int getLayoutMaxHeight() {
+ return mLayoutMaxHeight;
+ }
+
public float getTopPadding() {
return mTopPadding;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index ffe6e4b..e1dbf4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -1111,6 +1111,7 @@
@ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
private void updateAlgorithmHeightAndPadding() {
mAmbientState.setLayoutHeight(getLayoutHeight());
+ mAmbientState.setLayoutMaxHeight(mMaxLayoutHeight);
updateAlgorithmLayoutMinHeight();
mAmbientState.setTopPadding(mTopPadding);
}
@@ -3972,6 +3973,7 @@
updateChronometers();
requestChildrenUpdate();
updateUseRoundedRectClipping();
+ updateDismissBehavior();
}
}
@@ -4935,6 +4937,10 @@
StringBuilder sb = new StringBuilder("[")
.append(this.getClass().getSimpleName()).append(":")
.append(" pulsing=").append(mPulsing ? "T" : "f")
+ .append(" expanded=").append(mIsExpanded ? "T" : "f")
+ .append(" headsUpPinned=").append(mInHeadsUpPinnedMode ? "T" : "f")
+ .append(" qsClipping=").append(mShouldUseRoundedRectClipping ? "T" : "f")
+ .append(" qsClipDismiss=").append(mDismissUsingRowTranslationX ? "T" : "f")
.append(" visibility=").append(DumpUtilsKt.visibilityString(getVisibility()))
.append(" alpha=").append(getAlpha())
.append(" scrollY=").append(mAmbientState.getScrollY())
@@ -5462,7 +5468,7 @@
// On the split keyguard, dismissing with clipping without a visual boundary looks odd,
// so let's use the content dismiss behavior instead.
boolean dismissUsingRowTranslationX = !mShouldUseSplitNotificationShade
- || mStatusBarState != StatusBarState.KEYGUARD;
+ || (mStatusBarState != StatusBarState.KEYGUARD && mIsExpanded);
if (mDismissUsingRowTranslationX != dismissUsingRowTranslationX) {
mDismissUsingRowTranslationX = dismissUsingRowTranslationX;
for (int i = 0; i < getChildCount(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 015edb8..2c70a5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -29,6 +29,7 @@
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.R;
import com.android.systemui.animation.ShadeInterpolation;
+import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -60,6 +61,7 @@
@VisibleForTesting float mHeadsUpInset;
private int mPinnedZTranslationExtra;
private float mNotificationScrimPadding;
+ private int mCloseHandleUnderlapHeight;
public StackScrollAlgorithm(
Context context,
@@ -85,6 +87,7 @@
R.dimen.heads_up_pinned_elevation);
mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
mNotificationScrimPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
+ mCloseHandleUnderlapHeight = res.getDimensionPixelSize(R.dimen.close_handle_underlap);
}
/**
@@ -459,13 +462,17 @@
&& !hasOngoingNotifs(algorithmState));
}
} else {
- if (view != ambientState.getTrackedHeadsUpRow()) {
+ if (view instanceof EmptyShadeView) {
+ float fullHeight = ambientState.getLayoutMaxHeight() + mCloseHandleUnderlapHeight
+ - ambientState.getStackY();
+ viewState.yTranslation = (fullHeight - getMaxAllowedChildHeight(view)) / 2f;
+ } else if (view != ambientState.getTrackedHeadsUpRow()) {
if (ambientState.isExpansionChanging()) {
// We later update shelf state, then hide views below the shelf.
viewState.hidden = false;
viewState.inShelf = algorithmState.firstViewInShelf != null
&& i >= algorithmState.visibleChildren.indexOf(
- algorithmState.firstViewInShelf);
+ algorithmState.firstViewInShelf);
} else if (ambientState.getShelf() != null) {
// When pulsing (incoming notification on AOD), innerHeight is 0; clamp all
// to shelf start, thereby hiding all notifications (except the first one, which
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index e3a4bf0..2dc9276 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -51,6 +51,7 @@
public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150;
public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 400;
public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 400;
+ public static final int ANIMATION_DURATION_FOLD_TO_AOD = 600;
public static final int ANIMATION_DURATION_PULSE_APPEAR =
KeyguardSliceView.DEFAULT_ANIM_DURATION;
public static final int ANIMATION_DURATION_BLOCKING_HELPER_FADE = 240;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index aa3b3e1..ad1c232 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -74,7 +74,7 @@
private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock";
private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
- private static final int FP_ATTEMPTS_BEFORE_SHOW_BOUNCER = 3;
+ private static final int FP_ATTEMPTS_BEFORE_SHOW_BOUNCER = 2;
@IntDef(prefix = { "MODE_" }, value = {
MODE_NONE,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 4b8b580..d87a024 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -243,6 +243,10 @@
return mScreenOffAnimationController.shouldShowLightRevealScrim();
}
+ public boolean shouldAnimateDozingChange() {
+ return mScreenOffAnimationController.shouldAnimateDozingChange();
+ }
+
/**
* Whether we're capable of controlling the screen off animation if we want to. This isn't
* possible if AOD isn't even enabled or if the flag is disabled.
@@ -324,6 +328,7 @@
for (Callback callback : mCallbacks) {
callback.onAlwaysOnChange();
}
+ mScreenOffAnimationController.onAlwaysOnChanged(getAlwaysOn());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
index c09cca1..c61510c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
@@ -22,7 +22,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.annotation.Nullable;
import android.view.InsetsVisibilities;
import android.view.View;
import android.view.WindowInsetsController.Appearance;
@@ -30,13 +29,12 @@
import android.view.WindowManager;
import android.view.animation.AccelerateInterpolator;
+import androidx.lifecycle.Observer;
+
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope;
import com.android.systemui.util.ViewController;
@@ -44,20 +42,21 @@
import javax.inject.Named;
/**
- * Apps can request a low profile mode {@link View.SYSTEM_UI_FLAG_LOW_PROFILE}
+ * Apps can request a low profile mode {@link View#SYSTEM_UI_FLAG_LOW_PROFILE}
* where status bar and navigation icons dim. In this mode, a notification dot appears
* where the notification icons would appear if they would be shown outside of this mode.
*
* This controller shows and hides the notification dot in the status bar to indicate
- * whether there are notifications when the device is in {@link View.SYSTEM_UI_FLAG_LOW_PROFILE}.
+ * whether there are notifications when the device is in {@link View#SYSTEM_UI_FLAG_LOW_PROFILE}.
*/
@StatusBarFragmentScope
public class LightsOutNotifController extends ViewController<View> {
private final CommandQueue mCommandQueue;
- private final NotificationEntryManager mEntryManager;
+ private final NotifLiveDataStore mNotifDataStore;
private final WindowManager mWindowManager;
+ private final Observer<Boolean> mObserver = hasNotifs -> updateLightsOutView();
- /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int) */
+ /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
@VisibleForTesting @Appearance int mAppearance;
private int mDisplayId;
@@ -66,18 +65,18 @@
LightsOutNotifController(
@Named(LIGHTS_OUT_NOTIF_VIEW) View lightsOutNotifView,
WindowManager windowManager,
- NotificationEntryManager entryManager,
+ NotifLiveDataStore notifDataStore,
CommandQueue commandQueue) {
super(lightsOutNotifView);
mWindowManager = windowManager;
- mEntryManager = entryManager;
+ mNotifDataStore = notifDataStore;
mCommandQueue = commandQueue;
}
@Override
protected void onViewDetached() {
- mEntryManager.removeNotificationEntryListener(mEntryListener);
+ mNotifDataStore.getHasActiveNotifs().removeObserver(mObserver);
mCommandQueue.removeCallback(mCallback);
}
@@ -87,14 +86,14 @@
mView.setAlpha(0f);
mDisplayId = mWindowManager.getDefaultDisplay().getDisplayId();
- mEntryManager.addNotificationEntryListener(mEntryListener);
+ mNotifDataStore.getHasActiveNotifs().addSyncObserver(mObserver);
mCommandQueue.addCallback(mCallback);
updateLightsOutView();
}
private boolean hasActiveNotifications() {
- return mEntryManager.hasActiveNotifications();
+ return mNotifDataStore.getHasActiveNotifs().getValue();
}
@VisibleForTesting
@@ -153,23 +152,4 @@
updateLightsOutView();
}
};
-
- private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
- // Cares about notifications post-filtering
- @Override
- public void onNotificationAdded(NotificationEntry entry) {
- updateLightsOutView();
- }
-
- @Override
- public void onPostEntryUpdated(NotificationEntry entry) {
- updateLightsOutView();
- }
-
- @Override
- public void onEntryRemoved(@Nullable NotificationEntry entry,
- NotificationVisibility visibility, boolean removedByUser, int reason) {
- updateLightsOutView();
- }
- };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index a64e579..3b7063e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -26,6 +26,7 @@
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
import static com.android.keyguard.KeyguardClockSwitch.SMALL;
+import static com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
@@ -34,6 +35,7 @@
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
+import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_FOLD_TO_AOD;
import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_CLOSED;
import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_OPEN;
import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_OPENING;
@@ -519,8 +521,6 @@
private WeakReference<CommunalSource> mCommunalSource;
- private final CommunalSource.Callback mCommunalSourceCallback;
-
private final CommandQueue mCommandQueue;
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final UserManager mUserManager;
@@ -906,9 +906,6 @@
mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count);
mKeyguardUnfoldTransition = unfoldComponent.map(c -> c.getKeyguardUnfoldTransition());
- mCommunalSourceCallback = () -> {
- mUiExecutor.execute(() -> setCommunalSource(null /*source*/));
- };
mCommunalSourceMonitorCallback = (source) -> {
mUiExecutor.execute(() -> setCommunalSource(source));
@@ -1428,11 +1425,12 @@
.getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
boolean splitShadeWithActiveMedia =
mShouldUseSplitNotificationShade && mMediaDataManager.hasActiveMedia();
+ boolean shouldAnimateClockChange = mScreenOffAnimationController.shouldAnimateClockChange();
if ((hasVisibleNotifications && !mShouldUseSplitNotificationShade)
|| (splitShadeWithActiveMedia && !mDozing)) {
- mKeyguardStatusViewController.displayClock(SMALL);
+ mKeyguardStatusViewController.displayClock(SMALL, shouldAnimateClockChange);
} else {
- mKeyguardStatusViewController.displayClock(LARGE);
+ mKeyguardStatusViewController.displayClock(LARGE, shouldAnimateClockChange);
}
updateKeyguardStatusViewAlignment(true /* animate */);
int userIconHeight = mKeyguardQsUserSwitchController != null
@@ -1468,7 +1466,7 @@
mKeyguardStatusViewController.isClockTopAligned());
mClockPositionAlgorithm.run(mClockPositionResult);
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
- boolean animateClock = animate || mAnimateNextPositionUpdate;
+ boolean animateClock = (animate || mAnimateNextPositionUpdate) && shouldAnimateClockChange;
mKeyguardStatusViewController.updatePosition(
mClockPositionResult.clockX, mClockPositionResult.clockY,
mClockPositionResult.clockScale, animateClock);
@@ -3419,12 +3417,6 @@
mStatusBarStateController.setState(KEYGUARD);
}
return true;
- case StatusBarState.SHADE:
-
- // This gets called in the middle of the touch handling, where the state is still
- // that we are tracking the panel. Collapse the panel after this is done.
- mView.post(mPostCollapseRunnable);
- return false;
default:
return true;
}
@@ -3832,6 +3824,45 @@
}
}
+ /**
+ * Updates the views to the initial state for the fold to AOD animation
+ */
+ public void prepareFoldToAodAnimation() {
+ // Force show AOD UI even if we are not locked
+ showAodUi();
+
+ // Move the content of the AOD all the way to the left
+ // so we can animate to the initial position
+ final int translationAmount = mView.getResources().getDimensionPixelSize(
+ R.dimen.below_clock_padding_start);
+ mView.setTranslationX(-translationAmount);
+ mView.setAlpha(0);
+ }
+
+ /**
+ * Starts fold to AOD animation
+ */
+ public void startFoldToAodAnimation(Runnable endAction) {
+ mView.animate()
+ .translationX(0)
+ .alpha(1f)
+ .setDuration(ANIMATION_DURATION_FOLD_TO_AOD)
+ .setInterpolator(EMPHASIZED_DECELERATE)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ endAction.run();
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endAction.run();
+ }
+ })
+ .start();
+
+ mKeyguardStatusViewController.animateFoldToAod();
+ }
+
/** */
public void setImportantForAccessibility(int mode) {
mView.setImportantForAccessibility(mode);
@@ -3946,6 +3977,10 @@
mView.setAlpha(alpha);
}
+ public void resetTranslation() {
+ mView.setTranslationX(0f);
+ }
+
public ViewPropertyAnimator fadeOut(long startDelayMs, long durationMs, Runnable endAction) {
return mView.animate().alpha(0).setStartDelay(startDelayMs).setDuration(
durationMs).setInterpolator(Interpolators.ALPHA_OUT).withLayer().withEndAction(
@@ -4719,7 +4754,6 @@
CommunalSource existingSource = mCommunalSource != null ? mCommunalSource.get() : null;
if (existingSource != null) {
- existingSource.removeCallback(mCommunalSourceCallback);
mCommunalViewController.show(null /*source*/);
}
@@ -4728,7 +4762,6 @@
CommunalSource currentSource = mCommunalSource != null ? mCommunalSource.get() : null;
// Set source and register callback
if (currentSource != null && mCommunalViewController != null) {
- currentSource.addCallback(mCommunalSourceCallback);
mCommunalViewController.show(source);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 9af79a9..53bfd77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -933,7 +933,6 @@
private void abortAnimations() {
cancelHeightAnimator();
- mView.removeCallbacks(mPostCollapseRunnable);
mView.removeCallbacks(mFlingCollapseRunnable);
}
@@ -1110,13 +1109,6 @@
return onMiddleClicked();
}
- protected final Runnable mPostCollapseRunnable = new Runnable() {
- @Override
- public void run() {
- collapse(false /* delayed */, 1.0f /* speedUpFactor */);
- }
- };
-
protected abstract boolean onMiddleClicked();
protected abstract boolean isDozing();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index f67d181..1e71ceb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -38,7 +38,6 @@
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.window.StatusBarWindowView;
import com.android.systemui.util.leak.RotationUtils;
import java.util.Objects;
@@ -76,6 +75,7 @@
@Override
public void onFinishInflate() {
+ super.onFinishInflate();
mBattery = findViewById(R.id.battery);
mClock = findViewById(R.id.clock);
mCutoutSpace = findViewById(R.id.cutout_space_view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index ec7e93b..b9386bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -15,12 +15,14 @@
*/
package com.android.systemui.statusbar.phone
+import android.content.res.Configuration
import android.graphics.Point
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import com.android.systemui.R
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
+import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.UNFOLD_STATUS_BAR
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
@@ -35,9 +37,16 @@
view: PhoneStatusBarView,
@Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider?,
private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
- touchEventHandler: PhoneStatusBarView.TouchEventHandler
+ touchEventHandler: PhoneStatusBarView.TouchEventHandler,
+ private val configurationController: ConfigurationController
) : ViewController<PhoneStatusBarView>(view) {
+ private val configurationListener = object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ mView.updateResources()
+ }
+ }
+
override fun onViewAttached() {
moveFromCenterAnimationController?.let { animationController ->
val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_left_side)
@@ -66,11 +75,13 @@
}
progressProvider?.setReadyToHandleTransition(true)
+ configurationController.addCallback(configurationListener)
}
override fun onViewDetached() {
progressProvider?.setReadyToHandleTransition(false)
moveFromCenterAnimationController?.onViewDetached()
+ configurationController.removeCallback(configurationListener)
}
init {
@@ -116,7 +127,8 @@
class Factory @Inject constructor(
private val unfoldComponent: Optional<SysUIUnfoldComponent>,
@Named(UNFOLD_STATUS_BAR)
- private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider>
+ private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider>,
+ private val configurationController: ConfigurationController
) {
fun create(
view: PhoneStatusBarView,
@@ -128,7 +140,8 @@
unfoldComponent.map {
it.getStatusBarMoveFromCenterAnimationController()
}.getOrNull(),
- touchEventHandler
+ touchEventHandler,
+ configurationController
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
index 497e7d7..e806ca0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
@@ -19,16 +19,23 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.unfold.FoldAodAnimationController
+import com.android.systemui.unfold.SysUIUnfoldComponent
+import java.util.Optional
import javax.inject.Inject
@SysUISingleton
class ScreenOffAnimationController @Inject constructor(
+ sysUiUnfoldComponent: Optional<SysUIUnfoldComponent>,
unlockedScreenOffAnimation: UnlockedScreenOffAnimationController,
private val wakefulnessLifecycle: WakefulnessLifecycle,
) : WakefulnessLifecycle.Observer {
- // TODO(b/202844967) add fold to aod animation here
- private val animations: List<ScreenOffAnimation> = listOf(unlockedScreenOffAnimation)
+ private val foldToAodAnimation: FoldAodAnimationController? = sysUiUnfoldComponent
+ .orElse(null)?.getFoldAodAnimationController()
+
+ private val animations: List<ScreenOffAnimation> =
+ listOfNotNull(foldToAodAnimation, unlockedScreenOffAnimation)
fun initialize(statusBar: StatusBar, lightRevealScrim: LightRevealScrim) {
animations.forEach { it.initialize(statusBar, lightRevealScrim) }
@@ -43,6 +50,19 @@
}
/**
+ * Called when opaqueness of the light reveal scrim has change
+ * When [isOpaque] is true then scrim is visible and covers the screen
+ */
+ fun onScrimOpaqueChanged(isOpaque: Boolean) =
+ animations.forEach { it.onScrimOpaqueChanged(isOpaque) }
+
+ /**
+ * Called when always on display setting changed
+ */
+ fun onAlwaysOnChanged(alwaysOn: Boolean) =
+ animations.forEach { it.onAlwaysOnChanged(alwaysOn) }
+
+ /**
* If returns true we are taking over the screen off animation from display manager to SysUI.
* We can play our custom animation instead of default fade out animation.
*/
@@ -103,6 +123,12 @@
animations.any { it.isAnimationPlaying() }
/**
+ * Return true to ignore requests to hide keyguard
+ */
+ fun isKeyguardHideDelayed(): Boolean =
+ animations.any { it.isKeyguardHideDelayed() }
+
+ /**
* Return true to make the StatusBar expanded so we can animate [LightRevealScrim]
*/
fun shouldShowLightRevealScrim(): Boolean =
@@ -145,6 +171,19 @@
*/
fun shouldAnimateAodIcons(): Boolean =
animations.all { it.shouldAnimateAodIcons() }
+
+ /**
+ * Return true to animate doze state change, if returns false dozing will be applied without
+ * animation (sends only 0.0f or 1.0f dozing progress)
+ */
+ fun shouldAnimateDozingChange(): Boolean =
+ animations.all { it.shouldAnimateDozingChange() }
+
+ /**
+ * Return true to animate large <-> small clock transition
+ */
+ fun shouldAnimateClockChange(): Boolean =
+ animations.all { it.shouldAnimateClockChange() }
}
interface ScreenOffAnimation {
@@ -158,11 +197,17 @@
fun shouldPlayAnimation(): Boolean = false
fun isAnimationPlaying(): Boolean = false
+ fun onScrimOpaqueChanged(isOpaque: Boolean) {}
+ fun onAlwaysOnChanged(alwaysOn: Boolean) {}
+
fun shouldAnimateInKeyguard(): Boolean = false
fun animateInKeyguard(keyguardView: View, after: Runnable) = after.run()
+ fun isKeyguardHideDelayed(): Boolean = false
fun shouldHideScrimOnWakeUp(): Boolean = false
fun overrideNotificationsDozeAmount(): Boolean = false
fun shouldShowAodIconsWhenShade(): Boolean = false
fun shouldAnimateAodIcons(): Boolean = true
+ fun shouldAnimateDozingChange(): Boolean = true
+ fun shouldAnimateClockChange(): Boolean = true
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index a54251a..b4fed2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -218,10 +218,6 @@
return getStatusBar().getNotificationShadeWindowView();
}
- protected PhoneStatusBarView getStatusBarView() {
- return (PhoneStatusBarView) getStatusBar().getStatusBarView();
- }
-
private NotificationPanelViewController getNotificationPanelViewController() {
return getStatusBar().getPanelController();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
index 1ad9fa6..8cf7288 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -18,6 +18,7 @@
import android.view.View
import androidx.constraintlayout.motion.widget.MotionLayout
+import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
@@ -48,8 +49,9 @@
}
private val combinedHeaders = featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)
- // TODO(b/194178072) Handle RSSI hiding when multi carrier
- private val iconManager: StatusBarIconController.IconManager
+ private val iconManager: StatusBarIconController.TintedIconManager
+ private val iconContainer: StatusIconContainer
+ private val carrierIconSlots: List<String>
private val qsCarrierGroupController: QSCarrierGroupController
private var visible = false
set(value) {
@@ -116,8 +118,19 @@
batteryMeterViewController.ignoreTunerUpdates()
batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
- val iconContainer: StatusIconContainer = statusBar.findViewById(R.id.statusIcons)
- iconManager = StatusBarIconController.IconManager(iconContainer, featureFlags)
+ iconContainer = statusBar.findViewById(R.id.statusIcons)
+ iconManager = StatusBarIconController.TintedIconManager(iconContainer, featureFlags)
+ iconManager.setTint(Utils.getColorAttrDefaultColor(statusBar.context,
+ android.R.attr.textColorPrimary))
+
+ carrierIconSlots = if (featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
+ listOf(
+ statusBar.context.getString(com.android.internal.R.string.status_bar_no_calling),
+ statusBar.context.getString(com.android.internal.R.string.status_bar_call_strength)
+ )
+ } else {
+ listOf(statusBar.context.getString(com.android.internal.R.string.status_bar_mobile))
+ }
qsCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(statusBar.findViewById(R.id.carrier_group))
.build()
@@ -182,9 +195,20 @@
private fun updateListeners() {
qsCarrierGroupController.setListening(visible)
if (visible) {
+ updateSingleCarrier(qsCarrierGroupController.isSingleCarrier)
+ qsCarrierGroupController.setOnSingleCarrierChangedListener { updateSingleCarrier(it) }
statusBarIconController.addIconGroup(iconManager)
} else {
+ qsCarrierGroupController.setOnSingleCarrierChangedListener(null)
statusBarIconController.removeIconGroup(iconManager)
}
}
+
+ private fun updateSingleCarrier(singleCarrier: Boolean) {
+ if (singleCarrier) {
+ iconContainer.removeIgnoredSlots(carrierIconSlots)
+ } else {
+ iconContainer.addIgnoredSlots(carrierIconSlots)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 0f0a2f0..3312996 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -341,7 +341,7 @@
mStatusBarWindowState = state;
mStatusBarWindowHidden = state == WINDOW_STATE_HIDDEN;
mStatusBarHideIconsForBouncerManager.setStatusBarWindowHidden(mStatusBarWindowHidden);
- if (getStatusBarView() != null) {
+ if (mStatusBarView != null) {
// Should #updateHideIconsForBouncer always be called, regardless of whether we have a
// status bar view? If so, we can make #updateHideIconsForBouncer private.
mStatusBarHideIconsForBouncerManager.updateHideIconsForBouncer(/* animate= */ false);
@@ -1124,23 +1124,18 @@
// Set up CollapsedStatusBarFragment and PhoneStatusBarView
StatusBarInitializer initializer = mStatusBarComponent.getStatusBarInitializer();
initializer.setStatusBarViewUpdatedListener(
- new StatusBarInitializer.OnStatusBarViewUpdatedListener() {
- @Override
- public void onStatusBarViewUpdated(
- @NonNull PhoneStatusBarView statusBarView,
- @NonNull PhoneStatusBarViewController statusBarViewController) {
- mStatusBarView = statusBarView;
- mPhoneStatusBarViewController = statusBarViewController;
- mNotificationShadeWindowViewController.setStatusBarView(mStatusBarView);
- // Ensure we re-propagate panel expansion values to the panel controller and
- // any listeners it may have, such as PanelBar. This will also ensure we
- // re-display the notification panel if necessary (for example, if
- // a heads-up notification was being displayed and should continue being
- // displayed).
- mNotificationPanelViewController.updatePanelExpansionAndVisibility();
- setBouncerShowingForStatusBarComponents(mBouncerShowing);
- checkBarModes();
- }
+ (statusBarView, statusBarViewController) -> {
+ mStatusBarView = statusBarView;
+ mPhoneStatusBarViewController = statusBarViewController;
+ mNotificationShadeWindowViewController.setStatusBarView(mStatusBarView);
+ // Ensure we re-propagate panel expansion values to the panel controller and
+ // any listeners it may have, such as PanelBar. This will also ensure we
+ // re-display the notification panel if necessary (for example, if
+ // a heads-up notification was being displayed and should continue being
+ // displayed).
+ mNotificationPanelViewController.updatePanelExpansionAndVisibility();
+ setBouncerShowingForStatusBarComponents(mBouncerShowing);
+ checkBarModes();
});
initializer.initializeStatusBar(mStatusBarComponent);
@@ -1199,6 +1194,8 @@
Runnable updateOpaqueness = () -> {
mNotificationShadeWindowController.setLightRevealScrimOpaque(
mLightRevealScrim.isScrimOpaque());
+ mScreenOffAnimationController
+ .onScrimOpaqueChanged(mLightRevealScrim.isScrimOpaque());
};
if (opaque) {
// Delay making the view opaque for a frame, because it needs some time to render
@@ -1578,10 +1575,6 @@
Trace.endSection();
}
- protected PhoneStatusBarView getStatusBarView() {
- return mStatusBarView;
- }
-
public NotificationShadeWindowView getNotificationShadeWindowView() {
return mNotificationShadeWindowView;
}
@@ -2715,9 +2708,6 @@
mStatusBarWindowController.refreshStatusBarHeight();
}
- if (mStatusBarView != null) {
- mStatusBarView.updateResources();
- }
if (mNotificationPanelViewController != null) {
mNotificationPanelViewController.updateResources();
}
@@ -2918,7 +2908,17 @@
showKeyguardImpl();
}
} else {
- return hideKeyguardImpl(force);
+ // During folding a foldable device this might be called as a result of
+ // 'onScreenTurnedOff' call for the inner display.
+ // In this case:
+ // * When phone is locked on folding: it doesn't make sense to hide keyguard as it
+ // will be immediately locked again
+ // * When phone is unlocked: we still don't want to execute hiding of the keyguard
+ // as the animation could prepare 'fake AOD' interface (without actually
+ // transitioning to keyguard state) and this might reset the view states
+ if (!mScreenOffAnimationController.isKeyguardHideDelayed()) {
+ return hideKeyguardImpl(force);
+ }
}
return false;
}
@@ -3081,6 +3081,7 @@
mNotificationPanelViewController.onAffordanceLaunchEnded();
mNotificationPanelViewController.cancelAnimation();
mNotificationPanelViewController.setAlpha(1f);
+ mNotificationPanelViewController.resetTranslation();
mNotificationPanelViewController.resetViewGroupFade();
updateDozingState();
updateScrimController();
@@ -4192,7 +4193,7 @@
if (userSetup != mUserSetup) {
mUserSetup = userSetup;
- if (!mUserSetup && mStatusBarView != null) {
+ if (!mUserSetup) {
animateCollapseQuickSettings();
}
if (mNotificationPanelViewController != null) {
@@ -4307,7 +4308,7 @@
updateTheme();
mNavigationBarController.touchAutoDim(mDisplayId);
Trace.beginSection("StatusBar#updateKeyguardState");
- if (mState == StatusBarState.KEYGUARD && mStatusBarView != null) {
+ if (mState == StatusBarState.KEYGUARD) {
mNotificationPanelViewController.cancelPendingPanelCollapse();
}
updateDozingState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
index abb7449..b391de3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
@@ -529,9 +529,7 @@
if (StatusBar.DEBUG_WINDOW_STATE) {
Log.d(StatusBar.TAG, "Status bar " + windowStateToString(state));
}
- if (mStatusBar.getStatusBarView() != null
- && !showing
- && mStatusBarStateController.getState() == StatusBarState.SHADE) {
+ if (!showing && mStatusBarStateController.getState() == StatusBarState.SHADE) {
mNotificationPanelViewController.collapsePanel(
false /* animate */, false /* delayed */, 1.0f /* speedUpFactor */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index b84e6e6..a4aeae9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -111,8 +111,6 @@
private final NavigationModeController mNavigationModeController;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final KeyguardBouncer.Factory mKeyguardBouncerFactory;
- private final WakefulnessLifecycle mWakefulnessLifecycle;
- private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private final KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
private KeyguardMessageAreaController mKeyguardMessageAreaController;
private final Lazy<ShadeController> mShadeController;
@@ -244,8 +242,6 @@
KeyguardStateController keyguardStateController,
NotificationMediaManager notificationMediaManager,
KeyguardBouncer.Factory keyguardBouncerFactory,
- WakefulnessLifecycle wakefulnessLifecycle,
- UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
KeyguardMessageAreaController.Factory keyguardMessageAreaFactory,
Lazy<ShadeController> shadeController) {
mContext = context;
@@ -260,8 +256,6 @@
mStatusBarStateController = sysuiStatusBarStateController;
mDockManager = dockManager;
mKeyguardBouncerFactory = keyguardBouncerFactory;
- mWakefulnessLifecycle = wakefulnessLifecycle;
- mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
mKeyguardMessageAreaFactory = keyguardMessageAreaFactory;
mShadeController = shadeController;
}
@@ -1155,7 +1149,7 @@
@Override
public ViewRootImpl getViewRootImpl() {
- return mStatusBar.getStatusBarView().getViewRootImpl();
+ return mNotificationShadeWindowController.getNotificationShadeView().getViewRootImpl();
}
public void launchPendingWakeupAction() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 863ce57..ff86d74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -660,14 +660,6 @@
// --------------------- NotificationEntryManager/NotifPipeline methods ------------------------
- private int getVisibleNotificationsCount() {
- if (mNotifPipelineFlags.isNewPipelineEnabled()) {
- return mNotifPipeline.getShadeListCount();
- } else {
- return mEntryManager.getActiveNotificationsCount();
- }
- }
-
/**
* Public builder for {@link StatusBarNotificationActivityStarter}.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 8df7b45..e3b4caa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -45,6 +45,10 @@
/**
* Base class for dialogs that should appear over panels and keyguard.
+ *
+ * Optionally provide a {@link SystemUIDialogManager} to its constructor to send signals to
+ * listeners on whether this dialog is showing.
+ *
* The SystemUIDialog registers a listener for the screen off / close system dialogs broadcast,
* and dismisses itself when it receives the broadcast.
*/
@@ -54,8 +58,9 @@
"persist.systemui.flag_tablet_dialog_width";
private final Context mContext;
- private final DismissReceiver mDismissReceiver;
+ @Nullable private final DismissReceiver mDismissReceiver;
private final Handler mHandler = new Handler();
+ @Nullable private final SystemUIDialogManager mDialogManager;
private int mLastWidth = Integer.MIN_VALUE;
private int mLastHeight = Integer.MIN_VALUE;
@@ -66,11 +71,27 @@
this(context, R.style.Theme_SystemUI_Dialog);
}
+ public SystemUIDialog(Context context, SystemUIDialogManager dialogManager) {
+ this(context, R.style.Theme_SystemUI_Dialog, true, dialogManager);
+ }
+
public SystemUIDialog(Context context, int theme) {
this(context, theme, true /* dismissOnDeviceLock */);
}
+ public SystemUIDialog(Context context, int theme, SystemUIDialogManager dialogManager) {
+ this(context, theme, true /* dismissOnDeviceLock */, dialogManager);
+ }
+
public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock) {
+ this(context, theme, dismissOnDeviceLock, null);
+ }
+
+ /**
+ * @param udfpsDialogManager If set, UDFPS will hide if this dialog is showing.
+ */
+ public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock,
+ SystemUIDialogManager dialogManager) {
super(context, theme);
mContext = context;
@@ -80,6 +101,7 @@
getWindow().setAttributes(attrs);
mDismissReceiver = dismissOnDeviceLock ? new DismissReceiver(this) : null;
+ mDialogManager = dialogManager;
}
@Override
@@ -145,6 +167,10 @@
mDismissReceiver.register();
}
+ if (mDialogManager != null) {
+ mDialogManager.setShowing(this, true);
+ }
+
// Listen for configuration changes to resize this dialog window. This is mostly necessary
// for foldables that often go from large <=> small screen when folding/unfolding.
ViewRootImpl.addConfigCallback(this);
@@ -158,6 +184,10 @@
mDismissReceiver.unregister();
}
+ if (mDialogManager != null) {
+ mDialogManager.setShowing(this, false);
+ }
+
ViewRootImpl.removeConfigCallback(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java
new file mode 100644
index 0000000..204f710
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+/**
+ * Register dialogs to this manager if extraneous affordances (like the UDFPS sensor area)
+ * should be hidden from the screen when the dialog shows.
+ *
+ * Currently, only used if UDFPS is supported on the device; however, can be extended in the future
+ * for other use cases.
+ */
+@SysUISingleton
+public class SystemUIDialogManager implements Dumpable {
+ private final StatusBarKeyguardViewManager mKeyguardViewManager;
+
+ private final Set<SystemUIDialog> mDialogsShowing = new HashSet<>();
+ private final Set<Listener> mListeners = new HashSet<>();
+
+ @Inject
+ public SystemUIDialogManager(
+ DumpManager dumpManager,
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+ dumpManager.registerDumpable(this);
+ mKeyguardViewManager = statusBarKeyguardViewManager;
+ }
+
+ /**
+ * Whether listeners should hide affordances like the UDFPS sensor icon.
+ */
+ public boolean shouldHideAffordance() {
+ return !mDialogsShowing.isEmpty();
+ }
+
+ /**
+ * Register a listener to receive callbacks.
+ */
+ public void registerListener(@NonNull Listener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Unregister a listener from receiving callbacks.
+ */
+ public void unregisterListener(@NonNull Listener listener) {
+ mListeners.remove(listener);
+ }
+
+ void setShowing(SystemUIDialog dialog, boolean showing) {
+ final boolean wasHidingAffordances = shouldHideAffordance();
+ if (showing) {
+ mDialogsShowing.add(dialog);
+ } else {
+ mDialogsShowing.remove(dialog);
+ }
+
+ if (wasHidingAffordances != shouldHideAffordance()) {
+ updateDialogListeners();
+ }
+ }
+
+ private void updateDialogListeners() {
+ if (shouldHideAffordance()) {
+ mKeyguardViewManager.resetAlternateAuth(true);
+ }
+
+ for (Listener listener : mListeners) {
+ listener.shouldHideAffordances(shouldHideAffordance());
+ }
+ }
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("listeners:");
+ for (Listener listener : mListeners) {
+ pw.println("\t" + listener);
+ }
+ pw.println("dialogs tracked:");
+ for (SystemUIDialog dialog : mDialogsShowing) {
+ pw.println("\t" + dialog);
+ }
+ }
+
+ /** SystemUIDialogManagerListener */
+ public interface Listener {
+ /**
+ * Callback where shouldHide=true if listeners should hide their views that may overlap
+ * a showing dialog.
+ */
+ void shouldHideAffordances(boolean shouldHide);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index dc8dc99..79ee746 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -836,6 +836,11 @@
mRootView = notificationShadeWindowView;
}
+ @VisibleForTesting
+ public KeyguardStateController getKeyguardStateController() {
+ return mKeyguardStateController;
+ }
+
public static abstract class BaseUserAdapter extends BaseAdapter {
final UserSwitcherController mController;
@@ -843,7 +848,7 @@
protected BaseUserAdapter(UserSwitcherController controller) {
mController = controller;
- mKeyguardStateController = controller.mKeyguardStateController;
+ mKeyguardStateController = controller.getKeyguardStateController();
controller.addAdapter(new WeakReference<>(this));
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
new file mode 100644
index 0000000..fb9df01
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.unfold
+
+import android.os.PowerManager
+import android.provider.Settings
+import com.android.systemui.keyguard.KeyguardViewMediator
+import com.android.systemui.keyguard.ScreenLifecycle
+import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.statusbar.phone.ScreenOffAnimation
+import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus
+import com.android.systemui.util.settings.GlobalSettings
+import dagger.Lazy
+import javax.inject.Inject
+
+/**
+ * Controls folding to AOD animation: when AOD is enabled and foldable device is folded
+ * we play a special AOD animation on the outer screen
+ */
+@SysUIUnfoldScope
+class FoldAodAnimationController @Inject constructor(
+ private val screenLifecycle: ScreenLifecycle,
+ private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>,
+ private val wakefulnessLifecycle: WakefulnessLifecycle,
+ private val globalSettings: GlobalSettings
+) : ScreenLifecycle.Observer,
+ CallbackController<FoldAodAnimationStatus>,
+ ScreenOffAnimation,
+ WakefulnessLifecycle.Observer {
+
+ private var alwaysOnEnabled: Boolean = false
+ private var isScrimOpaque: Boolean = false
+ private lateinit var statusBar: StatusBar
+ private var pendingScrimReadyCallback: Runnable? = null
+
+ private var shouldPlayAnimation = false
+ private val statusListeners = arrayListOf<FoldAodAnimationStatus>()
+
+ private var isAnimationPlaying = false
+
+ override fun initialize(statusBar: StatusBar, lightRevealScrim: LightRevealScrim) {
+ this.statusBar = statusBar
+
+ screenLifecycle.addObserver(this)
+ wakefulnessLifecycle.addObserver(this)
+ }
+
+ /**
+ * Returns true if we should run fold to AOD animation
+ */
+ override fun shouldPlayAnimation(): Boolean =
+ shouldPlayAnimation
+
+ override fun startAnimation(): Boolean =
+ if (alwaysOnEnabled &&
+ wakefulnessLifecycle.lastSleepReason == PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD &&
+ globalSettings.getString(Settings.Global.ANIMATOR_DURATION_SCALE) != "0"
+ ) {
+ shouldPlayAnimation = true
+
+ isAnimationPlaying = true
+ statusBar.notificationPanelViewController.prepareFoldToAodAnimation()
+
+ statusListeners.forEach(FoldAodAnimationStatus::onFoldToAodAnimationChanged)
+
+ true
+ } else {
+ shouldPlayAnimation = false
+ false
+ }
+
+ override fun onStartedWakingUp() {
+ shouldPlayAnimation = false
+ isAnimationPlaying = false
+ }
+
+ /**
+ * Called when screen starts turning on, the contents of the screen might not be visible yet.
+ * This method reports back that the animation is ready in [onReady] callback.
+ *
+ * @param onReady callback when the animation is ready
+ * @see [com.android.systemui.keyguard.KeyguardViewMediator]
+ */
+ fun onScreenTurningOn(onReady: Runnable) {
+ if (shouldPlayAnimation) {
+ if (isScrimOpaque) {
+ onReady.run()
+ } else {
+ pendingScrimReadyCallback = onReady
+ }
+ } else {
+ // No animation, call ready callback immediately
+ onReady.run()
+ }
+ }
+
+ /**
+ * Called when keyguard scrim opaque changed
+ */
+ override fun onScrimOpaqueChanged(isOpaque: Boolean) {
+ isScrimOpaque = isOpaque
+
+ if (isOpaque) {
+ pendingScrimReadyCallback?.run()
+ pendingScrimReadyCallback = null
+ }
+ }
+
+ override fun onScreenTurnedOn() {
+ if (shouldPlayAnimation) {
+ statusBar.notificationPanelViewController.startFoldToAodAnimation {
+ // End action
+ isAnimationPlaying = false
+ keyguardViewMediatorLazy.get().maybeHandlePendingLock()
+ }
+ shouldPlayAnimation = false
+ }
+ }
+
+ override fun isAnimationPlaying(): Boolean =
+ isAnimationPlaying
+
+ override fun isKeyguardHideDelayed(): Boolean =
+ isAnimationPlaying()
+
+ override fun shouldShowAodIconsWhenShade(): Boolean =
+ shouldPlayAnimation()
+
+ override fun shouldAnimateAodIcons(): Boolean =
+ !shouldPlayAnimation()
+
+ override fun shouldAnimateDozingChange(): Boolean =
+ !shouldPlayAnimation()
+
+ override fun shouldAnimateClockChange(): Boolean =
+ !isAnimationPlaying()
+
+ /**
+ * Called when AOD status is changed
+ */
+ override fun onAlwaysOnChanged(alwaysOn: Boolean) {
+ alwaysOnEnabled = alwaysOn
+ }
+
+ override fun addCallback(listener: FoldAodAnimationStatus) {
+ statusListeners += listener
+ }
+
+ override fun removeCallback(listener: FoldAodAnimationStatus) {
+ statusListeners.remove(listener)
+ }
+
+ interface FoldAodAnimationStatus {
+ fun onFoldToAodAnimationChanged()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
index b53ab21..ccde316 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -78,6 +78,8 @@
fun getStatusBarMoveFromCenterAnimationController(): StatusBarMoveFromCenterAnimationController
+ fun getFoldAodAnimationController(): FoldAodAnimationController
+
fun getUnfoldTransitionWallpaperController(): UnfoldTransitionWallpaperController
fun getUnfoldLightRevealOverlayAnimation(): UnfoldLightRevealOverlayAnimation
diff --git a/packages/SystemUI/src/com/android/systemui/util/ListenerSet.kt b/packages/SystemUI/src/com/android/systemui/util/ListenerSet.kt
index 0f4193e9..4f20067 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ListenerSet.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/ListenerSet.kt
@@ -39,9 +39,17 @@
fun remove(element: E): Boolean = listeners.remove(element)
/**
+ * Determine if the listener set is empty
+ */
+ fun isEmpty(): Boolean = listeners.isEmpty()
+
+ /**
* Returns an iterator over the listeners currently in the set. Note that to ensure
* [ConcurrentModificationException] is never thrown, this iterator will not reflect changes
* made to the set after the iterator is constructed.
*/
override fun iterator(): Iterator<E> = listeners.iterator()
}
+
+/** Extension to match Collection which is implemented to only be (easily) accessible in kotlin */
+fun <T> ListenerSet<T>.isNotEmpty(): Boolean = !isEmpty()
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
index a7e9cdb..8b6e982 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
@@ -23,7 +23,6 @@
import org.jetbrains.annotations.NotNull;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -38,7 +37,7 @@
public class Monitor implements CallbackController<Monitor.Callback> {
private final String mTag = getClass().getSimpleName();
- private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
+ private final ArrayList<Callback> mCallbacks = new ArrayList<>();
// Set of all conditions that need to be monitored.
private final Set<Condition> mConditions;
@@ -66,9 +65,9 @@
mAllConditionsMet = newAllConditionsMet;
// Updates all callbacks.
- final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+ final Iterator<Callback> iterator = mCallbacks.iterator();
while (iterator.hasNext()) {
- final Callback callback = iterator.next().get();
+ final Callback callback = iterator.next();
if (callback == null) {
iterator.remove();
} else {
@@ -78,7 +77,7 @@
};
@Inject
- public Monitor(Set<Condition> conditions) {
+ public Monitor(Set<Condition> conditions, Set<Callback> callbacks) {
mConditions = conditions;
// If there is no condition, give green pass.
@@ -89,12 +88,20 @@
// Initializes the conditions map and registers a callback for each condition.
mConditions.forEach((condition -> mConditionsMap.put(condition, false)));
+
+ if (callbacks == null) {
+ return;
+ }
+
+ for (Callback callback : callbacks) {
+ addCallback(callback);
+ }
}
@Override
public void addCallback(@NotNull Callback callback) {
if (shouldLog()) Log.d(mTag, "adding callback");
- mCallbacks.add(new WeakReference<>(callback));
+ mCallbacks.add(callback);
// Updates the callback immediately.
callback.onConditionsChanged(mAllConditionsMet);
@@ -109,9 +116,9 @@
@Override
public void removeCallback(@NotNull Callback callback) {
if (shouldLog()) Log.d(mTag, "removing callback");
- final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+ final Iterator<Callback> iterator = mCallbacks.iterator();
while (iterator.hasNext()) {
- final Callback cb = iterator.next().get();
+ final Callback cb = iterator.next();
if (cb == null || cb == callback) {
iterator.remove();
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java b/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java
new file mode 100644
index 0000000..fc67973
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.util.condition.dagger;
+
+import com.android.systemui.util.condition.Condition;
+import com.android.systemui.util.condition.Monitor;
+
+import java.util.Set;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Component for {@link Monitor}.
+ */
+@Subcomponent
+public interface MonitorComponent {
+ /**
+ * Factory for {@link MonitorComponent}.
+ */
+ @Subcomponent.Factory
+ interface Factory {
+ MonitorComponent create(@BindsInstance Set<Condition> conditions,
+ @BindsInstance Set<Monitor.Callback> callbacks);
+ }
+
+ /**
+ * Provides {@link Monitor}.
+ * @return
+ */
+ Monitor getMonitor();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
index 981bf01..7892d6e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
@@ -18,6 +18,7 @@
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.RingerModeTrackerImpl;
+import com.android.systemui.util.condition.dagger.MonitorComponent;
import com.android.systemui.util.wrapper.UtilWrapperModule;
import dagger.Binds;
@@ -26,6 +27,9 @@
/** Dagger Module for code in the util package. */
@Module(includes = {
UtilWrapperModule.class
+ },
+ subcomponents = {
+ MonitorComponent.class,
})
public interface UtilModule {
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
index bad0c37..c57dbe3 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
@@ -16,9 +16,7 @@
package com.android.systemui.util.service;
-import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
-import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -26,6 +24,8 @@
import android.os.IBinder;
import android.util.Log;
+import com.android.systemui.dagger.qualifiers.Main;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
@@ -35,6 +35,8 @@
import java.util.concurrent.Executor;
import java.util.function.Consumer;
+import javax.inject.Inject;
+
/**
* {@link ObservableServiceConnection} is a concrete implementation of {@link ServiceConnection}
* that enables monitoring the status of a binder connection. It also aides in automatically
@@ -119,17 +121,17 @@
* Default constructor for {@link ObservableServiceConnection}.
* @param context The context from which the service will be bound with.
* @param serviceIntent The intent to bind service with.
- * @param flags The flags to use during the binding
* @param executor The executor for connection callbacks to be delivered on
* @param transformer A {@link ServiceTransformer} for transforming the resulting service
* into a desired type.
*/
+ @Inject
public ObservableServiceConnection(Context context, Intent serviceIntent,
- @Context.BindServiceFlags int flags, @NonNull @CallbackExecutor Executor executor,
+ @Main Executor executor,
ServiceTransformer<T> transformer) {
mContext = context;
mServiceIntent = serviceIntent;
- mFlags = flags;
+ mFlags = Context.BIND_AUTO_CREATE;
mExecutor = executor;
mTransformer = transformer;
mCallbacks = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 6dd6d6d..4600bc7 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -56,6 +56,7 @@
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
import com.android.wm.shell.ShellCommandHandler;
+import com.android.wm.shell.compatui.CompatUI;
import com.android.wm.shell.draganddrop.DragAndDrop;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
@@ -66,7 +67,6 @@
import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.protolog.ShellProtoLogImpl;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
import com.android.wm.shell.splitscreen.SplitScreen;
import java.io.FileDescriptor;
@@ -114,7 +114,7 @@
private final Optional<OneHanded> mOneHandedOptional;
private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
private final Optional<ShellCommandHandler> mShellCommandHandler;
- private final Optional<SizeCompatUI> mSizeCompatUIOptional;
+ private final Optional<CompatUI> mCompatUIOptional;
private final Optional<DragAndDrop> mDragAndDropOptional;
private final CommandQueue mCommandQueue;
@@ -132,7 +132,7 @@
private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
- private KeyguardUpdateMonitorCallback mSizeCompatUIKeyguardCallback;
+ private KeyguardUpdateMonitorCallback mCompatUIKeyguardCallback;
private WakefulnessLifecycle.Observer mWakefulnessObserver;
@Inject
@@ -143,7 +143,7 @@
Optional<OneHanded> oneHandedOptional,
Optional<HideDisplayCutout> hideDisplayCutoutOptional,
Optional<ShellCommandHandler> shellCommandHandler,
- Optional<SizeCompatUI> sizeCompatUIOptional,
+ Optional<CompatUI> sizeCompatUIOptional,
Optional<DragAndDrop> dragAndDropOptional,
CommandQueue commandQueue,
ConfigurationController configurationController,
@@ -169,7 +169,7 @@
mWakefulnessLifecycle = wakefulnessLifecycle;
mProtoTracer = protoTracer;
mShellCommandHandler = shellCommandHandler;
- mSizeCompatUIOptional = sizeCompatUIOptional;
+ mCompatUIOptional = sizeCompatUIOptional;
mDragAndDropOptional = dragAndDropOptional;
mSysUiMainExecutor = sysUiMainExecutor;
}
@@ -185,7 +185,7 @@
mSplitScreenOptional.ifPresent(this::initSplitScreen);
mOneHandedOptional.ifPresent(this::initOneHanded);
mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout);
- mSizeCompatUIOptional.ifPresent(this::initSizeCompatUi);
+ mCompatUIOptional.ifPresent(this::initCompatUi);
mDragAndDropOptional.ifPresent(this::initDragAndDrop);
}
@@ -391,14 +391,14 @@
}
@VisibleForTesting
- void initSizeCompatUi(SizeCompatUI sizeCompatUI) {
- mSizeCompatUIKeyguardCallback = new KeyguardUpdateMonitorCallback() {
+ void initCompatUi(CompatUI sizeCompatUI) {
+ mCompatUIKeyguardCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onKeyguardOccludedChanged(boolean occluded) {
sizeCompatUI.onKeyguardOccludedChanged(occluded);
}
};
- mKeyguardUpdateMonitor.registerCallback(mSizeCompatUIKeyguardCallback);
+ mKeyguardUpdateMonitor.registerCallback(mCompatUIKeyguardCallback);
}
void initDragAndDrop(DragAndDrop dragAndDrop) {
diff --git a/packages/SystemUI/tests/AndroidTest.xml b/packages/SystemUI/tests/AndroidTest.xml
index fc353a1..5ffd300 100644
--- a/packages/SystemUI/tests/AndroidTest.xml
+++ b/packages/SystemUI/tests/AndroidTest.xml
@@ -18,12 +18,17 @@
<option name="test-file-name" value="SystemUITests.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ <option name="force-root" value="true" />
+ </target_preparer>
+
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="framework-base-presubmit" />
<option name="test-tag" value="SystemUITests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.systemui.tests" />
<option name="runner" value="android.testing.TestableInstrumentation" />
+ <option name="test-filter-dir" value="/data/data/com.android.systemui.tests" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index e967033..74e0f40 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -264,7 +264,7 @@
reset(mView);
observer.onChange(true);
mExecutor.runAllReady();
- verify(mView).switchToClock(KeyguardClockSwitch.SMALL);
+ verify(mView).switchToClock(KeyguardClockSwitch.SMALL, /* animate */ true);
}
private void verifyAttachment(VerificationMode times) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index e4336fe..8717a0e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -253,8 +253,8 @@
}
@Test
- public void switchingToBigClock_makesSmallClockDisappear() {
- mKeyguardClockSwitch.switchToClock(LARGE);
+ public void switchingToBigClockWithAnimation_makesSmallClockDisappear() {
+ mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true);
mKeyguardClockSwitch.mClockInAnim.end();
mKeyguardClockSwitch.mClockOutAnim.end();
@@ -265,8 +265,17 @@
}
@Test
- public void switchingToSmallClock_makesBigClockDisappear() {
- mKeyguardClockSwitch.switchToClock(SMALL);
+ public void switchingToBigClockNoAnimation_makesSmallClockDisappear() {
+ mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ false);
+
+ assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
+ assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
+ assertThat(mClockFrame.getAlpha()).isEqualTo(0);
+ }
+
+ @Test
+ public void switchingToSmallClockWithAnimation_makesBigClockDisappear() {
+ mKeyguardClockSwitch.switchToClock(SMALL, /* animate */ true);
mKeyguardClockSwitch.mClockInAnim.end();
mKeyguardClockSwitch.mClockOutAnim.end();
@@ -279,8 +288,19 @@
}
@Test
+ public void switchingToSmallClockNoAnimation_makesBigClockDisappear() {
+ mKeyguardClockSwitch.switchToClock(SMALL, false);
+
+ assertThat(mClockFrame.getAlpha()).isEqualTo(1);
+ assertThat(mClockFrame.getVisibility()).isEqualTo(VISIBLE);
+ // only big clock is removed at switch
+ assertThat(mLargeClockFrame.getParent()).isNull();
+ assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);
+ }
+
+ @Test
public void switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore() {
- assertThat(mKeyguardClockSwitch.switchToClock(LARGE)).isTrue();
- assertThat(mKeyguardClockSwitch.switchToClock(LARGE)).isFalse();
+ assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isTrue();
+ assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isFalse();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 030464a..c873804 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -48,8 +48,10 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.settings.GlobalSettings;
import org.junit.Before;
@@ -110,7 +112,11 @@
@Mock
private FalsingCollector mFalsingCollector;
@Mock
+ private FalsingManager mFalsingManager;
+ @Mock
private GlobalSettings mGlobalSettings;
+ @Mock
+ private UserSwitcherController mUserSwitcherController;
private Configuration mConfiguration;
private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
@@ -144,8 +150,8 @@
mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, mKeyguardSecurityViewFlipperController,
- mConfigurationController, mFalsingCollector, mGlobalSettings)
- .create(mSecurityCallback);
+ mConfigurationController, mFalsingCollector, mFalsingManager,
+ mUserSwitcherController, mGlobalSettings).create(mSecurityCallback);
}
@Test
@@ -182,13 +188,15 @@
public void onResourcesUpdate_callsThroughOnRotationChange() {
// Rotation is the same, shouldn't cause an update
mKeyguardSecurityContainerController.updateResources();
- verify(mView, never()).initMode(MODE_DEFAULT, mGlobalSettings);
+ verify(mView, never()).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
+ mUserSwitcherController);
// Update rotation. Should trigger update
mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
mKeyguardSecurityContainerController.updateResources();
- verify(mView).initMode(MODE_DEFAULT, mGlobalSettings);
+ verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
+ mUserSwitcherController);
}
private void touchDownLeftSide() {
@@ -245,7 +253,8 @@
.thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
- verify(mView).initMode(MODE_DEFAULT, mGlobalSettings);
+ verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
+ mUserSwitcherController);
}
@Test
@@ -256,7 +265,8 @@
.thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
- verify(mView).initMode(MODE_ONE_HANDED, mGlobalSettings);
+ verify(mView).initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingManager,
+ mUserSwitcherController);
}
@Test
@@ -267,6 +277,7 @@
.thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
- verify(mView).initMode(MODE_DEFAULT, mGlobalSettings);
+ verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
+ mUserSwitcherController);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index c751081..24b01e0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -16,21 +16,27 @@
package com.android.keyguard;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
import static com.android.keyguard.KeyguardSecurityContainer.MODE_DEFAULT;
import static com.android.keyguard.KeyguardSecurityContainer.MODE_ONE_HANDED;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.pm.UserInfo;
+import android.content.res.Configuration;
import android.graphics.Insets;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
@@ -39,17 +45,26 @@
import androidx.test.filters.SmallTest;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.UserSwitcherController.UserRecord;
import com.android.systemui.util.settings.GlobalSettings;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.ArrayList;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper()
@@ -67,28 +82,43 @@
private KeyguardSecurityViewFlipper mSecurityViewFlipper;
@Mock
private GlobalSettings mGlobalSettings;
+ @Mock
+ private FalsingManager mFalsingManager;
+ @Mock
+ private UserSwitcherController mUserSwitcherController;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Captor
+ private ArgumentCaptor<FrameLayout.LayoutParams> mLayoutCaptor;
private KeyguardSecurityContainer mKeyguardSecurityContainer;
+ private FrameLayout.LayoutParams mSecurityViewFlipperLayoutParams;
@Before
public void setup() {
// Needed here, otherwise when mKeyguardSecurityContainer is created below, it'll cache
// the real references (rather than the TestableResources that this call creates).
mContext.ensureTestableResources();
- FrameLayout.LayoutParams securityViewFlipperLayoutParams = new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ mSecurityViewFlipperLayoutParams = new FrameLayout.LayoutParams(
+ MATCH_PARENT, MATCH_PARENT);
when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
- when(mSecurityViewFlipper.getLayoutParams()).thenReturn(securityViewFlipperLayoutParams);
+ when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext());
mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper;
mKeyguardSecurityContainer.addView(mSecurityViewFlipper, new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+
+ when(mUserSwitcherController.getCurrentUserName()).thenReturn("Test User");
+ when(mUserSwitcherController.getKeyguardStateController())
+ .thenReturn(mKeyguardStateController);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
}
@Test
public void onMeasure_usesHalfWidthWithOneHandedModeEnabled() {
- mKeyguardSecurityContainer.initMode(MODE_ONE_HANDED, mGlobalSettings);
+ mKeyguardSecurityContainer.initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingManager,
+ mUserSwitcherController);
int halfWidthMeasureSpec =
View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH / 2, View.MeasureSpec.EXACTLY);
@@ -99,7 +129,8 @@
@Test
public void onMeasure_usesFullWidthWithOneHandedModeDisabled() {
- mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings);
+ mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
+ mUserSwitcherController);
mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
@@ -110,7 +141,8 @@
int imeInsetAmount = 100;
int systemBarInsetAmount = 10;
- mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings);
+ mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
+ mUserSwitcherController);
Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
@@ -134,7 +166,8 @@
int imeInsetAmount = 0;
int systemBarInsetAmount = 10;
- mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings);
+ mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
+ mUserSwitcherController);
Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
@@ -154,7 +187,8 @@
private void setupForUpdateKeyguardPosition(boolean oneHandedMode) {
int mode = oneHandedMode ? MODE_ONE_HANDED : MODE_DEFAULT;
- mKeyguardSecurityContainer.initMode(mode, mGlobalSettings);
+ mKeyguardSecurityContainer.initMode(mode, mGlobalSettings, mFalsingManager,
+ mUserSwitcherController);
mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
mKeyguardSecurityContainer.layout(0, 0, SCREEN_WIDTH, SCREEN_WIDTH);
@@ -189,4 +223,92 @@
mKeyguardSecurityContainer.updatePositionByTouchX(1f);
verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
}
+
+ @Test
+ public void testUserSwitcherModeViewGravityLandscape() {
+ // GIVEN one user has been setup and in landscape
+ when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(1));
+ Configuration config = new Configuration();
+ config.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ when(getContext().getResources().getConfiguration()).thenReturn(config);
+
+ // WHEN UserSwitcherViewMode is initialized and config has changed
+ setupUserSwitcher();
+ reset(mSecurityViewFlipper);
+ when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
+ mKeyguardSecurityContainer.onConfigurationChanged(config);
+
+ // THEN views are oriented side by side
+ verify(mSecurityViewFlipper).setLayoutParams(mLayoutCaptor.capture());
+ assertThat(mLayoutCaptor.getValue().gravity).isEqualTo(Gravity.RIGHT | Gravity.BOTTOM);
+ ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
+ R.id.keyguard_bouncer_user_switcher);
+ assertThat(((FrameLayout.LayoutParams) userSwitcher.getLayoutParams()).gravity)
+ .isEqualTo(Gravity.LEFT | Gravity.CENTER_VERTICAL);
+ }
+
+ @Test
+ public void testUserSwitcherModeViewGravityPortrait() {
+ // GIVEN one user has been setup and in landscape
+ when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(1));
+ Configuration config = new Configuration();
+ config.orientation = Configuration.ORIENTATION_PORTRAIT;
+ when(getContext().getResources().getConfiguration()).thenReturn(config);
+
+ // WHEN UserSwitcherViewMode is initialized and config has changed
+ setupUserSwitcher();
+ reset(mSecurityViewFlipper);
+ when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
+ mKeyguardSecurityContainer.onConfigurationChanged(config);
+
+ // THEN views are both centered horizontally
+ verify(mSecurityViewFlipper).setLayoutParams(mLayoutCaptor.capture());
+ assertThat(mLayoutCaptor.getValue().gravity).isEqualTo(Gravity.CENTER_HORIZONTAL);
+ ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
+ R.id.keyguard_bouncer_user_switcher);
+ assertThat(((FrameLayout.LayoutParams) userSwitcher.getLayoutParams()).gravity)
+ .isEqualTo(Gravity.CENTER_HORIZONTAL);
+ }
+
+ @Test
+ public void testLessThanTwoUsersDoesNotAllowDropDown() {
+ // GIVEN one user has been setup
+ when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(1));
+
+ // WHEN UserSwitcherViewMode is initialized
+ setupUserSwitcher();
+
+ // THEN the UserSwitcher anchor should not be clickable
+ ViewGroup anchor = mKeyguardSecurityContainer.findViewById(R.id.user_switcher_anchor);
+ assertThat(anchor.isClickable()).isFalse();
+ }
+
+ @Test
+ public void testTwoOrMoreUsersDoesAllowDropDown() {
+ // GIVEN one user has been setup
+ when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(2));
+
+ // WHEN UserSwitcherViewMode is initialized
+ setupUserSwitcher();
+
+ // THEN the UserSwitcher anchor should not be clickable
+ ViewGroup anchor = mKeyguardSecurityContainer.findViewById(R.id.user_switcher_anchor);
+ assertThat(anchor.isClickable()).isTrue();
+ }
+
+ private void setupUserSwitcher() {
+ mKeyguardSecurityContainer.initMode(KeyguardSecurityContainer.MODE_USER_SWITCHER,
+ mGlobalSettings, mFalsingManager, mUserSwitcherController);
+ }
+
+ private ArrayList<UserRecord> buildUserRecords(int count) {
+ ArrayList<UserRecord> users = new ArrayList<>();
+ for (int i = 0; i < count; ++i) {
+ UserInfo info = new UserInfo(i /* id */, "Name: " + i, null /* iconPath */,
+ 0 /* flags */);
+ users.add(new UserRecord(info, null, false /* isGuest */, false /* isCurrent */,
+ false /* isAddUser */, false /* isRestricted */, true /* isSwitchToEnabled */));
+ }
+ return users;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 241b02d..1dea678 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -64,6 +64,7 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -169,6 +170,8 @@
private TypedArray mBrightnessValues;
@Mock
private TypedArray mBrightnessBacklight;
+ @Mock
+ private SystemUIDialogManager mSystemUIDialogManager;
// Capture listeners so that they can be used to send events
@Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor;
@@ -178,8 +181,6 @@
@Captor private ArgumentCaptor<ScreenLifecycle.Observer> mScreenObserverCaptor;
private ScreenLifecycle.Observer mScreenObserver;
- @Captor private ArgumentCaptor<UdfpsAnimationViewController> mAnimViewControllerCaptor;
-
@Before
public void setUp() {
setUpResources();
@@ -237,7 +238,8 @@
mHandler,
mConfigurationController,
mSystemClock,
- mUnlockedScreenOffAnimationController);
+ mUnlockedScreenOffAnimationController,
+ mSystemUIDialogManager);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
@@ -641,12 +643,12 @@
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
moveEvent.recycle();
- // THEN low-tick haptic is played
+ // THEN tick haptic is played
verify(mVibrator).vibrate(
anyInt(),
anyString(),
any(),
- eq("udfps-onStart-tick"),
+ eq("udfps-onStart-click"),
eq(UdfpsController.VIBRATION_ATTRIBUTES));
// THEN make sure vibration attributes has so that it always will play the haptic,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 1cf21ac..0ae3c39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -42,6 +42,7 @@
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
@@ -92,6 +93,8 @@
@Mock
private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@Mock
+ private SystemUIDialogManager mDialogManager;
+ @Mock
private UdfpsController mUdfpsController;
private FakeSystemClock mSystemClock = new FakeSystemClock();
@@ -130,6 +133,7 @@
mSystemClock,
mKeyguardStateController,
mUnlockedScreenOffAnimationController,
+ mDialogManager,
mUdfpsController);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java
index abc2099..4a29ada 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java
@@ -28,7 +28,9 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.condition.Monitor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -43,7 +45,9 @@
@Mock
private CommunalManager mCommunalManager;
@Mock
- private CommunalConditionsMonitor mCommunalConditionsMonitor;
+ private Monitor mCommunalConditionsMonitor;
+
+ private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@Before
public void setup() {
@@ -51,12 +55,12 @@
mContext.addMockSystemService(CommunalManager.class, mCommunalManager);
doAnswer(invocation -> {
- final CommunalConditionsMonitor.Callback callback = invocation.getArgument(0);
+ final Monitor.Callback callback = invocation.getArgument(0);
callback.onConditionsChanged(true);
return null;
}).when(mCommunalConditionsMonitor).addCallback(any());
- mMonitor = new CommunalSourceMonitor(mCommunalConditionsMonitor);
+ mMonitor = new CommunalSourceMonitor(mExecutor, mCommunalConditionsMonitor);
final CommunalManagerUpdater updater = new CommunalManagerUpdater(mContext, mMonitor);
updater.start();
clearInvocations(mCommunalManager);
@@ -65,6 +69,7 @@
@Test
public void testUpdateSystemService_false() {
mMonitor.setSource(null);
+ mExecutor.runAllReady();
verify(mCommunalManager).setCommunalViewShowing(false);
}
@@ -72,6 +77,7 @@
public void testUpdateSystemService_true() {
final CommunalSource source = mock(CommunalSource.class);
mMonitor.setSource(source);
+ mExecutor.runAllReady();
verify(mCommunalManager).setCommunalViewShowing(true);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
index df9a2cb..df1cc76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
@@ -31,7 +31,9 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.condition.Monitor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -47,16 +49,17 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class CommunalSourceMonitorTest extends SysuiTestCase {
- @Mock private CommunalConditionsMonitor mCommunalConditionsMonitor;
+ @Mock private Monitor mCommunalConditionsMonitor;
- @Captor private ArgumentCaptor<CommunalConditionsMonitor.Callback> mConditionsCallbackCaptor;
+ @Captor private ArgumentCaptor<Monitor.Callback> mConditionsCallbackCaptor;
private CommunalSourceMonitor mCommunalSourceMonitor;
+ private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mCommunalSourceMonitor = new CommunalSourceMonitor(mCommunalConditionsMonitor);
+ mCommunalSourceMonitor = new CommunalSourceMonitor(mExecutor, mCommunalConditionsMonitor);
}
@Test
@@ -64,7 +67,7 @@
final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
final CommunalSource source = mock(CommunalSource.class);
- mCommunalSourceMonitor.setSource(source);
+ setSource(source);
mCommunalSourceMonitor.addCallback(callback);
setConditionsMet(true);
@@ -78,7 +81,7 @@
mCommunalSourceMonitor.addCallback(callback);
mCommunalSourceMonitor.removeCallback(callback);
- mCommunalSourceMonitor.setSource(source);
+ setSource(source);
verify(callback, never()).onSourceAvailable(any());
}
@@ -91,7 +94,7 @@
mCommunalSourceMonitor.addCallback(callback);
setConditionsMet(true);
clearInvocations(callback);
- mCommunalSourceMonitor.setSource(source);
+ setSource(source);
verifyOnSourceAvailableCalledWith(callback, source);
}
@@ -103,7 +106,7 @@
mCommunalSourceMonitor.addCallback(callback);
setConditionsMet(false);
- mCommunalSourceMonitor.setSource(source);
+ setSource(source);
verify(callback, never()).onSourceAvailable(any());
}
@@ -114,7 +117,7 @@
final CommunalSource source = mock(CommunalSource.class);
mCommunalSourceMonitor.addCallback(callback);
- mCommunalSourceMonitor.setSource(source);
+ setSource(source);
// The callback should not have executed since communal is disabled.
verify(callback, never()).onSourceAvailable(any());
@@ -130,7 +133,7 @@
final CommunalSource source = mock(CommunalSource.class);
mCommunalSourceMonitor.addCallback(callback);
- mCommunalSourceMonitor.setSource(source);
+ setSource(source);
setConditionsMet(true);
verifyOnSourceAvailableCalledWith(callback, source);
@@ -151,9 +154,16 @@
// Pushes an update on whether the communal conditions are met, assuming that a callback has
// been registered with the communal conditions monitor.
private void setConditionsMet(boolean value) {
+ mExecutor.runAllReady();
verify(mCommunalConditionsMonitor).addCallback(mConditionsCallbackCaptor.capture());
- final CommunalConditionsMonitor.Callback conditionsCallback =
+ final Monitor.Callback conditionsCallback =
mConditionsCallbackCaptor.getValue();
conditionsCallback.onConditionsChanged(value);
+ mExecutor.runAllReady();
+ }
+
+ private void setSource(CommunalSource source) {
+ mCommunalSourceMonitor.setSource(source);
+ mExecutor.runAllReady();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java
index 2ed38a4..1d9a059 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.communal;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -26,14 +27,16 @@
import android.content.res.Resources;
import android.testing.AndroidTestingRunner;
-import androidx.concurrent.futures.CallbackToFutureAdapter;
import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.concurrency.FakeExecutor;;
+import com.android.systemui.util.ref.GcWeakReference;
import com.android.systemui.util.time.FakeSystemClock;
+import com.google.common.util.concurrent.ListenableFuture;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,6 +55,35 @@
private static final int RETRY_DELAY_MS = 1000;
private static final int CONNECTION_MIN_DURATION_MS = 5000;
+ // A simple implementation of {@link CommunalSource.Observer} to capture a callback value.
+ // Used to ensure the references to a {@link CommunalSource.Observer.Callback} can be fully
+ // removed.
+ private static class FakeObserver implements CommunalSource.Observer {
+ public GcWeakReference<Callback> mLastCallback;
+
+ @Override
+ public void addCallback(Callback callback) {
+ mLastCallback = new GcWeakReference<>(callback);
+ }
+
+ @Override
+ public void removeCallback(Callback callback) {
+ if (mLastCallback.get() == callback) {
+ mLastCallback = null;
+ }
+ }
+ }
+
+ // A simple implementation of {@link CommunalSource} to capture callback values. This
+ // implementation better emulates the {@link WeakReference} wrapping behavior of
+ // {@link CommunalSource} implementations than a mock.
+ private static class FakeSource implements CommunalSource {
+ @Override
+ public ListenableFuture<CommunalViewResult> requestCommunalView(Context context) {
+ return null;
+ }
+ }
+
@Mock
private Context mContext;
@@ -61,8 +93,7 @@
private FakeSystemClock mFakeClock = new FakeSystemClock();
private FakeExecutor mFakeExecutor = new FakeExecutor(mFakeClock);
- @Mock
- private CommunalSource mSource;
+ private FakeSource mSource = new FakeSource();
@Mock
private CommunalSourceMonitor mCommunalSourceMonitor;
@@ -71,7 +102,9 @@
private CommunalSource.Connector mConnector;
@Mock
- private CommunalSource.Observer mObserver;
+ private CommunalSource.Connection mConnection;
+
+ private FakeObserver mObserver = new FakeObserver();
private CommunalSourcePrimer mPrimer;
@@ -93,35 +126,35 @@
mCommunalSourceMonitor, Optional.of(mConnector), Optional.of(mObserver));
}
+ private CommunalSource.Connection.Callback captureCallbackAndSend(
+ CommunalSource.Connector connector, Optional<CommunalSource> source) {
+ ArgumentCaptor<CommunalSource.Connection.Callback> connectionCallback =
+ ArgumentCaptor.forClass(CommunalSource.Connection.Callback.class);
+
+ verify(connector).connect(connectionCallback.capture());
+ Mockito.clearInvocations(connector);
+
+ final CommunalSource.Connection.Callback callback = connectionCallback.getValue();
+ callback.onSourceEstablished(source);
+
+ return callback;
+ }
+
@Test
public void testConnect() {
- when(mConnector.connect()).thenReturn(
- CallbackToFutureAdapter.getFuture(completer -> {
- completer.set(Optional.of(mSource));
- return "test";
- }));
-
mPrimer.onBootCompleted();
- mFakeExecutor.runAllReady();
+ captureCallbackAndSend(mConnector, Optional.of(mSource));
verify(mCommunalSourceMonitor).setSource(mSource);
}
@Test
public void testRetryOnBindFailure() throws Exception {
- when(mConnector.connect()).thenReturn(
- CallbackToFutureAdapter.getFuture(completer -> {
- completer.set(Optional.empty());
- return "test";
- }));
-
mPrimer.onBootCompleted();
- mFakeExecutor.runAllReady();
// Verify attempts happen. Note that we account for the retries plus initial attempt, which
// is not scheduled.
for (int attemptCount = 0; attemptCount < MAX_RETRIES + 1; attemptCount++) {
- verify(mConnector, times(1)).connect();
- clearInvocations(mConnector);
+ captureCallbackAndSend(mConnector, Optional.empty());
mFakeExecutor.advanceClockToNext();
mFakeExecutor.runAllReady();
}
@@ -131,76 +164,42 @@
@Test
public void testRetryOnDisconnectFailure() throws Exception {
- when(mConnector.connect()).thenReturn(
- CallbackToFutureAdapter.getFuture(completer -> {
- completer.set(Optional.of(mSource));
- return "test";
- }));
-
mPrimer.onBootCompleted();
- mFakeExecutor.runAllReady();
-
// Verify attempts happen. Note that we account for the retries plus initial attempt, which
// is not scheduled.
for (int attemptCount = 0; attemptCount < MAX_RETRIES + 1; attemptCount++) {
- verify(mConnector, times(1)).connect();
- clearInvocations(mConnector);
- ArgumentCaptor<CommunalSource.Callback> callbackCaptor =
- ArgumentCaptor.forClass(CommunalSource.Callback.class);
- verify(mSource).addCallback(callbackCaptor.capture());
- clearInvocations(mSource);
+ final CommunalSource.Connection.Callback callback =
+ captureCallbackAndSend(mConnector, Optional.of(mSource));
verify(mCommunalSourceMonitor).setSource(Mockito.notNull());
clearInvocations(mCommunalSourceMonitor);
- callbackCaptor.getValue().onDisconnected();
+ callback.onDisconnected();
mFakeExecutor.advanceClockToNext();
mFakeExecutor.runAllReady();
}
- verify(mConnector, never()).connect();
+ verify(mConnector, never()).connect(any());
}
@Test
public void testAttemptOnPackageChange() {
- when(mConnector.connect()).thenReturn(
- CallbackToFutureAdapter.getFuture(completer -> {
- completer.set(Optional.empty());
- return "test";
- }));
-
mPrimer.onBootCompleted();
- mFakeExecutor.runAllReady();
+ captureCallbackAndSend(mConnector, Optional.empty());
- final ArgumentCaptor<CommunalSource.Observer.Callback> callbackCaptor =
- ArgumentCaptor.forClass(CommunalSource.Observer.Callback.class);
- verify(mObserver).addCallback(callbackCaptor.capture());
+ mObserver.mLastCallback.get().onSourceChanged();
- clearInvocations(mConnector);
- callbackCaptor.getValue().onSourceChanged();
-
- verify(mConnector, times(1)).connect();
+ verify(mConnector, times(1)).connect(any());
}
@Test
public void testDisconnect() {
- final ArgumentCaptor<CommunalSource.Callback> callbackCaptor =
- ArgumentCaptor.forClass(CommunalSource.Callback.class);
-
- when(mConnector.connect()).thenReturn(
- CallbackToFutureAdapter.getFuture(completer -> {
- completer.set(Optional.of(mSource));
- return "test";
- }));
-
mPrimer.onBootCompleted();
- mFakeExecutor.runAllReady();
+ final CommunalSource.Connection.Callback callback =
+ captureCallbackAndSend(mConnector, Optional.of(mSource));
verify(mCommunalSourceMonitor).setSource(mSource);
- verify(mSource).addCallback(callbackCaptor.capture());
- clearInvocations(mConnector);
mFakeClock.advanceTime(CONNECTION_MIN_DURATION_MS + 1);
- callbackCaptor.getValue().onDisconnected();
- mFakeExecutor.runAllReady();
+ callback.onDisconnected();
- verify(mConnector).connect();
+ verify(mConnector).connect(any());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
index 873f7a4..55af51d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -45,6 +45,8 @@
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.unfold.FoldAodAnimationController;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.util.wakelock.WakeLockFake;
import org.junit.After;
@@ -54,6 +56,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class DozeUiTest extends SysuiTestCase {
@@ -79,6 +83,10 @@
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
+ private FoldAodAnimationController mFoldAodAnimationController;
+ @Mock
+ private SysUIUnfoldComponent mSysUIUnfoldComponent;
+ @Mock
private ConfigurationController mConfigurationController;
@Before
@@ -90,9 +98,13 @@
mWakeLock = new WakeLockFake();
mHandler = mHandlerThread.getThreadHandler();
+ when(mSysUIUnfoldComponent.getFoldAodAnimationController())
+ .thenReturn(mFoldAodAnimationController);
+
mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService,
- mStatusBarStateController, mConfigurationController);
+ mStatusBarStateController, Optional.of(mSysUIUnfoldComponent),
+ mConfigurationController);
mDozeUi.setDozeMachine(mMachine);
}
@@ -121,6 +133,7 @@
reset(mHost);
when(mDozeParameters.getAlwaysOn()).thenReturn(false);
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
+ when(mDozeParameters.shouldAnimateDozingChange()).thenReturn(true);
mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
verify(mHost).setAnimateScreenOff(eq(false));
@@ -131,6 +144,7 @@
reset(mHost);
when(mDozeParameters.getAlwaysOn()).thenReturn(true);
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
+ when(mDozeParameters.shouldAnimateDozingChange()).thenReturn(true);
// Take over when the keyguard is visible.
mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
@@ -142,6 +156,18 @@
}
@Test
+ public void propagatesAnimateScreenOff_alwaysOn_shouldAnimateDozingChangeIsFalse() {
+ reset(mHost);
+ when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+ when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
+ when(mDozeParameters.shouldAnimateDozingChange()).thenReturn(false);
+
+ // Take over when the keyguard is visible.
+ mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
+ verify(mHost).setAnimateScreenOff(eq(false));
+ }
+
+ @Test
public void neverAnimateScreenOff_whenNotSupported() {
// Re-initialize DozeParameters saying that the display requires blanking.
reset(mDozeParameters);
@@ -149,7 +175,8 @@
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService,
- mStatusBarStateController, mConfigurationController);
+ mStatusBarStateController, Optional.of(mSysUIUnfoldComponent),
+ mConfigurationController);
mDozeUi.setDozeMachine(mMachine);
// Never animate if display doesn't support it.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 53bfeee..904ee91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -37,6 +37,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
+import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -78,16 +79,41 @@
@Mock
DreamOverlayStateController mDreamOverlayStateController;
+ @Mock
+ DreamOverlayComponent.Factory mDreamOverlayStatusBarViewComponentFactory;
+
+ @Mock
+ DreamOverlayComponent mDreamOverlayComponent;
+
+ @Mock
+ DreamOverlayStatusBarViewController mDreamOverlayStatusBarViewController;
+
+ @Mock
+ DreamOverlayContainerView mDreamOverlayContainerView;
+
+ @Mock
+ ViewGroup mDreamOverlayContentView;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext.addMockSystemService(WindowManager.class, mWindowManager);
+
+ when(mDreamOverlayComponent.getDreamOverlayContentView())
+ .thenReturn(mDreamOverlayContentView);
+ when(mDreamOverlayComponent.getDreamOverlayContainerView())
+ .thenReturn(mDreamOverlayContainerView);
+ when(mDreamOverlayComponent.getDreamOverlayStatusBarViewController())
+ .thenReturn(mDreamOverlayStatusBarViewController);
+ when(mDreamOverlayStatusBarViewComponentFactory.create())
+ .thenReturn(mDreamOverlayComponent);
+
}
@Test
public void testInteraction() throws Exception {
final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor,
- mDreamOverlayStateController);
+ mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory);
final IBinder proxy = service.onBind(new Intent());
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
clearInvocations(mWindowManager);
@@ -128,7 +154,7 @@
@Test
public void testListening() throws Exception {
final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor,
- mDreamOverlayStateController);
+ mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory);
final IBinder proxy = service.onBind(new Intent());
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
@@ -150,4 +176,35 @@
// Verify provider is asked to create overlay.
verify(mProvider).onCreateOverlay(any(), any(), any());
}
+
+ @Test
+ public void testDreamOverlayStatusBarViewControllerInitialized() throws Exception {
+ final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor,
+ mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory);
+
+ final IBinder proxy = service.onBind(new Intent());
+ final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+
+ // Inform the overlay service of dream starting.
+ overlay.startDream(mWindowParams, mDreamOverlayCallback);
+ mMainExecutor.runAllReady();
+
+ verify(mDreamOverlayStatusBarViewController).init();
+ }
+
+ @Test
+ public void testRootViewAttachListenerIsAddedToDreamOverlayContentView() throws Exception {
+ final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor,
+ mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory);
+
+ final IBinder proxy = service.onBind(new Intent());
+ final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+
+ // Inform the overlay service of dream starting.
+ overlay.startDream(mWindowParams, mDreamOverlayCallback);
+ mMainExecutor.runAllReady();
+
+ verify(mDreamOverlayContentView).addOnAttachStateChangeListener(
+ any(View.OnAttachStateChangeListener.class));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
new file mode 100644
index 0000000..7f72dda
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -0,0 +1,195 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.dreams;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.statusbar.policy.BatteryController;
+
+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;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
+
+ @Mock
+ DreamOverlayStatusBarView mView;
+ @Mock
+ BatteryController mBatteryController;
+ @Mock
+ BatteryMeterViewController mBatteryMeterViewController;
+ @Mock
+ ConnectivityManager mConnectivityManager;
+ @Mock
+ NetworkCapabilities mNetworkCapabilities;
+ @Mock
+ Network mNetwork;
+
+ DreamOverlayStatusBarViewController mController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mController = new DreamOverlayStatusBarViewController(
+ mContext, mView, mBatteryController, mBatteryMeterViewController,
+ mConnectivityManager);
+ }
+
+ @Test
+ public void testOnInitInitializesControllers() {
+ mController.onInit();
+ verify(mBatteryMeterViewController).init();
+ }
+
+ @Test
+ public void testOnViewAttachedAddsBatteryControllerCallback() {
+ mController.onViewAttached();
+ verify(mBatteryController)
+ .addCallback(any(BatteryController.BatteryStateChangeCallback.class));
+ }
+
+ @Test
+ public void testOnViewAttachedRegistersNetworkCallback() {
+ mController.onViewAttached();
+ verify(mConnectivityManager)
+ .registerNetworkCallback(any(NetworkRequest.class), any(
+ ConnectivityManager.NetworkCallback.class));
+ }
+
+ @Test
+ public void testOnViewAttachedShowsWifiStatusWhenWifiUnavailable() {
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(false);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+ verify(mView).showWifiStatus(true);
+ }
+
+ @Test
+ public void testOnViewAttachedHidesWifiStatusWhenWifiAvailable() {
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(true);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+ verify(mView).showWifiStatus(false);
+ }
+
+ @Test
+ public void testOnViewAttachedShowsWifiStatusWhenNetworkCapabilitiesUnavailable() {
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(null);
+ mController.onViewAttached();
+ verify(mView).showWifiStatus(true);
+ }
+
+ @Test
+ public void testOnViewDetachedRemovesBatteryControllerCallback() {
+ mController.onViewDetached();
+ verify(mBatteryController)
+ .removeCallback(any(BatteryController.BatteryStateChangeCallback.class));
+ }
+
+ @Test
+ public void testOnViewDetachedUnregistersNetworkCallback() {
+ mController.onViewDetached();
+ verify(mConnectivityManager)
+ .unregisterNetworkCallback(any(ConnectivityManager.NetworkCallback.class));
+ }
+
+ @Test
+ public void testBatteryPercentTextShownWhenBatteryLevelChangesWhileCharging() {
+ final ArgumentCaptor<BatteryController.BatteryStateChangeCallback> callbackCapture =
+ ArgumentCaptor.forClass(BatteryController.BatteryStateChangeCallback.class);
+ mController.onViewAttached();
+ verify(mBatteryController).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onBatteryLevelChanged(1, true, true);
+ verify(mView).showBatteryPercentText(true);
+ }
+
+ @Test
+ public void testBatteryPercentTextHiddenWhenBatteryLevelChangesWhileNotCharging() {
+ final ArgumentCaptor<BatteryController.BatteryStateChangeCallback> callbackCapture =
+ ArgumentCaptor.forClass(BatteryController.BatteryStateChangeCallback.class);
+ mController.onViewAttached();
+ verify(mBatteryController).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onBatteryLevelChanged(1, true, false);
+ verify(mView).showBatteryPercentText(false);
+ }
+
+ @Test
+ public void testWifiStatusHiddenWhenWifiBecomesAvailable() {
+ // Make sure wifi starts out unavailable when onViewAttached is called.
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(false);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+
+ final ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackCapture =
+ ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+ verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
+ callbackCapture.getValue().onAvailable(mNetwork);
+ verify(mView).showWifiStatus(false);
+ }
+
+ @Test
+ public void testWifiStatusShownWhenWifiBecomesUnavailable() {
+ // Make sure wifi starts out available when onViewAttached is called.
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(true);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+
+ final ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackCapture =
+ ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+ verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
+ callbackCapture.getValue().onLost(mNetwork);
+ verify(mView).showWifiStatus(true);
+ }
+
+ @Test
+ public void testWifiStatusHiddenWhenCapabilitiesChange() {
+ // Make sure wifi starts out unavailable when onViewAttached is called.
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(false);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+
+ final ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackCapture =
+ ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+ verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(true);
+ callbackCapture.getValue().onCapabilitiesChanged(mNetwork, mNetworkCapabilities);
+ verify(mView).showWifiStatus(false);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
new file mode 100644
index 0000000..cb16bec
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
@@ -0,0 +1,320 @@
+/*
+ * 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.
+ */
+package com.android.systemui.flags
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.NameNotFoundException
+import android.content.res.Resources
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.settings.SecureSettings
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+import java.io.PrintWriter
+import java.io.Serializable
+import java.io.StringWriter
+import java.util.function.Consumer
+import org.mockito.Mockito.`when` as whenever
+
+/**
+ * NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
+ * overriding, and should never return any value other than the one provided as the default.
+ */
+@SmallTest
+class FeatureFlagsDebugTest : SysuiTestCase() {
+ private lateinit var mFeatureFlagsDebug: FeatureFlagsDebug
+
+ @Mock private lateinit var mFlagManager: FlagManager
+ @Mock private lateinit var mMockContext: Context
+ @Mock private lateinit var mSecureSettings: SecureSettings
+ @Mock private lateinit var mResources: Resources
+ @Mock private lateinit var mDumpManager: DumpManager
+ private val mFlagMap = mutableMapOf<Int, Flag<*>>()
+ private lateinit var mBroadcastReceiver: BroadcastReceiver
+ private lateinit var mClearCacheAction: Consumer<Int>
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ mFeatureFlagsDebug = FeatureFlagsDebug(
+ mFlagManager,
+ mMockContext,
+ mSecureSettings,
+ mResources,
+ mDumpManager,
+ { mFlagMap }
+ )
+ verify(mFlagManager).restartAction = any()
+ mBroadcastReceiver = withArgCaptor {
+ verify(mMockContext).registerReceiver(capture(), any(), nullable(), nullable())
+ }
+ mClearCacheAction = withArgCaptor {
+ verify(mFlagManager).clearCacheAction = capture()
+ }
+ whenever(mFlagManager.idToSettingsKey(any())).thenAnswer { "key-${it.arguments[0]}" }
+ }
+
+ @Test
+ fun testReadBooleanFlag() {
+ whenever(mFlagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
+ whenever(mFlagManager.readFlagValue<Boolean>(eq(4), any())).thenReturn(false)
+ assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(1, false))).isFalse()
+ assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(2, true))).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(3, false))).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(4, true))).isFalse()
+ }
+
+ @Test
+ fun testReadResourceBooleanFlag() {
+ whenever(mResources.getBoolean(1001)).thenReturn(false)
+ whenever(mResources.getBoolean(1002)).thenReturn(true)
+ whenever(mResources.getBoolean(1003)).thenReturn(false)
+ whenever(mResources.getBoolean(1004)).thenAnswer { throw NameNotFoundException() }
+ whenever(mResources.getBoolean(1005)).thenAnswer { throw NameNotFoundException() }
+
+ whenever(mFlagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
+ whenever(mFlagManager.readFlagValue<Boolean>(eq(5), any())).thenReturn(false)
+
+ assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(1, 1001))).isFalse()
+ assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(2, 1002))).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(3, 1003))).isTrue()
+
+ Assert.assertThrows(NameNotFoundException::class.java) {
+ mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(4, 1004))
+ }
+ // Test that resource is loaded (and validated) even when the setting is set.
+ // This prevents developers from not noticing when they reference an invalid resource.
+ Assert.assertThrows(NameNotFoundException::class.java) {
+ mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(5, 1005))
+ }
+ }
+
+ @Test
+ fun testReadStringFlag() {
+ whenever(mFlagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo")
+ whenever(mFlagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar")
+ assertThat(mFeatureFlagsDebug.getString(StringFlag(1, "biz"))).isEqualTo("biz")
+ assertThat(mFeatureFlagsDebug.getString(StringFlag(2, "baz"))).isEqualTo("baz")
+ assertThat(mFeatureFlagsDebug.getString(StringFlag(3, "buz"))).isEqualTo("foo")
+ assertThat(mFeatureFlagsDebug.getString(StringFlag(4, "buz"))).isEqualTo("bar")
+ }
+
+ @Test
+ fun testReadResourceStringFlag() {
+ whenever(mResources.getString(1001)).thenReturn("")
+ whenever(mResources.getString(1002)).thenReturn("resource2")
+ whenever(mResources.getString(1003)).thenReturn("resource3")
+ whenever(mResources.getString(1004)).thenReturn(null)
+ whenever(mResources.getString(1005)).thenAnswer { throw NameNotFoundException() }
+ whenever(mResources.getString(1006)).thenAnswer { throw NameNotFoundException() }
+
+ whenever(mFlagManager.readFlagValue<String>(eq(3), any())).thenReturn("override3")
+ whenever(mFlagManager.readFlagValue<String>(eq(4), any())).thenReturn("override4")
+ whenever(mFlagManager.readFlagValue<String>(eq(6), any())).thenReturn("override6")
+
+ assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(1, 1001))).isEqualTo("")
+ assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(2, 1002))).isEqualTo("resource2")
+ assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(3, 1003))).isEqualTo("override3")
+
+ Assert.assertThrows(NullPointerException::class.java) {
+ mFeatureFlagsDebug.getString(ResourceStringFlag(4, 1004))
+ }
+ Assert.assertThrows(NameNotFoundException::class.java) {
+ mFeatureFlagsDebug.getString(ResourceStringFlag(5, 1005))
+ }
+ // Test that resource is loaded (and validated) even when the setting is set.
+ // This prevents developers from not noticing when they reference an invalid resource.
+ Assert.assertThrows(NameNotFoundException::class.java) {
+ mFeatureFlagsDebug.getString(ResourceStringFlag(6, 1005))
+ }
+ }
+
+ @Test
+ fun testBroadcastReceiverIgnoresInvalidData() {
+ addFlag(BooleanFlag(1, false))
+ addFlag(ResourceBooleanFlag(2, 1002))
+ addFlag(StringFlag(3, "flag3"))
+ addFlag(ResourceStringFlag(4, 1004))
+
+ mBroadcastReceiver.onReceive(mMockContext, null)
+ mBroadcastReceiver.onReceive(mMockContext, Intent())
+ mBroadcastReceiver.onReceive(mMockContext, Intent("invalid action"))
+ mBroadcastReceiver.onReceive(mMockContext, Intent(FlagManager.ACTION_SET_FLAG))
+ setByBroadcast(0, false) // unknown id does nothing
+ setByBroadcast(1, "string") // wrong type does nothing
+ setByBroadcast(2, 123) // wrong type does nothing
+ setByBroadcast(3, false) // wrong type does nothing
+ setByBroadcast(4, 123) // wrong type does nothing
+ verifyNoMoreInteractions(mFlagManager, mSecureSettings)
+ }
+
+ @Test
+ fun testIntentWithIdButNoValueKeyClears() {
+ addFlag(BooleanFlag(1, false))
+
+ // trying to erase an id not in the map does noting
+ mBroadcastReceiver.onReceive(
+ mMockContext,
+ Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_ID, 0)
+ )
+ verifyNoMoreInteractions(mFlagManager, mSecureSettings)
+
+ // valid id with no value puts empty string in the setting
+ mBroadcastReceiver.onReceive(
+ mMockContext,
+ Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_ID, 1)
+ )
+ verifyPutData(1, "", numReads = 0)
+ }
+
+ @Test
+ fun testSetBooleanFlag() {
+ addFlag(BooleanFlag(1, false))
+ addFlag(BooleanFlag(2, false))
+ addFlag(ResourceBooleanFlag(3, 1003))
+ addFlag(ResourceBooleanFlag(4, 1004))
+
+ setByBroadcast(1, false)
+ verifyPutData(1, "{\"type\":\"boolean\",\"value\":false}")
+
+ setByBroadcast(2, true)
+ verifyPutData(2, "{\"type\":\"boolean\",\"value\":true}")
+
+ setByBroadcast(3, false)
+ verifyPutData(3, "{\"type\":\"boolean\",\"value\":false}")
+
+ setByBroadcast(4, true)
+ verifyPutData(4, "{\"type\":\"boolean\",\"value\":true}")
+ }
+
+ @Test
+ fun testSetStringFlag() {
+ addFlag(StringFlag(1, "flag1"))
+ addFlag(ResourceStringFlag(2, 1002))
+
+ setByBroadcast(1, "override1")
+ verifyPutData(1, "{\"type\":\"string\",\"value\":\"override1\"}")
+
+ setByBroadcast(2, "override2")
+ verifyPutData(2, "{\"type\":\"string\",\"value\":\"override2\"}")
+ }
+
+ @Test
+ fun testSetFlagClearsCache() {
+ val flag1 = addFlag(StringFlag(1, "flag1"))
+ whenever(mFlagManager.readFlagValue<String>(eq(1), any())).thenReturn("original")
+
+ // gets the flag & cache it
+ assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("original")
+ verify(mFlagManager).readFlagValue(eq(1), eq(StringFlagSerializer))
+
+ // hit the cache
+ assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("original")
+ verifyNoMoreInteractions(mFlagManager)
+
+ // set the flag
+ setByBroadcast(1, "new")
+ verifyPutData(1, "{\"type\":\"string\",\"value\":\"new\"}", numReads = 2)
+ whenever(mFlagManager.readFlagValue<String>(eq(1), any())).thenReturn("new")
+
+ assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("new")
+ verify(mFlagManager, times(3)).readFlagValue(eq(1), eq(StringFlagSerializer))
+ }
+
+ private fun verifyPutData(id: Int, data: String, numReads: Int = 1) {
+ inOrder(mFlagManager, mSecureSettings).apply {
+ verify(mFlagManager, times(numReads)).readFlagValue(eq(id), any<FlagSerializer<*>>())
+ verify(mFlagManager).idToSettingsKey(eq(id))
+ verify(mSecureSettings).putString(eq("key-$id"), eq(data))
+ verify(mFlagManager).dispatchListenersAndMaybeRestart(eq(id))
+ }.verifyNoMoreInteractions()
+ verifyNoMoreInteractions(mFlagManager, mSecureSettings)
+ }
+
+ private fun setByBroadcast(id: Int, value: Serializable?) {
+ val intent = Intent(FlagManager.ACTION_SET_FLAG)
+ intent.putExtra(FlagManager.EXTRA_ID, id)
+ intent.putExtra(FlagManager.EXTRA_VALUE, value)
+ mBroadcastReceiver.onReceive(mMockContext, intent)
+ }
+
+ private fun <F : Flag<*>> addFlag(flag: F): F {
+ val old = mFlagMap.put(flag.id, flag)
+ check(old == null) { "Flag ${flag.id} already registered" }
+ return flag
+ }
+
+ @Test
+ fun testDump() {
+ val flag1 = BooleanFlag(1, true)
+ val flag2 = ResourceBooleanFlag(2, 1002)
+ val flag3 = BooleanFlag(3, false)
+ val flag4 = StringFlag(4, "")
+ val flag5 = StringFlag(5, "flag5default")
+ val flag6 = ResourceStringFlag(6, 1006)
+ val flag7 = ResourceStringFlag(7, 1007)
+
+ whenever(mResources.getBoolean(1002)).thenReturn(true)
+ whenever(mResources.getString(1006)).thenReturn("resource1006")
+ whenever(mResources.getString(1007)).thenReturn("resource1007")
+ whenever(mFlagManager.readFlagValue(eq(7), eq(StringFlagSerializer)))
+ .thenReturn("override7")
+
+ // WHEN the flags have been accessed
+ assertThat(mFeatureFlagsDebug.isEnabled(flag1)).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(flag2)).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(flag3)).isFalse()
+ assertThat(mFeatureFlagsDebug.getString(flag4)).isEmpty()
+ assertThat(mFeatureFlagsDebug.getString(flag5)).isEqualTo("flag5default")
+ assertThat(mFeatureFlagsDebug.getString(flag6)).isEqualTo("resource1006")
+ assertThat(mFeatureFlagsDebug.getString(flag7)).isEqualTo("override7")
+
+ // THEN the dump contains the flags and the default values
+ val dump = dumpToString()
+ assertThat(dump).contains(" sysui_flag_1: true\n")
+ assertThat(dump).contains(" sysui_flag_2: true\n")
+ assertThat(dump).contains(" sysui_flag_3: false\n")
+ assertThat(dump).contains(" sysui_flag_4: [length=0] \"\"\n")
+ assertThat(dump).contains(" sysui_flag_5: [length=12] \"flag5default\"\n")
+ assertThat(dump).contains(" sysui_flag_6: [length=12] \"resource1006\"\n")
+ assertThat(dump).contains(" sysui_flag_7: [length=9] \"override7\"\n")
+ }
+
+ private fun dumpToString(): String {
+ val sw = StringWriter()
+ val pw = PrintWriter(sw)
+ mFeatureFlagsDebug.dump(mock(), pw, emptyArray<String>())
+ pw.flush()
+ return sw.toString()
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.java
deleted file mode 100644
index dea42f9..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.java
+++ /dev/null
@@ -1,109 +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.
- */
-
-package com.android.systemui.flags;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.content.res.Resources;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-/**
- * NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
- * overriding, and should never return any value other than the one provided as the default.
- */
-@SmallTest
-public class FeatureFlagsReleaseTest extends SysuiTestCase {
- FeatureFlagsRelease mFeatureFlagsRelease;
-
- @Mock private Resources mResources;
- @Mock private DumpManager mDumpManager;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
-
- mFeatureFlagsRelease = new FeatureFlagsRelease(mResources, mDumpManager);
- }
-
- @After
- public void onFinished() {
- // The dump manager should be registered with even for the release version, but that's it.
- verify(mDumpManager).registerDumpable(anyString(), any());
- verifyNoMoreInteractions(mDumpManager);
- }
-
- @Test
- public void testBooleanResourceFlag() {
- int flagId = 213;
- int flagResourceId = 3;
- ResourceBooleanFlag flag = new ResourceBooleanFlag(flagId, flagResourceId);
- when(mResources.getBoolean(flagResourceId)).thenReturn(true);
-
- assertThat(mFeatureFlagsRelease.isEnabled(flag)).isTrue();
- }
-
- @Test
- public void testDump() {
- int flagIdA = 213;
- int flagIdB = 18;
- int flagResourceId = 3;
- BooleanFlag flagA = new BooleanFlag(flagIdA, true);
- ResourceBooleanFlag flagB = new ResourceBooleanFlag(flagIdB, flagResourceId);
- when(mResources.getBoolean(flagResourceId)).thenReturn(true);
-
- // WHEN the flags have been accessed
- assertThat(mFeatureFlagsRelease.isEnabled(1, false)).isFalse();
- assertThat(mFeatureFlagsRelease.isEnabled(flagA)).isTrue();
- assertThat(mFeatureFlagsRelease.isEnabled(flagB)).isTrue();
-
- // THEN the dump contains the flags and the default values
- String dump = dumpToString();
- assertThat(dump).contains(" sysui_flag_1: false\n");
- assertThat(dump).contains(" sysui_flag_" + flagIdA + ": true\n");
- assertThat(dump).contains(" sysui_flag_" + flagIdB + ": true\n");
- }
-
- private String dumpToString() {
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
- mFeatureFlagsRelease.dump(mock(FileDescriptor.class), pw, new String[0]);
- pw.flush();
- String dump = sw.toString();
- return dump;
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
new file mode 100644
index 0000000..b5e6602
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+package com.android.systemui.flags
+
+import android.content.pm.PackageManager.NameNotFoundException
+import android.content.res.Resources
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+import java.io.PrintWriter
+import java.io.StringWriter
+import org.mockito.Mockito.`when` as whenever
+
+/**
+ * NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
+ * overriding, and should never return any value other than the one provided as the default.
+ */
+@SmallTest
+class FeatureFlagsReleaseTest : SysuiTestCase() {
+ private lateinit var mFeatureFlagsRelease: FeatureFlagsRelease
+
+ @Mock private lateinit var mResources: Resources
+ @Mock private lateinit var mDumpManager: DumpManager
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ mFeatureFlagsRelease = FeatureFlagsRelease(mResources, mDumpManager)
+ }
+
+ @After
+ fun onFinished() {
+ // The dump manager should be registered with even for the release version, but that's it.
+ verify(mDumpManager).registerDumpable(any(), any())
+ verifyNoMoreInteractions(mDumpManager)
+ }
+
+ @Test
+ fun testBooleanResourceFlag() {
+ val flagId = 213
+ val flagResourceId = 3
+ val flag = ResourceBooleanFlag(flagId, flagResourceId)
+ whenever(mResources.getBoolean(flagResourceId)).thenReturn(true)
+ assertThat(mFeatureFlagsRelease.isEnabled(flag)).isTrue()
+ }
+
+ @Test
+ fun testReadResourceStringFlag() {
+ whenever(mResources.getString(1001)).thenReturn("")
+ whenever(mResources.getString(1002)).thenReturn("res2")
+ whenever(mResources.getString(1003)).thenReturn(null)
+ whenever(mResources.getString(1004)).thenAnswer { throw NameNotFoundException() }
+
+ assertThat(mFeatureFlagsRelease.getString(ResourceStringFlag(1, 1001))).isEqualTo("")
+ assertThat(mFeatureFlagsRelease.getString(ResourceStringFlag(2, 1002))).isEqualTo("res2")
+
+ assertThrows(NullPointerException::class.java) {
+ mFeatureFlagsRelease.getString(ResourceStringFlag(3, 1003))
+ }
+ assertThrows(NameNotFoundException::class.java) {
+ mFeatureFlagsRelease.getString(ResourceStringFlag(4, 1004))
+ }
+ }
+
+ @Test
+ fun testDump() {
+ val flag1 = BooleanFlag(1, true)
+ val flag2 = ResourceBooleanFlag(2, 1002)
+ val flag3 = BooleanFlag(3, false)
+ val flag4 = StringFlag(4, "")
+ val flag5 = StringFlag(5, "flag5default")
+ val flag6 = ResourceStringFlag(6, 1006)
+
+ whenever(mResources.getBoolean(1002)).thenReturn(true)
+ whenever(mResources.getString(1006)).thenReturn("resource1006")
+ whenever(mResources.getString(1007)).thenReturn("resource1007")
+
+ // WHEN the flags have been accessed
+ assertThat(mFeatureFlagsRelease.isEnabled(flag1)).isTrue()
+ assertThat(mFeatureFlagsRelease.isEnabled(flag2)).isTrue()
+ assertThat(mFeatureFlagsRelease.isEnabled(flag3)).isFalse()
+ assertThat(mFeatureFlagsRelease.getString(flag4)).isEmpty()
+ assertThat(mFeatureFlagsRelease.getString(flag5)).isEqualTo("flag5default")
+ assertThat(mFeatureFlagsRelease.getString(flag6)).isEqualTo("resource1006")
+
+ // THEN the dump contains the flags and the default values
+ val dump = dumpToString()
+ assertThat(dump).contains(" sysui_flag_1: true\n")
+ assertThat(dump).contains(" sysui_flag_2: true\n")
+ assertThat(dump).contains(" sysui_flag_3: false\n")
+ assertThat(dump).contains(" sysui_flag_4: [length=0] \"\"\n")
+ assertThat(dump).contains(" sysui_flag_5: [length=12] \"flag5default\"\n")
+ assertThat(dump).contains(" sysui_flag_6: [length=12] \"resource1006\"\n")
+ }
+
+ private fun dumpToString(): String {
+ val sw = StringWriter()
+ val pw = PrintWriter(sw)
+ mFeatureFlagsRelease.dump(mock(), pw, emptyArray())
+ pw.flush()
+ return sw.toString()
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
new file mode 100644
index 0000000..644bd21
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
@@ -0,0 +1,302 @@
+/*
+ * 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.
+ */
+package com.android.systemui.flags
+
+import android.content.Context
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+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.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+import java.util.function.Consumer
+import org.mockito.Mockito.`when` as whenever
+
+/**
+ * NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
+ * overriding, and should never return any value other than the one provided as the default.
+ */
+@SmallTest
+class FlagManagerTest : SysuiTestCase() {
+ private lateinit var mFlagManager: FlagManager
+
+ @Mock private lateinit var mMockContext: Context
+ @Mock private lateinit var mFlagSettingsHelper: FlagSettingsHelper
+ @Mock private lateinit var mHandler: Handler
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ mFlagManager = FlagManager(mMockContext, mFlagSettingsHelper, mHandler)
+ }
+
+ @Test
+ fun testContentObserverAddedAndRemoved() {
+ val listener1 = mock<FlagListenable.Listener>()
+ val listener2 = mock<FlagListenable.Listener>()
+
+ // no interactions before adding listener
+ verifyNoMoreInteractions(mFlagSettingsHelper)
+
+ // adding the first listener registers the observer
+ mFlagManager.addListener(BooleanFlag(1, true), listener1)
+ val observer = withArgCaptor<ContentObserver> {
+ verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
+ }
+ verifyNoMoreInteractions(mFlagSettingsHelper)
+
+ // adding another listener does nothing
+ mFlagManager.addListener(BooleanFlag(2, true), listener2)
+ verifyNoMoreInteractions(mFlagSettingsHelper)
+
+ // removing the original listener does nothing with second one still present
+ mFlagManager.removeListener(listener1)
+ verifyNoMoreInteractions(mFlagSettingsHelper)
+
+ // removing the final listener unregisters the observer
+ mFlagManager.removeListener(listener2)
+ verify(mFlagSettingsHelper).unregisterContentObserver(eq(observer))
+ verifyNoMoreInteractions(mFlagSettingsHelper)
+ }
+
+ @Test
+ fun testObserverClearsCache() {
+ val listener = mock<FlagListenable.Listener>()
+ val clearCacheAction = mock<Consumer<Int>>()
+ mFlagManager.clearCacheAction = clearCacheAction
+ mFlagManager.addListener(BooleanFlag(1, true), listener)
+ val observer = withArgCaptor<ContentObserver> {
+ verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
+ }
+ observer.onChange(false, flagUri(1))
+ verify(clearCacheAction).accept(eq(1))
+ }
+
+ @Test
+ fun testObserverInvokesListeners() {
+ val listener1 = mock<FlagListenable.Listener>()
+ val listener10 = mock<FlagListenable.Listener>()
+ mFlagManager.addListener(BooleanFlag(1, true), listener1)
+ mFlagManager.addListener(BooleanFlag(10, true), listener10)
+ val observer = withArgCaptor<ContentObserver> {
+ verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
+ }
+ observer.onChange(false, flagUri(1))
+ val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
+ verify(listener1).onFlagChanged(capture())
+ }
+ assertThat(flagEvent1.flagId).isEqualTo(1)
+ verifyNoMoreInteractions(listener1, listener10)
+
+ observer.onChange(false, flagUri(10))
+ val flagEvent10 = withArgCaptor<FlagListenable.FlagEvent> {
+ verify(listener10).onFlagChanged(capture())
+ }
+ assertThat(flagEvent10.flagId).isEqualTo(10)
+ verifyNoMoreInteractions(listener1, listener10)
+ }
+
+ fun flagUri(id: Int): Uri = Uri.parse("content://settings/system/systemui/flags/$id")
+
+ @Test
+ fun testOnlySpecificFlagListenerIsInvoked() {
+ val listener1 = mock<FlagListenable.Listener>()
+ val listener10 = mock<FlagListenable.Listener>()
+ mFlagManager.addListener(BooleanFlag(1, true), listener1)
+ mFlagManager.addListener(BooleanFlag(10, true), listener10)
+
+ mFlagManager.dispatchListenersAndMaybeRestart(1)
+ val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
+ verify(listener1).onFlagChanged(capture())
+ }
+ assertThat(flagEvent1.flagId).isEqualTo(1)
+ verifyNoMoreInteractions(listener1, listener10)
+
+ mFlagManager.dispatchListenersAndMaybeRestart(10)
+ val flagEvent10 = withArgCaptor<FlagListenable.FlagEvent> {
+ verify(listener10).onFlagChanged(capture())
+ }
+ assertThat(flagEvent10.flagId).isEqualTo(10)
+ verifyNoMoreInteractions(listener1, listener10)
+ }
+
+ @Test
+ fun testSameListenerCanBeUsedForMultipleFlags() {
+ val listener = mock<FlagListenable.Listener>()
+ mFlagManager.addListener(BooleanFlag(1, true), listener)
+ mFlagManager.addListener(BooleanFlag(10, true), listener)
+
+ mFlagManager.dispatchListenersAndMaybeRestart(1)
+ val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
+ verify(listener).onFlagChanged(capture())
+ }
+ assertThat(flagEvent1.flagId).isEqualTo(1)
+ verifyNoMoreInteractions(listener)
+
+ mFlagManager.dispatchListenersAndMaybeRestart(10)
+ val flagEvent10 = withArgCaptor<FlagListenable.FlagEvent> {
+ verify(listener, times(2)).onFlagChanged(capture())
+ }
+ assertThat(flagEvent10.flagId).isEqualTo(10)
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testRestartWithNoListeners() {
+ val restartAction = mock<Consumer<Boolean>>()
+ mFlagManager.restartAction = restartAction
+ mFlagManager.dispatchListenersAndMaybeRestart(1)
+ verify(restartAction).accept(eq(false))
+ verifyNoMoreInteractions(restartAction)
+ }
+
+ @Test
+ fun testListenerCanSuppressRestart() {
+ val restartAction = mock<Consumer<Boolean>>()
+ mFlagManager.restartAction = restartAction
+ mFlagManager.addListener(BooleanFlag(1, true)) { event ->
+ event.requestNoRestart()
+ }
+ mFlagManager.dispatchListenersAndMaybeRestart(1)
+ verify(restartAction).accept(eq(true))
+ verifyNoMoreInteractions(restartAction)
+ }
+
+ @Test
+ fun testListenerOnlySuppressesRestartForOwnFlag() {
+ val restartAction = mock<Consumer<Boolean>>()
+ mFlagManager.restartAction = restartAction
+ mFlagManager.addListener(BooleanFlag(10, true)) { event ->
+ event.requestNoRestart()
+ }
+ mFlagManager.dispatchListenersAndMaybeRestart(1)
+ verify(restartAction).accept(eq(false))
+ verifyNoMoreInteractions(restartAction)
+ }
+
+ @Test
+ fun testRestartWhenNotAllListenersRequestSuppress() {
+ val restartAction = mock<Consumer<Boolean>>()
+ mFlagManager.restartAction = restartAction
+ mFlagManager.addListener(BooleanFlag(10, true)) { event ->
+ event.requestNoRestart()
+ }
+ mFlagManager.addListener(BooleanFlag(10, true)) {
+ // do not request
+ }
+ mFlagManager.dispatchListenersAndMaybeRestart(1)
+ verify(restartAction).accept(eq(false))
+ verifyNoMoreInteractions(restartAction)
+ }
+
+ @Test
+ fun testReadBooleanFlag() {
+ // test that null string returns null
+ whenever(mFlagSettingsHelper.getString(any())).thenReturn(null)
+ assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isNull()
+
+ // test that empty string returns null
+ whenever(mFlagSettingsHelper.getString(any())).thenReturn("")
+ assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isNull()
+
+ // test false
+ whenever(mFlagSettingsHelper.getString(any()))
+ .thenReturn("{\"type\":\"boolean\",\"value\":false}")
+ assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isFalse()
+
+ // test true
+ whenever(mFlagSettingsHelper.getString(any()))
+ .thenReturn("{\"type\":\"boolean\",\"value\":true}")
+ assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isTrue()
+
+ // Reading a value of a different type should just return null
+ whenever(mFlagSettingsHelper.getString(any()))
+ .thenReturn("{\"type\":\"string\",\"value\":\"foo\"}")
+ assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isNull()
+
+ // Reading a value that isn't json should throw an exception
+ assertThrows(InvalidFlagStorageException::class.java) {
+ whenever(mFlagSettingsHelper.getString(any())).thenReturn("1")
+ mFlagManager.readFlagValue(1, BooleanFlagSerializer)
+ }
+ }
+
+ @Test
+ fun testSerializeBooleanFlag() {
+ // test false
+ assertThat(BooleanFlagSerializer.toSettingsData(false))
+ .isEqualTo("{\"type\":\"boolean\",\"value\":false}")
+
+ // test true
+ assertThat(BooleanFlagSerializer.toSettingsData(true))
+ .isEqualTo("{\"type\":\"boolean\",\"value\":true}")
+ }
+
+ @Test
+ fun testReadStringFlag() {
+ // test that null string returns null
+ whenever(mFlagSettingsHelper.getString(any())).thenReturn(null)
+ assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isNull()
+
+ // test that empty string returns null
+ whenever(mFlagSettingsHelper.getString(any())).thenReturn("")
+ assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isNull()
+
+ // test json with the empty string value returns empty string
+ whenever(mFlagSettingsHelper.getString(any()))
+ .thenReturn("{\"type\":\"string\",\"value\":\"\"}")
+ assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isEqualTo("")
+
+ // test string with value is returned
+ whenever(mFlagSettingsHelper.getString(any()))
+ .thenReturn("{\"type\":\"string\",\"value\":\"foo\"}")
+ assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isEqualTo("foo")
+
+ // Reading a value of a different type should just return null
+ whenever(mFlagSettingsHelper.getString(any()))
+ .thenReturn("{\"type\":\"boolean\",\"value\":false}")
+ assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isNull()
+
+ // Reading a value that isn't json should throw an exception
+ assertThrows(InvalidFlagStorageException::class.java) {
+ whenever(mFlagSettingsHelper.getString(any())).thenReturn("1")
+ mFlagManager.readFlagValue(1, StringFlagSerializer)
+ }
+ }
+
+ @Test
+ fun testSerializeStringFlag() {
+ // test empty string
+ assertThat(StringFlagSerializer.toSettingsData(""))
+ .isEqualTo("{\"type\":\"string\",\"value\":\"\"}")
+
+ // test string "foo"
+ assertThat(StringFlagSerializer.toSettingsData("foo"))
+ .isEqualTo("{\"type\":\"string\",\"value\":\"foo\"}")
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index bf5522c..e3a7e3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -62,6 +62,7 @@
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -117,6 +118,7 @@
@Mock private StatusBar mStatusBar;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private DialogLaunchAnimator mDialogLaunchAnimator;
+ @Mock private SystemUIDialogManager mDialogManager;
private TestableLooper mTestableLooper;
@@ -162,7 +164,8 @@
mPackageManager,
Optional.of(mStatusBar),
mKeyguardUpdateMonitor,
- mDialogLaunchAnimator);
+ mDialogLaunchAnimator,
+ mDialogManager);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 8d329c5..27fcb11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -58,6 +58,7 @@
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.unfold.FoldAodAnimationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
import com.android.systemui.util.DeviceConfigProxy;
@@ -69,7 +70,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -95,62 +95,40 @@
private @Mock NavigationModeController mNavigationModeController;
private @Mock KeyguardDisplayManager mKeyguardDisplayManager;
private @Mock DozeParameters mDozeParameters;
- private @Mock Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent;
- private @Mock Optional<UnfoldLightRevealOverlayAnimation> mUnfoldAnimationOptional;
+ private @Mock SysUIUnfoldComponent mSysUIUnfoldComponent;
private @Mock UnfoldLightRevealOverlayAnimation mUnfoldAnimation;
private @Mock SysuiStatusBarStateController mStatusBarStateController;
private @Mock KeyguardStateController mKeyguardStateController;
private @Mock NotificationShadeDepthController mNotificationShadeDepthController;
private @Mock KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private @Mock ScreenOffAnimationController mScreenOffAnimationController;
+ private @Mock FoldAodAnimationController mFoldAodAnimationController;
private @Mock IKeyguardDrawnCallback mKeyguardDrawnCallback;
private @Mock InteractionJankMonitor mInteractionJankMonitor;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
+ private Optional<SysUIUnfoldComponent> mSysUiUnfoldComponentOptional;
+
private FalsingCollectorFake mFalsingCollector;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mFalsingCollector = new FalsingCollectorFake();
+ mSysUiUnfoldComponentOptional = Optional.of(mSysUIUnfoldComponent);
when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager);
when(mPowerManager.newWakeLock(anyInt(), any())).thenReturn(mock(WakeLock.class));
- when(mSysUIUnfoldComponent.map(
- ArgumentMatchers.<Function<SysUIUnfoldComponent, UnfoldLightRevealOverlayAnimation>>
- any()))
- .thenReturn(mUnfoldAnimationOptional);
- when(mUnfoldAnimationOptional.isPresent()).thenReturn(true);
- when(mUnfoldAnimationOptional.get()).thenReturn(mUnfoldAnimation);
+ when(mSysUIUnfoldComponent.getUnfoldLightRevealOverlayAnimation())
+ .thenReturn(mUnfoldAnimation);
+ when(mSysUIUnfoldComponent.getFoldAodAnimationController())
+ .thenReturn(mFoldAodAnimationController);
+
when(mInteractionJankMonitor.begin(any(), anyInt())).thenReturn(true);
when(mInteractionJankMonitor.end(anyInt())).thenReturn(true);
- mViewMediator = new KeyguardViewMediator(
- mContext,
- mFalsingCollector,
- mLockPatternUtils,
- mBroadcastDispatcher,
- () -> mStatusBarKeyguardViewManager,
- mDismissCallbackRegistry,
- mUpdateMonitor,
- mDumpManager,
- mUiBgExecutor,
- mPowerManager,
- mTrustManager,
- mUserSwitcherController,
- mDeviceConfig,
- mNavigationModeController,
- mKeyguardDisplayManager,
- mDozeParameters,
- mSysUIUnfoldComponent,
- mStatusBarStateController,
- mKeyguardStateController,
- () -> mKeyguardUnlockAnimationController,
- mScreenOffAnimationController,
- () -> mNotificationShadeDepthController,
- mInteractionJankMonitor);
- mViewMediator.start();
+ createAndStartViewMediator();
}
@Test
@@ -179,6 +157,7 @@
mViewMediator.onScreenTurningOn(mKeyguardDrawnCallback);
TestableLooper.get(this).processAllMessages();
onUnfoldOverlayReady();
+ onFoldAodReady();
// Should be called when both unfold overlay and keyguard drawn ready
verify(mKeyguardDrawnCallback).onDrawn();
@@ -188,7 +167,8 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public void testUnfoldTransitionDisabledDrawnTasksReady_onScreenTurningOn_callsDrawnCallback()
throws RemoteException {
- when(mUnfoldAnimationOptional.isPresent()).thenReturn(false);
+ mSysUiUnfoldComponentOptional = Optional.empty();
+ createAndStartViewMediator();
mViewMediator.onScreenTurningOn(mKeyguardDrawnCallback);
TestableLooper.get(this).processAllMessages();
@@ -200,6 +180,7 @@
@Test
public void testIsAnimatingScreenOff() {
when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true);
+ when(mDozeParameters.shouldAnimateDozingChange()).thenReturn(true);
mViewMediator.onFinishedGoingToSleep(OFF_BECAUSE_OF_USER, false);
mViewMediator.setDozing(true);
@@ -244,4 +225,39 @@
overlayReadyCaptor.getValue().run();
TestableLooper.get(this).processAllMessages();
}
+
+ private void onFoldAodReady() {
+ ArgumentCaptor<Runnable> ready = ArgumentCaptor.forClass(Runnable.class);
+ verify(mFoldAodAnimationController).onScreenTurningOn(ready.capture());
+ ready.getValue().run();
+ TestableLooper.get(this).processAllMessages();
+ }
+
+ private void createAndStartViewMediator() {
+ mViewMediator = new KeyguardViewMediator(
+ mContext,
+ mFalsingCollector,
+ mLockPatternUtils,
+ mBroadcastDispatcher,
+ () -> mStatusBarKeyguardViewManager,
+ mDismissCallbackRegistry,
+ mUpdateMonitor,
+ mDumpManager,
+ mUiBgExecutor,
+ mPowerManager,
+ mTrustManager,
+ mUserSwitcherController,
+ mDeviceConfig,
+ mNavigationModeController,
+ mKeyguardDisplayManager,
+ mDozeParameters,
+ mSysUiUnfoldComponentOptional,
+ mStatusBarStateController,
+ mKeyguardStateController,
+ () -> mKeyguardUnlockAnimationController,
+ mScreenOffAnimationController,
+ () -> mNotificationShadeDepthController,
+ mInteractionJankMonitor);
+ mViewMediator.start();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index 81bcbfb..d7c00fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -26,7 +26,6 @@
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -43,7 +42,6 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Pair;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -69,8 +67,6 @@
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
-import com.airbnb.lottie.LottieAnimationView;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -110,8 +106,6 @@
private @Mock ConfigurationController mConfigurationController;
private @Mock Vibrator mVibrator;
private @Mock AuthRippleController mAuthRippleController;
- private @Mock LottieAnimationView mAodFp;
- private @Mock LayoutInflater mLayoutInflater;
private FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock());
private LockIconViewController mLockIconViewController;
@@ -149,7 +143,6 @@
when(mLockIconView.getResources()).thenReturn(mResources);
when(mLockIconView.getContext()).thenReturn(mContext);
- when(mLockIconView.findViewById(R.layout.udfps_aod_lock_icon)).thenReturn(mAodFp);
when(mContext.getResources()).thenReturn(mResources);
when(mContext.getSystemService(WindowManager.class)).thenReturn(mWindowManager);
Rect windowBounds = new Rect(0, 0, 800, 1200);
@@ -176,8 +169,7 @@
mDelayableExecutor,
mVibrator,
mAuthRippleController,
- mResources,
- mLayoutInflater
+ mResources
);
}
@@ -187,35 +179,6 @@
}
@Test
- public void testIgnoreUdfpsWhenNotSupported() {
- // GIVEN Udpfs sensor is NOT available
- mLockIconViewController.init();
- captureAttachListener();
-
- // WHEN the view is attached
- mAttachListener.onViewAttachedToWindow(mLockIconView);
-
- // THEN lottie animation should NOT be inflated
- verify(mLayoutInflater, never()).inflate(eq(R.layout.udfps_aod_lock_icon), any());
- }
-
- @Test
- public void testInflateUdfpsWhenSupported() {
- // GIVEN Udpfs sensor is available
- setupUdfps();
- when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true);
-
- mLockIconViewController.init();
- captureAttachListener();
-
- // WHEN the view is attached
- mAttachListener.onViewAttachedToWindow(mLockIconView);
-
- // THEN lottie animation should be inflated
- verify(mLayoutInflater).inflate(eq(R.layout.udfps_aod_lock_icon), any());
- }
-
- @Test
public void testUpdateFingerprintLocationOnInit() {
// GIVEN fp sensor location is available pre-attached
Pair<Integer, PointF> udfps = setupUdfps();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index d0b957c..649ee87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -669,7 +669,7 @@
@Test
fun testPlaybackActions_noPrevNext_usesCustom() {
- val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
+ val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4", "custom 5")
whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true)
val stateActions = PlaybackState.ACTION_PLAY
val stateBuilder = PlaybackState.Builder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 4dac6d5..9f542f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -44,6 +44,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import org.junit.Before;
import org.junit.Test;
@@ -66,6 +67,7 @@
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+ private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl;
private MediaOutputController mMediaOutputController;
@@ -79,7 +81,7 @@
public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext,
mMediaOutputController);
mMediaOutputBaseDialogImpl.onCreate(new Bundle());
@@ -169,7 +171,7 @@
class MediaOutputBaseDialogImpl extends MediaOutputBaseDialog {
MediaOutputBaseDialogImpl(Context context, MediaOutputController mediaOutputController) {
- super(context, mediaOutputController);
+ super(context, mediaOutputController, mDialogManager);
mAdapter = mMediaOutputBaseAdapter;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index d71d98e..a84a803 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -55,6 +55,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import org.junit.Before;
import org.junit.Test;
@@ -94,6 +95,7 @@
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+ private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
private Context mSpyContext;
private MediaOutputController mMediaOutputController;
@@ -116,7 +118,7 @@
mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
MediaDescription.Builder builder = new MediaDescription.Builder();
@@ -160,7 +162,7 @@
public void start_withoutPackageName_verifyMediaControllerInit() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
mMediaOutputController.start(mCb);
@@ -181,7 +183,7 @@
public void stop_withoutPackageName_verifyMediaControllerDeinit() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
mMediaOutputController.start(mCb);
@@ -452,7 +454,7 @@
public void getNotificationLargeIcon_withoutPackageName_returnsNull() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
assertThat(mMediaOutputController.getNotificationIcon()).isNull();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index 8a3ea56..ada8d35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -40,6 +40,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import org.junit.After;
import org.junit.Before;
@@ -67,6 +68,7 @@
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+ private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
private MediaOutputDialog mMediaOutputDialog;
private MediaOutputController mMediaOutputController;
@@ -76,10 +78,10 @@
public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputDialog = new MediaOutputDialog(mContext, false,
- mMediaOutputController, mUiEventLogger);
+ mMediaOutputController, mUiEventLogger, mDialogManager);
mMediaOutputDialog.show();
when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice);
@@ -125,7 +127,7 @@
// and verify if the calling times increases.
public void onCreate_ShouldLogVisibility() {
MediaOutputDialog testDialog = new MediaOutputDialog(mContext, false,
- mMediaOutputController, mUiEventLogger);
+ mMediaOutputController, mUiEventLogger, mDialogManager);
testDialog.show();
testDialog.dismissDialog();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
index e8cd6c8..b114452 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
@@ -38,6 +38,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import org.junit.After;
import org.junit.Before;
@@ -66,6 +67,7 @@
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+ private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
private MediaOutputGroupDialog mMediaOutputGroupDialog;
private MediaOutputController mMediaOutputController;
@@ -75,10 +77,10 @@
public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputGroupDialog = new MediaOutputGroupDialog(mContext, false,
- mMediaOutputController);
+ mMediaOutputController, mDialogManager);
mMediaOutputGroupDialog.show();
when(mLocalMediaManager.getSelectedMediaDevice()).thenReturn(mMediaDevices);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
index 923f018..50d94dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
@@ -23,8 +23,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.commandline.Command
-import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -35,83 +33,58 @@
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-import java.io.PrintWriter
-import java.io.StringWriter
-import java.util.concurrent.Executor
@SmallTest
class MediaTttChipControllerTest : SysuiTestCase() {
private lateinit var mediaTttChipController: MediaTttChipController
- private val inlineExecutor = Executor { command -> command.run() }
- private val commandRegistry = CommandRegistry(context, inlineExecutor)
- private val pw = PrintWriter(StringWriter())
-
@Mock
private lateinit var windowManager: WindowManager
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- mediaTttChipController = MediaTttChipController(commandRegistry, context, windowManager)
- }
-
- @Test(expected = IllegalStateException::class)
- fun constructor_addCommmandAlreadyRegistered() {
- // Since creating the chip controller should automatically register the add command, it
- // should throw when registering it again.
- commandRegistry.registerCommand(
- MediaTttChipController.ADD_CHIP_COMMAND_TAG
- ) { EmptyCommand() }
- }
-
- @Test(expected = IllegalStateException::class)
- fun constructor_removeCommmandAlreadyRegistered() {
- // Since creating the chip controller should automatically register the remove command, it
- // should throw when registering it again.
- commandRegistry.registerCommand(
- MediaTttChipController.REMOVE_CHIP_COMMAND_TAG
- ) { EmptyCommand() }
+ mediaTttChipController = MediaTttChipController(context, windowManager)
}
@Test
- fun addChipCommand_chipAdded() {
- commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+ fun displayChip_chipAdded() {
+ mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
verify(windowManager).addView(any(), any())
}
@Test
- fun addChipCommand_twice_chipNotAddedTwice() {
- commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+ fun displayChip_twice_chipNotAddedTwice() {
+ mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
reset(windowManager)
- commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+ mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
verify(windowManager, never()).addView(any(), any())
}
@Test
- fun removeChipCommand_chipRemoved() {
+ fun removeChip_chipRemoved() {
// First, add the chip
- commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+ mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
// Then, remove it
- commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.REMOVE_CHIP_COMMAND_TAG))
+ mediaTttChipController.removeChip()
verify(windowManager).removeView(any())
}
@Test
- fun removeChipCommand_noAdd_viewNotRemoved() {
- commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.REMOVE_CHIP_COMMAND_TAG))
+ fun removeChip_noAdd_viewNotRemoved() {
+ mediaTttChipController.removeChip()
verify(windowManager, never()).removeView(any())
}
@Test
fun moveCloserToTransfer_chipTextContainsDeviceName_noLoadingIcon_noUndo() {
- commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+ mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
val chipView = getChipView()
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
@@ -121,7 +94,7 @@
@Test
fun transferInitiated_chipTextContainsDeviceName_loadingIcon_noUndo() {
- commandRegistry.onShellCommand(pw, getTransferInitiatedCommand())
+ mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
val chipView = getChipView()
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
@@ -131,7 +104,7 @@
@Test
fun transferSucceeded_chipTextContainsDeviceName_noLoadingIcon_undo() {
- commandRegistry.onShellCommand(pw, getTransferSucceededCommand())
+ mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME))
val chipView = getChipView()
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
@@ -141,57 +114,36 @@
@Test
fun changeFromCloserToTransferToTransferInitiated_loadingIconAppears() {
- commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
- commandRegistry.onShellCommand(pw, getTransferInitiatedCommand())
+ mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
+ mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
}
@Test
fun changeFromTransferInitiatedToTransferSucceeded_loadingIconDisappears() {
- commandRegistry.onShellCommand(pw, getTransferInitiatedCommand())
- commandRegistry.onShellCommand(pw, getTransferSucceededCommand())
+ mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
+ mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME))
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.GONE)
}
@Test
fun changeFromTransferInitiatedToTransferSucceeded_undoButtonAppears() {
- commandRegistry.onShellCommand(pw, getTransferInitiatedCommand())
- commandRegistry.onShellCommand(pw, getTransferSucceededCommand())
+ mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
+ mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME))
assertThat(getChipView().getUndoButtonVisibility()).isEqualTo(View.VISIBLE)
}
@Test
fun changeFromTransferSucceededToMoveCloser_undoButtonDisappears() {
- commandRegistry.onShellCommand(pw, getTransferSucceededCommand())
- commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+ mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME))
+ mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
assertThat(getChipView().getUndoButtonVisibility()).isEqualTo(View.GONE)
}
- private fun getMoveCloserToTransferCommand(): Array<String> =
- arrayOf(
- MediaTttChipController.ADD_CHIP_COMMAND_TAG,
- DEVICE_NAME,
- MediaTttChipController.ChipType.MOVE_CLOSER_TO_TRANSFER.name
- )
-
- private fun getTransferInitiatedCommand(): Array<String> =
- arrayOf(
- MediaTttChipController.ADD_CHIP_COMMAND_TAG,
- DEVICE_NAME,
- MediaTttChipController.ChipType.TRANSFER_INITIATED.name
- )
-
- private fun getTransferSucceededCommand(): Array<String> =
- arrayOf(
- MediaTttChipController.ADD_CHIP_COMMAND_TAG,
- DEVICE_NAME,
- MediaTttChipController.ChipType.TRANSFER_SUCCEEDED.name
- )
-
private fun LinearLayout.getChipText(): String =
(this.requireViewById<TextView>(R.id.text)).text as String
@@ -206,14 +158,6 @@
verify(windowManager).addView(viewCaptor.capture(), any())
return viewCaptor.value as LinearLayout
}
-
- class EmptyCommand : Command {
- override fun execute(pw: PrintWriter, args: List<String>) {
- }
-
- override fun help(pw: PrintWriter) {
- }
- }
}
private const val DEVICE_NAME = "My Tablet"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
new file mode 100644
index 0000000..4b85fa9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.media.taptotransfer
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.mockito.any
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.io.PrintWriter
+import java.io.StringWriter
+import java.util.concurrent.Executor
+
+@SmallTest
+class MediaTttCommandLineHelperTest : SysuiTestCase() {
+
+ private val inlineExecutor = Executor { command -> command.run() }
+ private val commandRegistry = CommandRegistry(context, inlineExecutor)
+ private val pw = PrintWriter(StringWriter())
+
+ private lateinit var mediaTttCommandLineHelper: MediaTttCommandLineHelper
+
+ @Mock
+ private lateinit var mediaTttChipController: MediaTttChipController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ mediaTttCommandLineHelper =
+ MediaTttCommandLineHelper(commandRegistry, mediaTttChipController)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun constructor_addCommandAlreadyRegistered() {
+ // Since creating the chip controller should automatically register the add command, it
+ // should throw when registering it again.
+ commandRegistry.registerCommand(
+ ADD_CHIP_COMMAND_TAG
+ ) { EmptyCommand() }
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun constructor_removeCommandAlreadyRegistered() {
+ // Since creating the chip controller should automatically register the remove command, it
+ // should throw when registering it again.
+ commandRegistry.registerCommand(
+ REMOVE_CHIP_COMMAND_TAG
+ ) { EmptyCommand() }
+ }
+
+ @Test
+ fun moveCloserToTransfer_chipDisplayWithCorrectState() {
+ commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+
+ verify(mediaTttChipController).displayChip(any(MoveCloserToTransfer::class.java))
+ }
+
+ @Test
+ fun transferInitiated_chipDisplayWithCorrectState() {
+ commandRegistry.onShellCommand(pw, getTransferInitiatedCommand())
+
+ verify(mediaTttChipController).displayChip(any(TransferInitiated::class.java))
+ }
+
+ @Test
+ fun transferSucceeded_chipDisplayWithCorrectState() {
+ commandRegistry.onShellCommand(pw, getTransferSucceededCommand())
+
+ verify(mediaTttChipController).displayChip(any(TransferSucceeded::class.java))
+ }
+
+ @Test
+ fun removeCommand_chipRemoved() {
+ commandRegistry.onShellCommand(pw, arrayOf(REMOVE_CHIP_COMMAND_TAG))
+
+ verify(mediaTttChipController).removeChip()
+ }
+
+ private fun getMoveCloserToTransferCommand(): Array<String> =
+ arrayOf(
+ ADD_CHIP_COMMAND_TAG,
+ DEVICE_NAME,
+ MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME
+ )
+
+ private fun getTransferInitiatedCommand(): Array<String> =
+ arrayOf(
+ ADD_CHIP_COMMAND_TAG,
+ DEVICE_NAME,
+ TRANSFER_INITIATED_COMMAND_NAME
+ )
+
+ private fun getTransferSucceededCommand(): Array<String> =
+ arrayOf(
+ ADD_CHIP_COMMAND_TAG,
+ DEVICE_NAME,
+ TRANSFER_SUCCEEDED_COMMAND_NAME
+ )
+
+ class EmptyCommand : Command {
+ override fun execute(pw: PrintWriter, args: List<String>) {
+ }
+
+ override fun help(pw: PrintWriter) {
+ }
+ }
+}
+
+private const val DEVICE_NAME = "My Tablet"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
new file mode 100644
index 0000000..af33daf
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
@@ -0,0 +1,216 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.qs.tileimpl
+
+import android.content.ComponentName
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.external.CustomTile
+import com.android.systemui.qs.tiles.AirplaneModeTile
+import com.android.systemui.qs.tiles.AlarmTile
+import com.android.systemui.qs.tiles.BatterySaverTile
+import com.android.systemui.qs.tiles.BluetoothTile
+import com.android.systemui.qs.tiles.CameraToggleTile
+import com.android.systemui.qs.tiles.CastTile
+import com.android.systemui.qs.tiles.CellularTile
+import com.android.systemui.qs.tiles.ColorInversionTile
+import com.android.systemui.qs.tiles.DataSaverTile
+import com.android.systemui.qs.tiles.DeviceControlsTile
+import com.android.systemui.qs.tiles.DndTile
+import com.android.systemui.qs.tiles.FgsManagerTile
+import com.android.systemui.qs.tiles.FlashlightTile
+import com.android.systemui.qs.tiles.HotspotTile
+import com.android.systemui.qs.tiles.InternetTile
+import com.android.systemui.qs.tiles.LocationTile
+import com.android.systemui.qs.tiles.MicrophoneToggleTile
+import com.android.systemui.qs.tiles.NfcTile
+import com.android.systemui.qs.tiles.NightDisplayTile
+import com.android.systemui.qs.tiles.OneHandedModeTile
+import com.android.systemui.qs.tiles.QRCodeScannerTile
+import com.android.systemui.qs.tiles.QuickAccessWalletTile
+import com.android.systemui.qs.tiles.ReduceBrightColorsTile
+import com.android.systemui.qs.tiles.RotationLockTile
+import com.android.systemui.qs.tiles.ScreenRecordTile
+import com.android.systemui.qs.tiles.UiModeNightTile
+import com.android.systemui.qs.tiles.UserTile
+import com.android.systemui.qs.tiles.WifiTile
+import com.android.systemui.qs.tiles.WorkModeTile
+import com.android.systemui.util.leak.GarbageMonitor
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Answers
+import org.mockito.Mock
+import org.mockito.Mockito.inOrder
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+private val specMap = mapOf(
+ "wifi" to WifiTile::class.java,
+ "internet" to InternetTile::class.java,
+ "bt" to BluetoothTile::class.java,
+ "cell" to CellularTile::class.java,
+ "dnd" to DndTile::class.java,
+ "inversion" to ColorInversionTile::class.java,
+ "airplane" to AirplaneModeTile::class.java,
+ "work" to WorkModeTile::class.java,
+ "rotation" to RotationLockTile::class.java,
+ "flashlight" to FlashlightTile::class.java,
+ "location" to LocationTile::class.java,
+ "cast" to CastTile::class.java,
+ "hotspot" to HotspotTile::class.java,
+ "user" to UserTile::class.java,
+ "battery" to BatterySaverTile::class.java,
+ "saver" to DataSaverTile::class.java,
+ "night" to NightDisplayTile::class.java,
+ "nfc" to NfcTile::class.java,
+ "dark" to UiModeNightTile::class.java,
+ "screenrecord" to ScreenRecordTile::class.java,
+ "reduce_brightness" to ReduceBrightColorsTile::class.java,
+ "cameratoggle" to CameraToggleTile::class.java,
+ "mictoggle" to MicrophoneToggleTile::class.java,
+ "controls" to DeviceControlsTile::class.java,
+ "alarm" to AlarmTile::class.java,
+ "wallet" to QuickAccessWalletTile::class.java,
+ "qr_code_scanner" to QRCodeScannerTile::class.java,
+ "onehanded" to OneHandedModeTile::class.java,
+ "fgsmanager" to FgsManagerTile::class.java
+)
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class QSFactoryImplTest : SysuiTestCase() {
+
+ @Mock private lateinit var qsHost: QSHost
+ @Mock(answer = Answers.RETURNS_SELF) private lateinit var customTileBuilder: CustomTile.Builder
+ @Mock private lateinit var customTile: CustomTile
+
+ @Mock private lateinit var wifiTile: WifiTile
+ @Mock private lateinit var internetTile: InternetTile
+ @Mock private lateinit var bluetoothTile: BluetoothTile
+ @Mock private lateinit var cellularTile: CellularTile
+ @Mock private lateinit var dndTile: DndTile
+ @Mock private lateinit var colorInversionTile: ColorInversionTile
+ @Mock private lateinit var airplaneTile: AirplaneModeTile
+ @Mock private lateinit var workTile: WorkModeTile
+ @Mock private lateinit var rotationTile: RotationLockTile
+ @Mock private lateinit var flashlightTile: FlashlightTile
+ @Mock private lateinit var locationTile: LocationTile
+ @Mock private lateinit var castTile: CastTile
+ @Mock private lateinit var hotspotTile: HotspotTile
+ @Mock private lateinit var userTile: UserTile
+ @Mock private lateinit var batterySaverTile: BatterySaverTile
+ @Mock private lateinit var dataSaverTile: DataSaverTile
+ @Mock private lateinit var nightDisplayTile: NightDisplayTile
+ @Mock private lateinit var nfcTile: NfcTile
+ @Mock private lateinit var memoryTile: GarbageMonitor.MemoryTile
+ @Mock private lateinit var darkModeTile: UiModeNightTile
+ @Mock private lateinit var screenRecordTile: ScreenRecordTile
+ @Mock private lateinit var reduceBrightColorsTile: ReduceBrightColorsTile
+ @Mock private lateinit var cameraToggleTile: CameraToggleTile
+ @Mock private lateinit var microphoneToggleTile: MicrophoneToggleTile
+ @Mock private lateinit var deviceControlsTile: DeviceControlsTile
+ @Mock private lateinit var alarmTile: AlarmTile
+ @Mock private lateinit var quickAccessWalletTile: QuickAccessWalletTile
+ @Mock private lateinit var qrCodeScannerTile: QRCodeScannerTile
+ @Mock private lateinit var oneHandedModeTile: OneHandedModeTile
+ @Mock private lateinit var fgsManagerTile: FgsManagerTile
+
+ private lateinit var factory: QSFactoryImpl
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ whenever(qsHost.context).thenReturn(mContext)
+ whenever(qsHost.userContext).thenReturn(mContext)
+ whenever(customTileBuilder.build()).thenReturn(customTile)
+
+ factory = QSFactoryImpl(
+ { qsHost },
+ { customTileBuilder },
+ { wifiTile },
+ { internetTile },
+ { bluetoothTile },
+ { cellularTile },
+ { dndTile },
+ { colorInversionTile },
+ { airplaneTile },
+ { workTile },
+ { rotationTile },
+ { flashlightTile },
+ { locationTile },
+ { castTile },
+ { hotspotTile },
+ { userTile },
+ { batterySaverTile },
+ { dataSaverTile },
+ { nightDisplayTile },
+ { nfcTile },
+ { memoryTile },
+ { darkModeTile },
+ { screenRecordTile },
+ { reduceBrightColorsTile },
+ { cameraToggleTile },
+ { microphoneToggleTile },
+ { deviceControlsTile },
+ { alarmTile },
+ { quickAccessWalletTile },
+ { qrCodeScannerTile },
+ { oneHandedModeTile },
+ { fgsManagerTile }
+ )
+ // When adding/removing tiles, fix also [specMap]
+ }
+
+ @Test
+ fun testCorrectTileClassStock() {
+ specMap.forEach { spec, klazz ->
+ assertThat(factory.createTile(spec)).isInstanceOf(klazz)
+ }
+ }
+
+ @Test
+ fun testCustomTileClass() {
+ val customSpec = CustomTile.toSpec(ComponentName("test", "test"))
+ assertThat(factory.createTile(customSpec)).isInstanceOf(CustomTile::class.java)
+ }
+
+ @Test
+ fun testBadTileNull() {
+ assertThat(factory.createTile("-432~")).isNull()
+ }
+
+ @Test
+ fun testTileInitializedAndStale() {
+ specMap.forEach { spec, _ ->
+ val tile = factory.createTile(spec) as QSTileImpl<*>
+ val inOrder = inOrder(tile)
+ inOrder.verify(tile).initialize()
+ inOrder.verify(tile).postStale()
+ }
+
+ val customSpec = CustomTile.toSpec(ComponentName("test", "test"))
+ val tile = factory.createTile(customSpec) as QSTileImpl<*>
+ val inOrder = inOrder(tile)
+ inOrder.verify(tile).initialize()
+ inOrder.verify(tile).postStale()
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index 6dca2a7..47f6e5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -235,10 +235,18 @@
}
@Test
- public void getSubtitleText_withAirplaneModeOn_returnNull() {
+ public void getSubtitleText_withApmOnAndWifiOff_returnWifiIsOff() {
fakeAirplaneModeEnabled(true);
+ when(mWifiManager.isWifiEnabled()).thenReturn(false);
- assertThat(mInternetDialogController.getSubtitleText(false)).isNull();
+ assertThat(mInternetDialogController.getSubtitleText(false))
+ .isEqualTo(getResourcesString("wifi_is_off"));
+
+ // if the Wi-Fi disallow config, then don't return Wi-Fi related string.
+ mInternetDialogController.mCanConfigWifi = false;
+
+ assertThat(mInternetDialogController.getSubtitleText(false))
+ .isNotEqualTo(getResourcesString("wifi_is_off"));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index 651bcde..8953788 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -140,7 +140,7 @@
mInternetDialog.updateDialog(true);
- assertThat(mSubTitle.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RunningFgsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RunningFgsControllerTest.java
new file mode 100644
index 0000000..c7c8d04
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RunningFgsControllerTest.java
@@ -0,0 +1,340 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar;
+
+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.Mockito.atLeastOnce;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.IActivityManager;
+import android.app.IForegroundServiceObserver;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.util.Pair;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.test.filters.MediumTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.RunningFgsController;
+import com.android.systemui.statusbar.policy.RunningFgsController.UserPackageTime;
+import com.android.systemui.statusbar.policy.RunningFgsControllerImpl;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Random;
+import java.util.function.Consumer;
+
+@MediumTest
+@RunWith(AndroidTestingRunner.class)
+public class RunningFgsControllerTest extends SysuiTestCase {
+
+ private RunningFgsController mController;
+
+ private FakeSystemClock mSystemClock = new FakeSystemClock();
+ private FakeExecutor mExecutor = new FakeExecutor(mSystemClock);
+ private TestCallback mCallback = new TestCallback();
+
+ @Mock
+ private IActivityManager mActivityManager;
+ @Mock
+ private Lifecycle mLifecycle;
+ @Mock
+ private LifecycleOwner mLifecycleOwner;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mLifecycleOwner.getLifecycle()).thenReturn(mLifecycle);
+ mController = new RunningFgsControllerImpl(mExecutor, mSystemClock, mActivityManager);
+ }
+
+ @Test
+ public void testInitRegistersListenerInImpl() throws RemoteException {
+ ((RunningFgsControllerImpl) mController).init();
+ verify(mActivityManager, times(1)).registerForegroundServiceObserver(any());
+ }
+
+ @Test
+ public void testAddCallbackCallsInitInImpl() {
+ verifyInitIsCalled(controller -> controller.addCallback(mCallback));
+ }
+
+ @Test
+ public void testRemoveCallbackCallsInitInImpl() {
+ verifyInitIsCalled(controller -> controller.removeCallback(mCallback));
+ }
+
+ @Test
+ public void testObserve1CallsInitInImpl() {
+ verifyInitIsCalled(controller -> controller.observe(mLifecycle, mCallback));
+ }
+
+ @Test
+ public void testObserve2CallsInitInImpl() {
+ verifyInitIsCalled(controller -> controller.observe(mLifecycleOwner, mCallback));
+ }
+
+ @Test
+ public void testGetPackagesWithFgsCallsInitInImpl() {
+ verifyInitIsCalled(controller -> controller.getPackagesWithFgs());
+ }
+
+ @Test
+ public void testStopFgsCallsInitInImpl() {
+ verifyInitIsCalled(controller -> controller.stopFgs(0, ""));
+ }
+
+ /**
+ * Tests that callbacks can be added
+ */
+ @Test
+ public void testAddCallback() throws RemoteException {
+ String testPackageName = "testPackageName";
+ int testUserId = 0;
+
+ IForegroundServiceObserver observer = prepareObserver();
+ mController.addCallback(mCallback);
+
+ observer.onForegroundStateChanged(new Binder(), testPackageName, testUserId, true);
+
+ mExecutor.advanceClockToLast();
+ mExecutor.runAllReady();
+
+ assertEquals("Callback should have been invoked exactly once.",
+ 1, mCallback.mInvocations.size());
+
+ List<UserPackageTime> userPackageTimes = mCallback.mInvocations.get(0);
+ assertEquals("There should have only been one package in callback. packages="
+ + userPackageTimes,
+ 1, userPackageTimes.size());
+
+ UserPackageTime upt = userPackageTimes.get(0);
+ assertEquals(testPackageName, upt.getPackageName());
+ assertEquals(testUserId, upt.getUserId());
+ }
+
+ /**
+ * Tests that callbacks can be removed. This test is only meaningful if
+ * {@link #testAddCallback()} can pass.
+ */
+ @Test
+ public void testRemoveCallback() throws RemoteException {
+ String testPackageName = "testPackageName";
+ int testUserId = 0;
+
+ IForegroundServiceObserver observer = prepareObserver();
+ mController.addCallback(mCallback);
+ mController.removeCallback(mCallback);
+
+ observer.onForegroundStateChanged(new Binder(), testPackageName, testUserId, true);
+
+ mExecutor.advanceClockToLast();
+ mExecutor.runAllReady();
+
+ assertEquals("Callback should not have been invoked.",
+ 0, mCallback.mInvocations.size());
+ }
+
+ /**
+ * Tests packages are added when the controller receives a callback from activity manager for
+ * a foreground service start.
+ */
+ @Test
+ public void testGetPackagesWithFgsAddingPackages() throws RemoteException {
+ int numPackages = 20;
+ int numUsers = 3;
+
+ IForegroundServiceObserver observer = prepareObserver();
+
+ assertEquals("List should be empty", 0, mController.getPackagesWithFgs().size());
+
+ List<Pair<Integer, String>> addedPackages = new ArrayList<>();
+ for (int pkgNumber = 0; pkgNumber < numPackages; pkgNumber++) {
+ for (int userId = 0; userId < numUsers; userId++) {
+ String packageName = "package.name." + pkgNumber;
+ addedPackages.add(new Pair(userId, packageName));
+
+ observer.onForegroundStateChanged(new Binder(), packageName, userId, true);
+
+ containsAllAddedPackages(addedPackages, mController.getPackagesWithFgs());
+ }
+ }
+ }
+
+ /**
+ * Tests packages are removed when the controller receives a callback from activity manager for
+ * a foreground service ending.
+ */
+ @Test
+ public void testGetPackagesWithFgsRemovingPackages() throws RemoteException {
+ int numPackages = 20;
+ int numUsers = 3;
+ int arrayLength = numPackages * numUsers;
+
+ String[] packages = new String[arrayLength];
+ int[] users = new int[arrayLength];
+ IBinder[] tokens = new IBinder[arrayLength];
+ for (int pkgNumber = 0; pkgNumber < numPackages; pkgNumber++) {
+ for (int userId = 0; userId < numUsers; userId++) {
+ int i = pkgNumber * numUsers + userId;
+ packages[i] = "package.name." + pkgNumber;
+ users[i] = userId;
+ tokens[i] = new Binder();
+ }
+ }
+
+ IForegroundServiceObserver observer = prepareObserver();
+
+ for (int i = 0; i < packages.length; i++) {
+ observer.onForegroundStateChanged(tokens[i], packages[i], users[i], true);
+ }
+
+ assertEquals(packages.length, mController.getPackagesWithFgs().size());
+
+ List<Integer> removeOrder = new ArrayList<>();
+ for (int i = 0; i < packages.length; i++) {
+ removeOrder.add(i);
+ }
+ Collections.shuffle(removeOrder, new Random(12345));
+
+ for (int idx : removeOrder) {
+ removePackageAndAssertRemovedFromList(observer, tokens[idx], packages[idx], users[idx]);
+ }
+
+ assertEquals(0, mController.getPackagesWithFgs().size());
+ }
+
+ /**
+ * Tests a call on stopFgs forwards to activity manager.
+ */
+ @Test
+ public void testStopFgs() throws RemoteException {
+ String pkgName = "package.name";
+ mController.stopFgs(0, pkgName);
+ verify(mActivityManager).makeServicesNonForeground(pkgName, 0);
+ }
+
+ /**
+ * Tests a package which starts multiple services is only listed once and is only removed once
+ * all services are stopped.
+ */
+ @Test
+ public void testSinglePackageWithMultipleServices() throws RemoteException {
+ String packageName = "package.name";
+ int userId = 0;
+ IBinder serviceToken1 = new Binder();
+ IBinder serviceToken2 = new Binder();
+
+ IForegroundServiceObserver observer = prepareObserver();
+
+ assertEquals(0, mController.getPackagesWithFgs().size());
+
+ observer.onForegroundStateChanged(serviceToken1, packageName, userId, true);
+ assertSinglePackage(packageName, userId);
+
+ observer.onForegroundStateChanged(serviceToken2, packageName, userId, true);
+ assertSinglePackage(packageName, userId);
+
+ observer.onForegroundStateChanged(serviceToken2, packageName, userId, false);
+ assertSinglePackage(packageName, userId);
+
+ observer.onForegroundStateChanged(serviceToken1, packageName, userId, false);
+ assertEquals(0, mController.getPackagesWithFgs().size());
+ }
+
+ private IForegroundServiceObserver prepareObserver()
+ throws RemoteException {
+ mController.getPackagesWithFgs();
+
+ ArgumentCaptor<IForegroundServiceObserver> argumentCaptor =
+ ArgumentCaptor.forClass(IForegroundServiceObserver.class);
+ verify(mActivityManager).registerForegroundServiceObserver(argumentCaptor.capture());
+
+ return argumentCaptor.getValue();
+ }
+
+ private void verifyInitIsCalled(Consumer<RunningFgsControllerImpl> c) {
+ RunningFgsControllerImpl spiedController = Mockito.spy(
+ ((RunningFgsControllerImpl) mController));
+ c.accept(spiedController);
+ verify(spiedController, atLeastOnce()).init();
+ }
+
+ private void containsAllAddedPackages(List<Pair<Integer, String>> addedPackages,
+ List<UserPackageTime> runningFgsPackages) {
+ for (Pair<Integer, String> userPkg : addedPackages) {
+ assertTrue(userPkg + " was not found in returned list",
+ runningFgsPackages.stream().anyMatch(
+ upt -> userPkg.first == upt.getUserId()
+ && Objects.equals(upt.getPackageName(), userPkg.second)));
+ }
+ for (UserPackageTime upt : runningFgsPackages) {
+ int userId = upt.getUserId();
+ String packageName = upt.getPackageName();
+ assertTrue("Unknown <user=" + userId + ", package=" + packageName + ">"
+ + " in returned list",
+ addedPackages.stream().anyMatch(userPkg -> userPkg.first == userId
+ && Objects.equals(packageName, userPkg.second)));
+ }
+ }
+
+ private void removePackageAndAssertRemovedFromList(IForegroundServiceObserver observer,
+ IBinder token, String pkg, int userId) throws RemoteException {
+ observer.onForegroundStateChanged(token, pkg, userId, false);
+ List<UserPackageTime> packagesWithFgs = mController.getPackagesWithFgs();
+ assertFalse("Package \"" + pkg + "\" was not removed",
+ packagesWithFgs.stream().anyMatch(upt ->
+ Objects.equals(upt.getPackageName(), pkg) && upt.getUserId() == userId));
+ }
+
+ private void assertSinglePackage(String packageName, int userId) {
+ assertEquals(1, mController.getPackagesWithFgs().size());
+ assertEquals(packageName, mController.getPackagesWithFgs().get(0).getPackageName());
+ assertEquals(userId, mController.getPackagesWithFgs().get(0).getUserId());
+ }
+
+ private static class TestCallback implements RunningFgsController.Callback {
+
+ private List<List<UserPackageTime>> mInvocations = new ArrayList<>();
+
+ @Override
+ public void onFgsPackagesChanged(List<UserPackageTime> packages) {
+ mInvocations.add(packages);
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index f62de51..dc83c0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -75,6 +75,7 @@
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreMocksKt;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
@@ -201,6 +202,7 @@
mLeakDetector,
mock(ForegroundServiceDismissalFeatureController.class),
mock(IStatusBarService.class),
+ NotifLiveDataStoreMocksKt.createNotifLiveDataStoreImplMock(),
mock(DumpManager.class)
);
mEntryManager.initialize(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index b02a336..ed8b532 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -37,6 +37,7 @@
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import androidx.annotation.NonNull;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
@@ -50,6 +51,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.provider.DebugModeFilterProvider;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -78,6 +80,8 @@
mock(StatusBarNotification.class);
@Mock
+ DebugModeFilterProvider mDebugModeFilterProvider;
+ @Mock
StatusBarStateController mStatusBarStateController;
@Mock
KeyguardEnvironment mEnvironment;
@@ -132,7 +136,13 @@
mDependency,
TestableLooper.get(this));
mRow = testHelper.createRow();
- mNotificationFilter = new NotificationFilter(
+ mNotificationFilter = newNotificationFilter();
+ }
+
+ @NonNull
+ private NotificationFilter newNotificationFilter() {
+ return new NotificationFilter(
+ mDebugModeFilterProvider,
mStatusBarStateController,
mEnvironment,
mFsc,
@@ -205,12 +215,7 @@
public void shouldFilterOtherNotificationWhenDisabled() {
// GIVEN that the media feature is disabled
when(mMediaFeatureFlag.getEnabled()).thenReturn(false);
- NotificationFilter filter = new NotificationFilter(
- mStatusBarStateController,
- mEnvironment,
- mFsc,
- mUserManager,
- mMediaFeatureFlag);
+ NotificationFilter filter = newNotificationFilter();
// WHEN the media filter is asked about an entry
NotificationEntry otherEntry = new NotificationEntryBuilder().build();
final boolean shouldFilter = filter.shouldFilterOut(otherEntry);
@@ -222,12 +227,7 @@
public void shouldFilterOtherNotificationWhenEnabled() {
// GIVEN that the media feature is enabled
when(mMediaFeatureFlag.getEnabled()).thenReturn(true);
- NotificationFilter filter = new NotificationFilter(
- mStatusBarStateController,
- mEnvironment,
- mFsc,
- mUserManager,
- mMediaFeatureFlag);
+ NotificationFilter filter = newNotificationFilter();
// WHEN the media filter is asked about an entry
NotificationEntry otherEntry = new NotificationEntryBuilder().build();
final boolean shouldFilter = filter.shouldFilterOut(otherEntry);
@@ -239,12 +239,7 @@
public void shouldFilterMediaNotificationWhenDisabled() {
// GIVEN that the media feature is disabled
when(mMediaFeatureFlag.getEnabled()).thenReturn(false);
- NotificationFilter filter = new NotificationFilter(
- mStatusBarStateController,
- mEnvironment,
- mFsc,
- mUserManager,
- mMediaFeatureFlag);
+ NotificationFilter filter = newNotificationFilter();
// WHEN the media filter is asked about a media entry
final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry);
// THEN it shouldn't be filtered
@@ -255,12 +250,7 @@
public void shouldFilterMediaNotificationWhenEnabled() {
// GIVEN that the media feature is enabled
when(mMediaFeatureFlag.getEnabled()).thenReturn(true);
- NotificationFilter filter = new NotificationFilter(
- mStatusBarStateController,
- mEnvironment,
- mFsc,
- mUserManager,
- mMediaFeatureFlag);
+ NotificationFilter filter = newNotificationFilter();
// WHEN the media filter is asked about a media entry
final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry);
// THEN it should be filtered
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
index 2971c05..b45d78d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
@@ -18,6 +18,8 @@
import androidx.annotation.Nullable;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
+
import java.util.ArrayList;
import java.util.List;
@@ -28,6 +30,7 @@
private String mKey = "test_group_key";
private long mCreationTime = 0;
@Nullable private GroupEntry mParent = GroupEntry.ROOT_ENTRY;
+ private NotifSection mNotifSection;
private NotificationEntry mSummary = null;
private List<NotificationEntry> mChildren = new ArrayList<>();
@@ -35,6 +38,7 @@
public GroupEntry build() {
GroupEntry ge = new GroupEntry(mKey, mCreationTime);
ge.setParent(mParent);
+ ge.getAttachState().setSection(mNotifSection);
ge.setSummary(mSummary);
mSummary.setParent(ge);
@@ -61,6 +65,11 @@
return this;
}
+ public GroupEntryBuilder setSection(@Nullable NotifSection section) {
+ mNotifSection = section;
+ return this;
+ }
+
public GroupEntryBuilder setSummary(
NotificationEntry summary) {
mSummary = summary;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt
new file mode 100644
index 0000000..892575a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.lifecycle.Observer
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotifLiveDataImplTest : SysuiTestCase() {
+
+ private val executor = FakeExecutor(FakeSystemClock())
+ private val liveDataImpl: NotifLiveDataImpl<Int> = NotifLiveDataImpl("tst", 9, executor)
+ private val syncObserver: Observer<Int> = mock()
+ private val asyncObserver: Observer<Int> = mock()
+
+ @Before
+ fun setup() {
+ allowTestableLooperAsMainThread()
+ liveDataImpl.addSyncObserver(syncObserver)
+ liveDataImpl.addAsyncObserver(asyncObserver)
+ }
+
+ @Test
+ fun testGetInitialValue() {
+ assertThat(liveDataImpl.value).isEqualTo(9)
+ }
+
+ @Test
+ fun testGetModifiedValue() {
+ liveDataImpl.value = 13
+ assertThat(liveDataImpl.value).isEqualTo(13)
+ }
+
+ @Test
+ fun testGetsModifiedValueFromWithinSyncObserver() {
+ liveDataImpl.addSyncObserver { intVal ->
+ assertThat(intVal).isEqualTo(13)
+ assertThat(liveDataImpl.value).isEqualTo(13)
+ }
+ liveDataImpl.value = 13
+ }
+
+ @Test
+ fun testDoesNotAlertsRemovedObservers() {
+ liveDataImpl.removeObserver(syncObserver)
+ liveDataImpl.removeObserver(asyncObserver)
+
+ liveDataImpl.value = 13
+
+ // There should be no runnables on the executor
+ assertThat(executor.runAllReady()).isEqualTo(0)
+
+ // And observers should not be called
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+ }
+
+ @Test
+ fun testDoesNotAsyncObserversRemovedSinceChange() {
+ liveDataImpl.value = 13
+ liveDataImpl.removeObserver(asyncObserver)
+
+ // There should be a runnable that will get executed...
+ assertThat(executor.runAllReady()).isEqualTo(1)
+
+ // ...but async observers should not be called
+ verifyNoMoreInteractions(asyncObserver)
+ }
+
+ @Test
+ fun testAlertsObservers() {
+ liveDataImpl.value = 13
+
+ // Verify that the synchronous observer is called immediately
+ verify(syncObserver).onChanged(eq(13))
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+
+ // Verify that the asynchronous observer is called when the executor runs
+ assertThat(executor.runAllReady()).isEqualTo(1)
+ verify(asyncObserver).onChanged(eq(13))
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+ }
+
+ @Test
+ fun testAlertsObserversFromDispatcher() {
+ // GIVEN that we use setValueAndProvideDispatcher()
+ val dispatcher = liveDataImpl.setValueAndProvideDispatcher(13)
+
+ // VERIFY that nothing is done before the dispatcher is called
+ assertThat(executor.numPending()).isEqualTo(0)
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+
+ // WHEN the dispatcher is invoked...
+ dispatcher.invoke()
+
+ // Verify that the synchronous observer is called immediately
+ verify(syncObserver).onChanged(eq(13))
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+
+ // Verify that the asynchronous observer is called when the executor runs
+ assertThat(executor.runAllReady()).isEqualTo(1)
+ verify(asyncObserver).onChanged(eq(13))
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+ }
+
+ @Test
+ fun testSkipsAllObserversIfValueDidNotChange() {
+ liveDataImpl.value = 9
+ // Does not add a runnable
+ assertThat(executor.runAllReady()).isEqualTo(0)
+ // Setting the current value does not call synchronous observers
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+ }
+
+ @Test
+ fun testSkipsAsyncObserversWhenValueTogglesBack() {
+ liveDataImpl.value = 13
+ liveDataImpl.value = 11
+ liveDataImpl.value = 9
+
+ // Synchronous observers will receive every change event immediately
+ inOrder(syncObserver).apply {
+ verify(syncObserver).onChanged(eq(13))
+ verify(syncObserver).onChanged(eq(11))
+ verify(syncObserver).onChanged(eq(9))
+ }
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+
+ // Running the first runnable on the queue will just emit the most recent value
+ assertThat(executor.runNextReady()).isTrue()
+ verify(asyncObserver).onChanged(eq(9))
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+
+ // Running the next 2 runnable will have no effect
+ assertThat(executor.runAllReady()).isEqualTo(2)
+ verifyNoMoreInteractions(syncObserver, asyncObserver)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt
new file mode 100644
index 0000000..9c8ac5c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.lang.UnsupportedOperationException
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotifLiveDataStoreImplTest : SysuiTestCase() {
+
+ private val executor = FakeExecutor(FakeSystemClock())
+ private val liveDataStoreImpl = NotifLiveDataStoreImpl(executor)
+
+ @Before
+ fun setup() {
+ allowTestableLooperAsMainThread()
+ }
+
+ @Test
+ fun testAllObserversSeeConsistentValues() {
+ val entry1 = NotificationEntryBuilder().setId(1).build()
+ val entry2 = NotificationEntryBuilder().setId(2).build()
+ val observer: (Any) -> Unit = {
+ assertThat(liveDataStoreImpl.hasActiveNotifs.value).isEqualTo(true)
+ assertThat(liveDataStoreImpl.activeNotifCount.value).isEqualTo(2)
+ assertThat(liveDataStoreImpl.activeNotifList.value).isEqualTo(listOf(entry1, entry2))
+ }
+ liveDataStoreImpl.hasActiveNotifs.addSyncObserver(observer)
+ liveDataStoreImpl.hasActiveNotifs.addAsyncObserver(observer)
+ liveDataStoreImpl.activeNotifCount.addSyncObserver(observer)
+ liveDataStoreImpl.activeNotifCount.addAsyncObserver(observer)
+ liveDataStoreImpl.activeNotifList.addSyncObserver(observer)
+ liveDataStoreImpl.activeNotifList.addAsyncObserver(observer)
+ liveDataStoreImpl.setActiveNotifList(listOf(entry1, entry2))
+ executor.runAllReady()
+ }
+
+ @Test
+ fun testOriginalListIsCopied() {
+ val entry1 = NotificationEntryBuilder().setId(1).build()
+ val entry2 = NotificationEntryBuilder().setId(2).build()
+ val mutableInputList = mutableListOf(entry1, entry2)
+ val observer: (Any) -> Unit = {
+ mutableInputList.clear()
+ assertThat(liveDataStoreImpl.hasActiveNotifs.value).isEqualTo(true)
+ assertThat(liveDataStoreImpl.activeNotifCount.value).isEqualTo(2)
+ assertThat(liveDataStoreImpl.activeNotifList.value).isEqualTo(listOf(entry1, entry2))
+ }
+ liveDataStoreImpl.hasActiveNotifs.addSyncObserver(observer)
+ liveDataStoreImpl.hasActiveNotifs.addAsyncObserver(observer)
+ liveDataStoreImpl.activeNotifCount.addSyncObserver(observer)
+ liveDataStoreImpl.activeNotifCount.addAsyncObserver(observer)
+ liveDataStoreImpl.activeNotifList.addSyncObserver(observer)
+ liveDataStoreImpl.activeNotifList.addAsyncObserver(observer)
+ liveDataStoreImpl.setActiveNotifList(mutableInputList)
+ executor.runAllReady()
+ }
+
+ @Test
+ fun testProvidedListIsUnmodifiable() {
+ val entry1 = NotificationEntryBuilder().setId(1).build()
+ val entry2 = NotificationEntryBuilder().setId(2).build()
+ val observer: (List<NotificationEntry>) -> Unit = { providedValue ->
+ val provided = providedValue as MutableList<NotificationEntry>
+ Assert.assertThrows(UnsupportedOperationException::class.java) {
+ provided.clear()
+ }
+ val current = liveDataStoreImpl.activeNotifList.value as MutableList<NotificationEntry>
+ Assert.assertThrows(UnsupportedOperationException::class.java) {
+ current.clear()
+ }
+ assertThat(liveDataStoreImpl.activeNotifList.value).isEqualTo(listOf(entry1, entry2))
+ }
+ liveDataStoreImpl.activeNotifList.addSyncObserver(observer)
+ liveDataStoreImpl.activeNotifList.addAsyncObserver(observer)
+ liveDataStoreImpl.setActiveNotifList(mutableListOf(entry1, entry2))
+ executor.runAllReady()
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreMocks.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreMocks.kt
new file mode 100644
index 0000000..6e81c69
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreMocks.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import com.android.systemui.util.mockito.mock
+import org.mockito.Mockito.`when` as whenever
+
+/** Creates a mock which returns mocks for the NotifLiveDataImpl fields. */
+fun createNotifLiveDataStoreImplMock(): NotifLiveDataStoreImpl {
+ val dataStoreImpl: NotifLiveDataStoreImpl = mock()
+ whenever(dataStoreImpl.hasActiveNotifs).thenReturn(mock())
+ whenever(dataStoreImpl.activeNotifCount).thenReturn(mock())
+ whenever(dataStoreImpl.activeNotifList).thenReturn(mock())
+ return dataStoreImpl
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineTest.kt
deleted file mode 100644
index 287f50c..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineTest.kt
+++ /dev/null
@@ -1,83 +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.systemui.statusbar.notification.collection
-
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.notification.collection.render.RenderStageManager
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.`when` as whenever
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class NotifPipelineTest : SysuiTestCase() {
-
- @Mock private lateinit var notifCollection: NotifCollection
- @Mock private lateinit var shadeListBuilder: ShadeListBuilder
- @Mock private lateinit var renderStageManager: RenderStageManager
- private lateinit var notifPipeline: NotifPipeline
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- notifPipeline = NotifPipeline(notifCollection, shadeListBuilder, renderStageManager)
- whenever(shadeListBuilder.shadeList).thenReturn(listOf(
- NotificationEntryBuilder().setPkg("foo").setId(1).build(),
- NotificationEntryBuilder().setPkg("foo").setId(2).build(),
- group(
- NotificationEntryBuilder().setPkg("bar").setId(1).build(),
- NotificationEntryBuilder().setPkg("bar").setId(2).build(),
- NotificationEntryBuilder().setPkg("bar").setId(3).build(),
- NotificationEntryBuilder().setPkg("bar").setId(4).build()
- ),
- NotificationEntryBuilder().setPkg("baz").setId(1).build()
- ))
- }
-
- private fun group(summary: NotificationEntry, vararg children: NotificationEntry): GroupEntry {
- return GroupEntry(summary.key, summary.creationTime).also { group ->
- group.summary = summary
- for (it in children) {
- group.addChild(it)
- }
- }
- }
-
- @Test
- fun testGetShadeListCount() {
- assertThat(notifPipeline.getShadeListCount()).isEqualTo(7)
- }
-
- @Test
- fun testGetFlatShadeList() {
- assertThat(notifPipeline.getFlatShadeList().map { it.key }).containsExactly(
- "0|foo|1|null|0",
- "0|foo|2|null|0",
- "0|bar|1|null|0",
- "0|bar|2|null|0",
- "0|bar|3|null|0",
- "0|bar|4|null|0",
- "0|baz|1|null|0"
- ).inOrder()
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt
new file mode 100644
index 0000000..59fc591
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
+import com.android.systemui.statusbar.notification.collection.render.NotifStackController
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations.initMocks
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class DataStoreCoordinatorTest : SysuiTestCase() {
+ private lateinit var coordinator: DataStoreCoordinator
+ private lateinit var afterRenderListListener: OnAfterRenderListListener
+
+ private lateinit var entry: NotificationEntry
+
+ @Mock private lateinit var pipeline: NotifPipeline
+ @Mock private lateinit var notifLiveDataStoreImpl: NotifLiveDataStoreImpl
+ @Mock private lateinit var stackController: NotifStackController
+ @Mock private lateinit var section: NotifSection
+
+ @Before
+ fun setUp() {
+ initMocks(this)
+ entry = NotificationEntryBuilder().setSection(section).build()
+ coordinator = DataStoreCoordinator(notifLiveDataStoreImpl)
+ coordinator.attach(pipeline)
+ afterRenderListListener = withArgCaptor {
+ verify(pipeline).addOnAfterRenderListListener(capture())
+ }
+ }
+
+ @Test
+ fun testUpdateDataStore_withOneEntry() {
+ afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
+ verify(notifLiveDataStoreImpl).setActiveNotifList(eq(listOf(entry)))
+ verifyNoMoreInteractions(notifLiveDataStoreImpl)
+ }
+
+ @Test
+ fun testUpdateDataStore_withGroups() {
+ afterRenderListListener.onAfterRenderList(
+ listOf(
+ notificationEntry("foo", 1),
+ notificationEntry("foo", 2),
+ GroupEntryBuilder().setSummary(
+ notificationEntry("bar", 1)
+ ).setChildren(
+ listOf(
+ notificationEntry("bar", 2),
+ notificationEntry("bar", 3),
+ notificationEntry("bar", 4)
+ )
+ ).setSection(section).build(),
+ notificationEntry("baz", 1)
+ ),
+ stackController
+ )
+ val list: List<NotificationEntry> = withArgCaptor {
+ verify(notifLiveDataStoreImpl).setActiveNotifList(capture())
+ }
+ assertThat(list.map { it.key }).containsExactly(
+ "0|foo|1|null|0",
+ "0|foo|2|null|0",
+ "0|bar|1|null|0",
+ "0|bar|2|null|0",
+ "0|bar|3|null|0",
+ "0|bar|4|null|0",
+ "0|baz|1|null|0"
+ ).inOrder()
+ verifyNoMoreInteractions(notifLiveDataStoreImpl)
+ }
+
+ private fun notificationEntry(pkg: String, id: Int) =
+ NotificationEntryBuilder().setPkg(pkg).setId(id).setSection(section).build()
+
+ @Test
+ fun testUpdateDataStore_withZeroEntries_whenNewPipelineEnabled() {
+ afterRenderListListener.onAfterRenderList(listOf(), stackController)
+ verify(notifLiveDataStoreImpl).setActiveNotifList(eq(listOf()))
+ verifyNoMoreInteractions(notifLiveDataStoreImpl)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java
index 395aec3..2dfb9fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java
@@ -46,6 +46,8 @@
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifLiveData;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -65,6 +67,7 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
@@ -82,6 +85,8 @@
// Dependency mocks:
@Mock private NotifPipelineFlags mNotifPipelineFlags;
+ @Mock private NotifLiveDataStore mNotifLiveDataStore;
+ @Mock private NotifLiveData<List<NotificationEntry>> mActiveNotifList;
@Mock private NotificationVisibilityProvider mVisibilityProvider;
@Mock private NotificationEntryManager mEntryManager;
@Mock private NotifPipeline mNotifPipeline;
@@ -97,6 +102,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mNotifLiveDataStore.getActiveNotifList()).thenReturn(mActiveNotifList);
mEntry = new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_NAME)
@@ -112,6 +118,7 @@
mListener,
mUiBgExecutor,
mNotifPipelineFlags,
+ mNotifLiveDataStore,
mVisibilityProvider,
mEntryManager,
mNotifPipeline,
@@ -145,7 +152,7 @@
any(NotificationVisibility[].class));
when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
- when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(mEntry));
mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
mUiBgExecutor.runAllReady();
@@ -167,7 +174,7 @@
public void testStoppingNotificationLoggingReportsCurrentNotifications()
throws Exception {
when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
- when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(mEntry));
mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
mUiBgExecutor.runAllReady();
@@ -196,7 +203,7 @@
@Test
public void testLogPanelShownOnWake() {
- when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(mEntry));
setStateAsleep();
mLogger.onDozingChanged(false); // Wake to lockscreen
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
@@ -212,7 +219,7 @@
@Test
public void testLogPanelShownOnShadePull() {
- when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(mEntry));
setStateAwake();
// Now expand panel
mLogger.onPanelExpandedChanged(true);
@@ -240,7 +247,7 @@
.build();
entry.setRow(mRow);
- when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(entry));
+ when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(entry));
setStateAsleep();
mLogger.onDozingChanged(false); // Wake to lockscreen
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
@@ -254,6 +261,7 @@
TestableNotificationLogger(NotificationListener notificationListener,
Executor uiBgExecutor,
NotifPipelineFlags notifPipelineFlags,
+ NotifLiveDataStore notifLiveDataStore,
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
@@ -264,6 +272,7 @@
notificationListener,
uiBgExecutor,
notifPipelineFlags,
+ notifLiveDataStore,
visibilityProvider,
entryManager,
notifPipeline,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 3a9b297..4d861f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -46,6 +46,8 @@
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifLiveData;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -65,6 +67,7 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
@@ -82,6 +85,8 @@
// Dependency mocks:
@Mock private NotifPipelineFlags mNotifPipelineFlags;
+ @Mock private NotifLiveDataStore mNotifLiveDataStore;
+ @Mock private NotifLiveData<List<NotificationEntry>> mActiveNotifEntries;
@Mock private NotificationVisibilityProvider mVisibilityProvider;
@Mock private NotificationEntryManager mEntryManager;
@Mock private NotifPipeline mNotifPipeline;
@@ -98,6 +103,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(true);
+ when(mNotifLiveDataStore.getActiveNotifList()).thenReturn(mActiveNotifEntries);
mEntry = new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_NAME)
@@ -113,6 +119,7 @@
mListener,
mUiBgExecutor,
mNotifPipelineFlags,
+ mNotifLiveDataStore,
mVisibilityProvider,
mEntryManager,
mNotifPipeline,
@@ -146,7 +153,7 @@
any(NotificationVisibility[].class));
when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
- when(mNotifPipeline.getFlatShadeList()).thenReturn(Lists.newArrayList(mEntry));
+ when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(mEntry));
mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
mUiBgExecutor.runAllReady();
@@ -168,7 +175,7 @@
public void testStoppingNotificationLoggingReportsCurrentNotifications()
throws Exception {
when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
- when(mNotifPipeline.getFlatShadeList()).thenReturn(Lists.newArrayList(mEntry));
+ when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(mEntry));
mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
mUiBgExecutor.runAllReady();
@@ -197,7 +204,7 @@
@Test
public void testLogPanelShownOnWake() {
- when(mNotifPipeline.getFlatShadeList()).thenReturn(Lists.newArrayList(mEntry));
+ when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(mEntry));
setStateAsleep();
mLogger.onDozingChanged(false); // Wake to lockscreen
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
@@ -213,7 +220,7 @@
@Test
public void testLogPanelShownOnShadePull() {
- when(mNotifPipeline.getFlatShadeList()).thenReturn(Lists.newArrayList(mEntry));
+ when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(mEntry));
setStateAwake();
// Now expand panel
mLogger.onPanelExpandedChanged(true);
@@ -241,7 +248,7 @@
.build();
entry.setRow(mRow);
- when(mNotifPipeline.getFlatShadeList()).thenReturn(Lists.newArrayList(entry));
+ when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(entry));
setStateAsleep();
mLogger.onDozingChanged(false); // Wake to lockscreen
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
@@ -255,6 +262,7 @@
TestableNotificationLogger(NotificationListener notificationListener,
Executor uiBgExecutor,
NotifPipelineFlags notifPipelineFlags,
+ NotifLiveDataStore notifLiveDataStore,
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
@@ -265,6 +273,7 @@
notificationListener,
uiBgExecutor,
notifPipelineFlags,
+ notifLiveDataStore,
visibilityProvider,
entryManager,
notifPipeline,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index eeda9dd..a890414 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -68,6 +68,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreMocksKt;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
@@ -191,6 +192,7 @@
mLeakDetector,
mock(ForegroundServiceDismissalFeatureController.class),
mock(IStatusBarService.class),
+ NotifLiveDataStoreMocksKt.createNotifLiveDataStoreImplMock(),
mock(DumpManager.class)
);
mEntryManager.initialize(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 5b60c9e..ea68143 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -4,6 +4,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.EmptyShadeView
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.BypassController
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider
@@ -55,4 +56,24 @@
// top margin presence should decrease heads up translation up to minHeadsUpTranslation
assertThat(expandableViewState.yTranslation).isEqualTo(minHeadsUpTranslation)
}
-}
\ No newline at end of file
+
+ @Test
+ fun resetViewStates_childIsEmptyShadeView_viewIsCenteredVertically() {
+ stackScrollAlgorithm.initView(context)
+ val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply {
+ layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
+ }
+ hostView.removeAllViews()
+ hostView.addView(emptyShadeView)
+ ambientState.layoutMaxHeight = 1280
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ val closeHandleUnderlapHeight =
+ context.resources.getDimensionPixelSize(R.dimen.close_handle_underlap)
+ val fullHeight =
+ ambientState.layoutMaxHeight + closeHandleUnderlapHeight - ambientState.stackY
+ val centeredY = ambientState.stackY + fullHeight / 2f - emptyShadeView.height / 2f
+ assertThat(emptyShadeView.viewState?.yTranslation).isEqualTo(centeredY)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 07debe6..c3349f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -381,16 +381,15 @@
}
@Test
- public void onUdfpsConsecutivelyFailedThreeTimes_showBouncer() {
+ public void onUdfpsConsecutivelyFailedTwoTimes_showBouncer() {
// GIVEN UDFPS is supported
when(mUpdateMonitor.isUdfpsSupported()).thenReturn(true);
- // WHEN udfps fails twice - then don't show the bouncer
- mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+ // WHEN udfps fails once - then don't show the bouncer
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
- // WHEN udfps fails the third time
+ // WHEN udfps fails the second time
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
// THEN show the bouncer
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
index e386263..9664035 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
@@ -16,14 +16,12 @@
package com.android.systemui.statusbar.phone;
-import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -34,13 +32,13 @@
import android.view.ViewPropertyAnimator;
import android.view.WindowManager;
+import androidx.lifecycle.Observer;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotifLiveData;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import org.junit.Before;
import org.junit.Test;
@@ -59,18 +57,19 @@
private static final int LIGHTS_ON = 0;
private static final int LIGHTS_OUT = APPEARANCE_LOW_PROFILE_BARS;
- @Mock private NotificationEntryManager mEntryManager;
+ @Mock private NotifLiveData<Boolean> mHasActiveNotifs;
+ @Mock private NotifLiveDataStore mNotifLiveDataStore;
@Mock private CommandQueue mCommandQueue;
@Mock private WindowManager mWindowManager;
@Mock private Display mDisplay;
- @Captor private ArgumentCaptor<NotificationEntryListener> mListenerCaptor;
+ @Captor private ArgumentCaptor<Observer<Boolean>> mObserverCaptor;
@Captor private ArgumentCaptor<CommandQueue.Callbacks> mCallbacksCaptor;
private View mLightsOutView;
private LightsOutNotifController mLightsOutNotifController;
private int mDisplayId;
- private NotificationEntryListener mEntryListener;
+ private Observer<Boolean> mHaActiveNotifsObserver;
private CommandQueue.Callbacks mCallbacks;
@Before
@@ -80,15 +79,20 @@
mLightsOutView = new View(mContext);
when(mWindowManager.getDefaultDisplay()).thenReturn(mDisplay);
when(mDisplay.getDisplayId()).thenReturn(mDisplayId);
+ when(mNotifLiveDataStore.getHasActiveNotifs()).thenReturn(mHasActiveNotifs);
+ when(mHasActiveNotifs.getValue()).thenReturn(false);
mLightsOutNotifController = new LightsOutNotifController(
- mLightsOutView, mWindowManager, mEntryManager, mCommandQueue);
+ mLightsOutView,
+ mWindowManager,
+ mNotifLiveDataStore,
+ mCommandQueue);
mLightsOutNotifController.init();
mLightsOutNotifController.onViewAttached();
// Capture the entry listener object so we can simulate events in tests below
- verify(mEntryManager).addNotificationEntryListener(mListenerCaptor.capture());
- mEntryListener = Objects.requireNonNull(mListenerCaptor.getValue());
+ verify(mHasActiveNotifs).addSyncObserver(mObserverCaptor.capture());
+ mHaActiveNotifsObserver = Objects.requireNonNull(mObserverCaptor.getValue());
// Capture the callback object so we can simulate callback events in tests below
verify(mCommandQueue).addCallback(mCallbacksCaptor.capture());
@@ -138,7 +142,7 @@
@Test
public void testLightsOut_withNotifs_onSystemBarAttributesChanged() {
// GIVEN active visible notifications
- when(mEntryManager.hasActiveNotifications()).thenReturn(true);
+ when(mHasActiveNotifs.getValue()).thenReturn(true);
// WHEN lights out
mCallbacks.onSystemBarAttributesChanged(
@@ -158,7 +162,7 @@
@Test
public void testLightsOut_withoutNotifs_onSystemBarAttributesChanged() {
// GIVEN no active visible notifications
- when(mEntryManager.hasActiveNotifications()).thenReturn(false);
+ when(mHasActiveNotifs.getValue()).thenReturn(false);
// WHEN lights out
mCallbacks.onSystemBarAttributesChanged(
@@ -178,7 +182,7 @@
@Test
public void testLightsOn_afterLightsOut_onSystemBarAttributesChanged() {
// GIVEN active visible notifications
- when(mEntryManager.hasActiveNotifications()).thenReturn(true);
+ when(mHasActiveNotifs.getValue()).thenReturn(true);
// WHEN lights on
mCallbacks.onSystemBarAttributesChanged(
@@ -198,15 +202,15 @@
@Test
public void testEntryAdded() {
// GIVEN no visible notifications and lights out
- when(mEntryManager.hasActiveNotifications()).thenReturn(false);
+ when(mHasActiveNotifs.getValue()).thenReturn(false);
mLightsOutNotifController.mAppearance = LIGHTS_OUT;
mLightsOutNotifController.updateLightsOutView();
assertIsShowingDot(false);
// WHEN an active notification is added
- when(mEntryManager.hasActiveNotifications()).thenReturn(true);
+ when(mHasActiveNotifs.getValue()).thenReturn(true);
assertTrue(mLightsOutNotifController.shouldShowDot());
- mEntryListener.onNotificationAdded(mock(NotificationEntry.class));
+ mHaActiveNotifsObserver.onChanged(true);
// THEN we should see the dot view
assertIsShowingDot(true);
@@ -215,38 +219,20 @@
@Test
public void testEntryRemoved() {
// GIVEN a visible notification and lights out
- when(mEntryManager.hasActiveNotifications()).thenReturn(true);
+ when(mHasActiveNotifs.getValue()).thenReturn(true);
mLightsOutNotifController.mAppearance = LIGHTS_OUT;
mLightsOutNotifController.updateLightsOutView();
assertIsShowingDot(true);
// WHEN all active notifications are removed
- when(mEntryManager.hasActiveNotifications()).thenReturn(false);
+ when(mHasActiveNotifs.getValue()).thenReturn(false);
assertFalse(mLightsOutNotifController.shouldShowDot());
- mEntryListener.onEntryRemoved(
- mock(NotificationEntry.class), null, false, REASON_CANCEL_ALL);
+ mHaActiveNotifsObserver.onChanged(false);
// THEN we shouldn't see the dot view
assertIsShowingDot(false);
}
- @Test
- public void testEntryUpdated() {
- // GIVEN no visible notifications and lights out
- when(mEntryManager.hasActiveNotifications()).thenReturn(false);
- mLightsOutNotifController.mAppearance = LIGHTS_OUT;
- mLightsOutNotifController.updateLightsOutView();
- assertIsShowingDot(false);
-
- // WHEN an active notification is added
- when(mEntryManager.hasActiveNotifications()).thenReturn(true);
- assertTrue(mLightsOutNotifController.shouldShowDot());
- mEntryListener.onPostEntryUpdated(mock(NotificationEntry.class));
-
- // THEN we should see the dot view
- assertIsShowingDot(true);
- }
-
private void assertIsShowingDot(boolean isShowing) {
// cancel the animation so we can check the end state
final ViewPropertyAnimator animation = mLightsOutView.animate();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 9bcdcc9..1cd9b9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -425,6 +425,7 @@
.thenReturn(mKeyguardUserSwitcherComponent);
when(mKeyguardUserSwitcherComponent.getKeyguardUserSwitcherController())
.thenReturn(mKeyguardUserSwitcherController);
+ when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(true);
doAnswer((Answer<Void>) invocation -> {
mTouchHandler = invocation.getArgument(0);
@@ -880,11 +881,11 @@
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController).displayClock(LARGE);
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
mNotificationPanelViewController.closeQs();
- verify(mKeyguardStatusViewController).displayClock(SMALL);
+ verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true);
}
@Test
@@ -894,12 +895,14 @@
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController).displayClock(LARGE);
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController, times(2)).displayClock(LARGE);
- verify(mKeyguardStatusViewController, never()).displayClock(SMALL);
+ verify(mKeyguardStatusViewController, times(2))
+ .displayClock(LARGE, /* animate */ true);
+ verify(mKeyguardStatusViewController, never())
+ .displayClock(SMALL, /* animate */ true);
}
@Test
@@ -911,7 +914,20 @@
mNotificationPanelViewController.setDozing(true, false, null);
- verify(mKeyguardStatusViewController).displayClock(LARGE);
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
+ }
+
+ @Test
+ public void testSwitchesToBigClockInSplitShadeOnAodAnimateDisabled() {
+ when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(false);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+ when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+
+ mNotificationPanelViewController.setDozing(true, false, null);
+
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ false);
}
@Test
@@ -923,13 +939,13 @@
// one notification + media player visible
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController).displayClock(SMALL);
+ verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true);
// only media player visible
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController, times(2)).displayClock(SMALL);
- verify(mKeyguardStatusViewController, never()).displayClock(LARGE);
+ verify(mKeyguardStatusViewController, times(2)).displayClock(SMALL, true);
+ verify(mKeyguardStatusViewController, never()).displayClock(LARGE, /* animate */ true);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 7d266e9..235de1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -26,6 +26,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
@@ -57,6 +58,8 @@
private lateinit var sysuiUnfoldComponent: SysUIUnfoldComponent
@Mock
private lateinit var progressProvider: ScopedUnfoldTransitionProgressProvider
+ @Mock
+ private lateinit var configurationController: ConfigurationController
private lateinit var view: PhoneStatusBarView
private lateinit var controller: PhoneStatusBarViewController
@@ -116,7 +119,8 @@
private fun createController(view: PhoneStatusBarView): PhoneStatusBarViewController {
return PhoneStatusBarViewController.Factory(
Optional.of(sysuiUnfoldComponent),
- Optional.of(progressProvider)
+ Optional.of(progressProvider),
+ configurationController
).create(view, touchEventHandler)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
index 2d548e9..a8544a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
@@ -1,8 +1,8 @@
package com.android.systemui.statusbar.phone
-import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.view.View
+import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation
@@ -42,6 +42,7 @@
var viewVisibility = View.GONE
private lateinit var splitShadeHeaderController: SplitShadeHeaderController
+ private lateinit var carrierIconSlots: List<String>
@Before
fun setup() {
@@ -67,12 +68,13 @@
featureFlags,
batteryMeterViewController
)
+ carrierIconSlots = listOf(
+ context.getString(com.android.internal.R.string.status_bar_mobile))
}
@Test
fun setVisible_onlyInSplitShade() {
- splitShadeHeaderController.splitShadeMode = true
- splitShadeHeaderController.shadeExpanded = true
+ makeShadeVisible()
assertThat(viewVisibility).isEqualTo(View.VISIBLE)
splitShadeHeaderController.splitShadeMode = false
@@ -81,17 +83,38 @@
@Test
fun updateListeners_registersWhenVisible() {
- splitShadeHeaderController.splitShadeMode = true
- splitShadeHeaderController.shadeExpanded = true
+ makeShadeVisible()
verify(qsCarrierGroupController).setListening(true)
verify(statusBarIconController).addIconGroup(any())
}
@Test
fun shadeExpandedFraction_updatesAlpha() {
- splitShadeHeaderController.splitShadeMode = true
- splitShadeHeaderController.shadeExpanded = true
+ makeShadeVisible()
splitShadeHeaderController.shadeExpandedFraction = 0.5f
verify(view).setAlpha(ShadeInterpolation.getContentAlpha(0.5f))
}
-}
\ No newline at end of file
+
+ @Test
+ fun singleCarrier_enablesCarrierIconsInStatusIcons() {
+ whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(true)
+
+ makeShadeVisible()
+
+ verify(statusIcons).removeIgnoredSlots(carrierIconSlots)
+ }
+
+ @Test
+ fun dualCarrier_disablesCarrierIconsInStatusIcons() {
+ whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(false)
+
+ makeShadeVisible()
+
+ verify(statusIcons).addIgnoredSlots(carrierIconSlots)
+ }
+
+ private fun makeShadeVisible() {
+ splitShadeHeaderController.splitShadeMode = true
+ splitShadeHeaderController.shadeExpanded = true
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index c5bdfed..cbaa460 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -43,9 +43,6 @@
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -79,9 +76,7 @@
@Mock
private NotificationPanelViewController mNotificationPanelView;
@Mock
- private BiometricUnlockController mBiometrucUnlockController;
- @Mock
- private DismissCallbackRegistry mDismissCallbackRegistry;
+ private BiometricUnlockController mBiometricUnlockController;
@Mock
private SysuiStatusBarStateController mStatusBarStateController;
@Mock
@@ -97,15 +92,12 @@
@Mock
private KeyguardBouncer mBouncer;
@Mock
- private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
- @Mock
private StatusBarKeyguardViewManager.AlternateAuthInterceptor mAlternateAuthInterceptor;
@Mock
private KeyguardMessageArea mKeyguardMessageArea;
@Mock
private ShadeController mShadeController;
- private WakefulnessLifecycle mWakefulnessLifecycle;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Before
@@ -117,10 +109,6 @@
.thenReturn(mBouncer);
when(mStatusBar.getBouncerContainer()).thenReturn(mContainer);
when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
- mWakefulnessLifecycle = new WakefulnessLifecycle(
- getContext(),
- null,
- mock(DumpManager.class));
mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager(
getContext(),
mViewMediatorCallback,
@@ -134,15 +122,13 @@
mKeyguardStateController,
mock(NotificationMediaManager.class),
mKeyguardBouncerFactory,
- mWakefulnessLifecycle,
- mUnlockedScreenOffAnimationController,
mKeyguardMessageAreaFactory,
() -> mShadeController);
mStatusBarKeyguardViewManager.registerStatusBar(
mStatusBar,
mNotificationPanelView,
new PanelExpansionStateManager(),
- mBiometrucUnlockController,
+ mBiometricUnlockController,
mNotificationContainer,
mBypassController);
mStatusBarKeyguardViewManager.show(null);
@@ -261,7 +247,7 @@
@Test
public void onPanelExpansionChanged_neverTranslatesBouncerWhenWakeAndUnlock() {
- when(mBiometrucUnlockController.getMode())
+ when(mBiometricUnlockController.getMode())
.thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
/* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 0289b9a..9d5b17e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -119,6 +119,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -274,6 +275,7 @@
@Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
@Mock private ActivityLaunchAnimator mActivityLaunchAnimator;
@Mock private NotifPipelineFlags mNotifPipelineFlags;
+ @Mock private NotifLiveDataStore mNotifLiveDataStore;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -306,6 +308,7 @@
mNotificationListener,
mUiBgExecutor,
mNotifPipelineFlags,
+ mNotifLiveDataStore,
mVisibilityProvider,
mock(NotificationEntryManager.class),
mock(NotifPipeline.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
index 878bdea..d645449 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
@@ -58,7 +58,7 @@
mCondition3 = spy(new FakeCondition());
mConditions = new HashSet<>(Arrays.asList(mCondition1, mCondition2, mCondition3));
- mConditionMonitor = new Monitor(mConditions);
+ mConditionMonitor = new Monitor(mConditions, null /*callbacks*/);
}
@Test
@@ -98,7 +98,7 @@
@Test
public void addCallback_noConditions_reportAllConditionsMet() {
- final Monitor monitor = new Monitor(new HashSet<>());
+ final Monitor monitor = new Monitor(new HashSet<>(), null /*callbacks*/);
final Monitor.Callback callback = mock(Monitor.Callback.class);
monitor.addCallback(callback);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
index eb54fe0..0f1b65c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
@@ -44,6 +44,11 @@
inline fun <reified T> any(): T = any(T::class.java)
/**
+ * Kotlin type-inferred version of Mockito.nullable()
+ */
+inline fun <reified T> nullable(): T? = Mockito.nullable(T::class.java)
+
+/**
* Returns ArgumentCaptor.capture() as nullable type to avoid java.lang.IllegalStateException
* when null is returned.
*
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ref/GcWeakReference.java b/packages/SystemUI/tests/src/com/android/systemui/util/ref/GcWeakReference.java
new file mode 100644
index 0000000..ff6fd6f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/ref/GcWeakReference.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.util.ref;
+
+import com.android.internal.util.GcUtils;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * A WeakReference subclass that forces gc/finalizing on access.
+ */
+public class GcWeakReference<T> extends WeakReference<T> {
+ public GcWeakReference(T referent) {
+ super(referent);
+ }
+
+ @Override
+ public T get() throws RuntimeException {
+ GcUtils.runGcAndFinalizersSync();
+ return super.get();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
index 7c6d80b..22d7273 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
@@ -99,7 +99,7 @@
@Test
public void testConnect() {
ObservableServiceConnection<Foo> connection = new ObservableServiceConnection<>(mContext,
- mIntent, 0, mExecutor, mTransformer);
+ mIntent, mExecutor, mTransformer);
// Register twice to ensure only one callback occurs.
connection.addCallback(mCallback);
connection.addCallback(mCallback);
@@ -119,7 +119,7 @@
@Test
public void testDisconnect() {
ObservableServiceConnection<Foo> connection = new ObservableServiceConnection<>(mContext,
- mIntent, 0, mExecutor, mTransformer);
+ mIntent, mExecutor, mTransformer);
connection.addCallback(mCallback);
connection.onServiceDisconnected(mComponentName);
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 c35d51a..71ae5eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -108,6 +108,7 @@
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.bubbles.BubbleData;
@@ -246,6 +247,8 @@
private ScreenOffAnimationController mScreenOffAnimationController;
@Mock
private AuthController mAuthController;
+ @Mock
+ private TaskViewTransitions mTaskViewTransitions;
private TestableBubblePositioner mPositioner;
@@ -344,6 +347,7 @@
mock(DisplayController.class),
syncExecutor,
mock(Handler.class),
+ mTaskViewTransitions,
mock(SyncTransactionQueue.class));
mBubbleController.setExpandListener(mBubbleExpandListener);
spyOn(mBubbleController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index e2fce67..7b9e6a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -90,6 +90,7 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.bubbles.BubbleData;
@@ -217,6 +218,8 @@
private KeyguardStateController mKeyguardStateController;
@Mock
private ScreenOffAnimationController mScreenOffAnimationController;
+ @Mock
+ private TaskViewTransitions mTaskViewTransitions;
private TestableBubblePositioner mPositioner;
@@ -306,6 +309,7 @@
mock(DisplayController.class),
syncExecutor,
mock(Handler.class),
+ mTaskViewTransitions,
mock(SyncTransactionQueue.class));
mBubbleController.setExpandListener(mBubbleExpandListener);
spyOn(mBubbleController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index 7b77cb0..80834c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -23,6 +23,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleData;
@@ -56,11 +57,12 @@
DisplayController displayController,
ShellExecutor shellMainExecutor,
Handler shellMainHandler,
+ TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue) {
super(context, data, Runnable::run, floatingContentCoordinator, dataRepository,
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
bubbleLogger, taskStackListener, shellTaskOrganizer, positioner, displayController,
- shellMainExecutor, shellMainHandler, syncQueue);
+ shellMainExecutor, shellMainHandler, taskViewTransitions, syncQueue);
setInflateSynchronously(true);
initialize();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 1e15d2a..2f2e536 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -35,6 +35,7 @@
import com.android.systemui.tracing.ProtoTracer;
import com.android.wm.shell.ShellCommandHandler;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.compatui.CompatUI;
import com.android.wm.shell.draganddrop.DragAndDrop;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
@@ -42,7 +43,6 @@
import com.android.wm.shell.onehanded.OneHandedEventCallback;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
import com.android.wm.shell.splitscreen.SplitScreen;
import org.junit.Before;
@@ -78,7 +78,7 @@
@Mock WakefulnessLifecycle mWakefulnessLifecycle;
@Mock ProtoTracer mProtoTracer;
@Mock ShellCommandHandler mShellCommandHandler;
- @Mock SizeCompatUI mSizeCompatUI;
+ @Mock CompatUI mCompatUI;
@Mock ShellExecutor mSysUiMainExecutor;
@Mock DragAndDrop mDragAndDrop;
@@ -88,7 +88,7 @@
mWMShell = new WMShell(mContext, Optional.of(mPip), Optional.of(mLegacySplitScreen),
Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
- Optional.of(mShellCommandHandler), Optional.of(mSizeCompatUI),
+ Optional.of(mShellCommandHandler), Optional.of(mCompatUI),
Optional.of(mDragAndDrop),
mCommandQueue, mConfigurationController, mKeyguardUpdateMonitor,
mNavigationModeController, mScreenLifecycle, mSysUiState, mProtoTracer,
@@ -136,8 +136,8 @@
}
@Test
- public void initSizeCompatUI_registersCallbacks() {
- mWMShell.initSizeCompatUi(mSizeCompatUI);
+ public void initCompatUI_registersCallbacks() {
+ mWMShell.initCompatUi(mCompatUI);
verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
}
diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/res/values-land/dimens.xml b/packages/overlays/NavigationBarModeGesturalOverlay/res/values-land/dimens.xml
index 1f5834d..a03dcbd 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlay/res/values-land/dimens.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlay/res/values-land/dimens.xml
@@ -18,5 +18,5 @@
-->
<resources>
<!-- The height of the bottom navigation gesture area. -->
- <dimen name="navigation_bar_gesture_height">32dp</dimen>
+ <dimen name="navigation_bar_gesture_height">24dp</dimen>
</resources>
diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/res/values/dimens.xml b/packages/overlays/NavigationBarModeGesturalOverlay/res/values/dimens.xml
index ac1f022..c5d0c9e 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlay/res/values/dimens.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlay/res/values/dimens.xml
@@ -18,13 +18,13 @@
-->
<resources>
<!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">16dp</dimen>
+ <dimen name="navigation_bar_height">24dp</dimen>
<!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
- <dimen name="navigation_bar_height_landscape">16dp</dimen>
+ <dimen name="navigation_bar_height_landscape">24dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">16dp</dimen>
+ <dimen name="navigation_bar_width">24dp</dimen>
<!-- Height of the bottom navigation / system bar. -->
<dimen name="navigation_bar_frame_height">48dp</dimen>
<!-- The height of the bottom navigation gesture area. -->
- <dimen name="navigation_bar_gesture_height">32dp</dimen>
+ <dimen name="navigation_bar_gesture_height">24dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/res/values/dimens.xml b/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/res/values/dimens.xml
index ac1f022..c5d0c9e 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/res/values/dimens.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/res/values/dimens.xml
@@ -18,13 +18,13 @@
-->
<resources>
<!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">16dp</dimen>
+ <dimen name="navigation_bar_height">24dp</dimen>
<!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
- <dimen name="navigation_bar_height_landscape">16dp</dimen>
+ <dimen name="navigation_bar_height_landscape">24dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">16dp</dimen>
+ <dimen name="navigation_bar_width">24dp</dimen>
<!-- Height of the bottom navigation / system bar. -->
<dimen name="navigation_bar_frame_height">48dp</dimen>
<!-- The height of the bottom navigation gesture area. -->
- <dimen name="navigation_bar_gesture_height">32dp</dimen>
+ <dimen name="navigation_bar_gesture_height">24dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/res/values/dimens.xml b/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/res/values/dimens.xml
index ac1f022..c5d0c9e 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/res/values/dimens.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/res/values/dimens.xml
@@ -18,13 +18,13 @@
-->
<resources>
<!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">16dp</dimen>
+ <dimen name="navigation_bar_height">24dp</dimen>
<!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
- <dimen name="navigation_bar_height_landscape">16dp</dimen>
+ <dimen name="navigation_bar_height_landscape">24dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">16dp</dimen>
+ <dimen name="navigation_bar_width">24dp</dimen>
<!-- Height of the bottom navigation / system bar. -->
<dimen name="navigation_bar_frame_height">48dp</dimen>
<!-- The height of the bottom navigation gesture area. -->
- <dimen name="navigation_bar_gesture_height">32dp</dimen>
+ <dimen name="navigation_bar_gesture_height">24dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/NavigationBarModeGesturalOverlayWideBack/res/values/dimens.xml b/packages/overlays/NavigationBarModeGesturalOverlayWideBack/res/values/dimens.xml
index ac1f022..c5d0c9e 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlayWideBack/res/values/dimens.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlayWideBack/res/values/dimens.xml
@@ -18,13 +18,13 @@
-->
<resources>
<!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">16dp</dimen>
+ <dimen name="navigation_bar_height">24dp</dimen>
<!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
- <dimen name="navigation_bar_height_landscape">16dp</dimen>
+ <dimen name="navigation_bar_height_landscape">24dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">16dp</dimen>
+ <dimen name="navigation_bar_width">24dp</dimen>
<!-- Height of the bottom navigation / system bar. -->
<dimen name="navigation_bar_frame_height">48dp</dimen>
<!-- The height of the bottom navigation gesture area. -->
- <dimen name="navigation_bar_gesture_height">32dp</dimen>
+ <dimen name="navigation_bar_gesture_height">24dp</dimen>
</resources>
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 881c910..f050b66 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1027,8 +1027,8 @@
mSystemSupport.getMagnificationProcessor();
final long identity = Binder.clearCallingIdentity();
try {
- magnificationProcessor.getMagnificationRegion(displayId, region,
- mSecurityPolicy.canControlMagnification(this));
+ magnificationProcessor.getFullscreenMagnificationRegion(displayId,
+ region, mSecurityPolicy.canControlMagnification(this));
return region;
} finally {
Binder.restoreCallingIdentity(identity);
@@ -1095,7 +1095,7 @@
try {
MagnificationProcessor magnificationProcessor =
mSystemSupport.getMagnificationProcessor();
- return (magnificationProcessor.reset(displayId, animate)
+ return (magnificationProcessor.resetFullscreenMagnification(displayId, animate)
|| !magnificationProcessor.isMagnifying(displayId));
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index eaf2694..6744ea8 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -288,8 +288,6 @@
showGlobalActions();
return true;
}
- case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN:
- return toggleSplitScreen();
case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN:
return lockScreen();
case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT:
@@ -369,21 +367,6 @@
mWindowManagerService.showGlobalActions();
}
- private boolean toggleSplitScreen() {
- final long token = Binder.clearCallingIdentity();
- try {
- StatusBarManagerInternal statusBarService = LocalServices.getService(
- StatusBarManagerInternal.class);
- if (statusBarService == null) {
- return false;
- }
- statusBarService.toggleSplitScreen();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- return true;
- }
-
private boolean lockScreen() {
mContext.getSystemService(PowerManager.class).goToSleep(SystemClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_ACCESSIBILITY, 0);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
index d0b9895..7a525ee 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -119,6 +119,18 @@
return false;
}
+ private boolean setScaleAndCenterForFullScreenMagnification(int displayId, float scale,
+ float centerX, float centerY,
+ boolean animate, int id) {
+ if (!isRegistered(displayId)) {
+ register(displayId);
+ }
+ return mController.getFullScreenMagnificationController().setScaleAndCenter(
+ displayId,
+ scale,
+ centerX, centerY, animate, id);
+ }
+
/**
* Returns {@code true} if transition magnification mode needed. And it is no need to transition
* mode when the controlling mode is unchanged or the controlling magnifier is not activated.
@@ -135,24 +147,18 @@
}
/**
- * Returns the magnification scale. If an animation is in progress,
- * this reflects the end state of the animation.
+ * Returns the magnification scale of full-screen magnification on the display.
+ * If an animation is in progress, this reflects the end state of the animation.
*
* @param displayId The logical display id.
* @return the scale
*/
public float getScale(int displayId) {
- int mode = getControllingMode(displayId);
- if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
- return mController.getFullScreenMagnificationController().getScale(displayId);
- } else if (mode == MAGNIFICATION_MODE_WINDOW) {
- return mController.getWindowMagnificationMgr().getScale(displayId);
- }
- return 0;
+ return mController.getFullScreenMagnificationController().getScale(displayId);
}
/**
- * Returns the magnification center in X coordinate of the controlling magnification mode.
+ * Returns the magnification center in X coordinate of full-screen magnification.
* If the service can control magnification but fullscreen magnifier is not registered, it will
* register the magnifier for this call then unregister the magnifier finally to make the
* magnification center correct.
@@ -162,25 +168,19 @@
* @return the X coordinate
*/
public float getCenterX(int displayId, boolean canControlMagnification) {
- int mode = getControllingMode(displayId);
- if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
- boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
- canControlMagnification);
- try {
- return mController.getFullScreenMagnificationController().getCenterX(displayId);
- } finally {
- if (registeredJustForThisCall) {
- unregister(displayId);
- }
+ boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
+ canControlMagnification);
+ try {
+ return mController.getFullScreenMagnificationController().getCenterX(displayId);
+ } finally {
+ if (registeredJustForThisCall) {
+ unregister(displayId);
}
- } else if (mode == MAGNIFICATION_MODE_WINDOW) {
- return mController.getWindowMagnificationMgr().getCenterX(displayId);
}
- return 0;
}
/**
- * Returns the magnification center in Y coordinate of the controlling magnification mode.
+ * Returns the magnification center in Y coordinate of full-screen magnification.
* If the service can control magnification but fullscreen magnifier is not registered, it will
* register the magnifier for this call then unregister the magnifier finally to make the
* magnification center correct.
@@ -190,49 +190,25 @@
* @return the Y coordinate
*/
public float getCenterY(int displayId, boolean canControlMagnification) {
- int mode = getControllingMode(displayId);
- if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
- boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
- canControlMagnification);
- try {
- return mController.getFullScreenMagnificationController().getCenterY(displayId);
- } finally {
- if (registeredJustForThisCall) {
- unregister(displayId);
- }
+ boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
+ canControlMagnification);
+ try {
+ return mController.getFullScreenMagnificationController().getCenterY(displayId);
+ } finally {
+ if (registeredJustForThisCall) {
+ unregister(displayId);
}
- } else if (mode == MAGNIFICATION_MODE_WINDOW) {
- return mController.getWindowMagnificationMgr().getCenterY(displayId);
}
- return 0;
}
/**
- * Return the magnification bounds of the current controlling magnification on the given
- * display. If the magnifier is not enabled, it returns an empty region.
- * If the service can control magnification but fullscreen magnifier is not registered, it will
- * register the magnifier for this call then unregister the magnifier finally to make
- * the magnification region correct.
+ * Returns the magnification bounds of full-screen magnification on the given display.
*
* @param displayId The logical display id
* @param outRegion the region to populate
* @param canControlMagnification Whether the service can control magnification
- * @return outRegion the magnification bounds of full-screen magnifier or the magnification
- * source bounds of window magnifier
*/
- public Region getMagnificationRegion(int displayId, @NonNull Region outRegion,
- boolean canControlMagnification) {
- int mode = getControllingMode(displayId);
- if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
- getFullscreenMagnificationRegion(displayId, outRegion, canControlMagnification);
- } else if (mode == MAGNIFICATION_MODE_WINDOW) {
- mController.getWindowMagnificationMgr().getMagnificationSourceBounds(displayId,
- outRegion);
- }
- return outRegion;
- }
-
- private void getFullscreenMagnificationRegion(int displayId, @NonNull Region outRegion,
+ public void getFullscreenMagnificationRegion(int displayId, @NonNull Region outRegion,
boolean canControlMagnification) {
boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
canControlMagnification);
@@ -246,21 +222,9 @@
}
}
- private boolean setScaleAndCenterForFullScreenMagnification(int displayId, float scale,
- float centerX, float centerY,
- boolean animate, int id) {
- if (!isRegistered(displayId)) {
- register(displayId);
- }
- return mController.getFullScreenMagnificationController().setScaleAndCenter(
- displayId,
- scale,
- centerX, centerY, animate, id);
- }
-
/**
- * Resets the magnification on the given display. The reset mode could be full-screen or
- * window if it is activated.
+ * Resets the current magnification on the given display. The reset mode could be
+ * full-screen or window if it is activated.
*
* @param displayId The logical display id.
* @param animate {@code true} to animate the transition, {@code false}
@@ -268,7 +232,7 @@
* @return {@code true} if the magnification spec changed, {@code false} if
* the spec did not change
*/
- public boolean reset(int displayId, boolean animate) {
+ public boolean resetCurrentMagnification(int displayId, boolean animate) {
int mode = getControllingMode(displayId);
if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
return mController.getFullScreenMagnificationController().reset(displayId, animate);
@@ -279,6 +243,19 @@
}
/**
+ * Resets the full-screen magnification on the given display.
+ *
+ * @param displayId The logical display id.
+ * @param animate {@code true} to animate the transition, {@code false}
+ * to transition immediately
+ * @return {@code true} if the magnification spec changed, {@code false} if
+ * the spec did not change
+ */
+ public boolean resetFullscreenMagnification(int displayId, boolean animate) {
+ return mController.getFullScreenMagnificationController().reset(displayId, animate);
+ }
+
+ /**
* {@link FullScreenMagnificationController#resetIfNeeded(int, boolean)}
*/
// TODO: support window magnification
diff --git a/services/companion/Android.bp b/services/companion/Android.bp
index e3926b4..d3ef6dc 100644
--- a/services/companion/Android.bp
+++ b/services/companion/Android.bp
@@ -24,5 +24,8 @@
type: "stream",
},
srcs: [":services.companion-sources"],
- libs: ["services.core"],
+ libs: [
+ "app-compat-annotations",
+ "services.core",
+ ],
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index b32d543..9c996f4 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -177,6 +177,7 @@
new BluetoothDeviceConnectedListener();
private BleStateBroadcastReceiver mBleStateBroadcastReceiver = new BleStateBroadcastReceiver();
private List<String> mCurrentlyConnectedDevices = new ArrayList<>();
+ Set<Integer> mPresentSelfManagedDevices = new HashSet<>();
private ArrayMap<String, Date> mDevicesLastNearby = new ArrayMap<>();
private UnbindDeviceListenersRunnable
mUnbindDeviceListenersRunnable = new UnbindDeviceListenersRunnable();
@@ -217,7 +218,7 @@
mPermissionControllerManager = requireNonNull(
context.getSystemService(PermissionControllerManager.class));
mUserManager = context.getSystemService(UserManager.class);
- mCompanionDevicePresenceController = new CompanionDevicePresenceController();
+ mCompanionDevicePresenceController = new CompanionDevicePresenceController(this);
mAssociationRequestsProcessor = new AssociationRequestsProcessor(this);
registerPackageMonitor();
@@ -555,6 +556,57 @@
//TODO: b/199427116
}
+ @Override
+ public void notifyDeviceAppeared(int associationId) {
+ final AssociationInfo association = getAssociationWithCallerChecks(associationId);
+ if (association == null) {
+ throw new IllegalArgumentException("Association with ID " + associationId + " "
+ + "does not exist "
+ + "or belongs to a different package "
+ + "or belongs to a different user");
+ }
+
+ if (!association.isSelfManaged()) {
+ throw new IllegalArgumentException("Association with ID " + associationId
+ + " is not self-managed. notifyDeviceAppeared(int) can only be called for"
+ + " self-managed associations.");
+ }
+
+ if (!mPresentSelfManagedDevices.add(associationId)) {
+ Slog.w(LOG_TAG, "Association with ID " + associationId + " is already present");
+ return;
+ }
+
+ mCompanionDevicePresenceController.onDeviceNotifyAppeared(
+ association, getContext(), mMainHandler);
+ }
+
+ @Override
+ public void notifyDeviceDisappeared(int associationId) {
+ final AssociationInfo association = getAssociationWithCallerChecks(associationId);
+ if (association == null) {
+ throw new IllegalArgumentException("Association with ID " + associationId + " "
+ + "does not exist "
+ + "or belongs to a different package "
+ + "or belongs to a different user");
+ }
+
+ if (!association.isSelfManaged()) {
+ throw new IllegalArgumentException("Association with ID " + associationId
+ + " is not self-managed. notifyDeviceAppeared(int) can only be called for"
+ + " self-managed associations.");
+ }
+
+ if (!mPresentSelfManagedDevices.contains(associationId)) {
+ Slog.w(LOG_TAG, "Association with ID " + associationId + " is not connected");
+ return;
+ }
+
+ mPresentSelfManagedDevices.remove(associationId);
+ mCompanionDevicePresenceController.onDeviceNotifyDisappearedAndUnbind(
+ association, getContext(), mMainHandler);
+ }
+
private void registerDevicePresenceListenerActive(String packageName, String deviceAddress,
boolean active) throws RemoteException {
getContext().enforceCallingOrSelfPermission(
@@ -645,11 +697,18 @@
}
}
+
fout.append("Currently Connected Devices:").append('\n');
for (int i = 0, size = mCurrentlyConnectedDevices.size(); i < size; i++) {
fout.append(" ").append(mCurrentlyConnectedDevices.get(i)).append('\n');
}
+ fout.append("Currently SelfManaged Connected Devices associationId:").append('\n');
+ for (Integer associationId : mPresentSelfManagedDevices) {
+ fout.append(" ").append("AssociationId: ").append(
+ String.valueOf(associationId)).append('\n');
+ }
+
fout.append("Devices Last Nearby:").append('\n');
for (int i = 0, size = mDevicesLastNearby.size(); i < size; i++) {
String device = mDevicesLastNearby.keyAt(i);
@@ -774,7 +833,9 @@
}
void onAssociationPreRemove(AssociationInfo association) {
- if (association.isNotifyOnDeviceNearby()) {
+ if (association.isNotifyOnDeviceNearby()
+ || (association.isSelfManaged()
+ && mPresentSelfManagedDevices.contains(association.getId()))) {
mCompanionDevicePresenceController.unbindDevicePresenceListener(
association.getPackageName(), association.getUserId());
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java b/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
index 3e00846..4447684 100644
--- a/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
+++ b/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
@@ -49,8 +49,10 @@
private static final String LOG_TAG = "CompanionDevicePresenceController";
PerUser<ArrayMap<String, List<BoundService>>> mBoundServices;
private static final String META_DATA_KEY_PRIMARY = "primary";
+ private final CompanionDeviceManagerService mService;
- public CompanionDevicePresenceController() {
+ public CompanionDevicePresenceController(CompanionDeviceManagerService service) {
+ mService = service;
mBoundServices = new PerUser<ArrayMap<String, List<BoundService>>>() {
@NonNull
@Override
@@ -61,24 +63,45 @@
}
void onDeviceNotifyAppeared(AssociationInfo association, Context context, Handler handler) {
- ServiceConnector<ICompanionDeviceService> primaryConnector =
- getPrimaryServiceConnector(association, context, handler);
- if (primaryConnector != null) {
- Slog.i(LOG_TAG,
- "Sending onDeviceAppeared to " + association.getPackageName() + ")");
- primaryConnector.run(
- s -> s.onDeviceAppeared(association.getDeviceMacAddressAsString()));
+ for (BoundService boundService : getDeviceListenerServiceConnector(
+ association, context, handler)) {
+ if (boundService.mIsPrimary) {
+ Slog.i(LOG_TAG,
+ "Sending onDeviceAppeared to " + association.getPackageName() + ")");
+ boundService.mServiceConnector.run(
+ service -> service.onDeviceAppeared(association));
+ } else {
+ Slog.i(LOG_TAG, "Connecting to " + boundService.mComponentName);
+ boundService.mServiceConnector.connect();
+ }
}
}
void onDeviceNotifyDisappeared(AssociationInfo association, Context context, Handler handler) {
- ServiceConnector<ICompanionDeviceService> primaryConnector =
- getPrimaryServiceConnector(association, context, handler);
- if (primaryConnector != null) {
- Slog.i(LOG_TAG,
- "Sending onDeviceDisappeared to " + association.getPackageName() + ")");
- primaryConnector.run(
- s -> s.onDeviceDisappeared(association.getDeviceMacAddressAsString()));
+ for (BoundService boundService : getDeviceListenerServiceConnector(
+ association, context, handler)) {
+ if (boundService.mIsPrimary) {
+ Slog.i(LOG_TAG,
+ "Sending onDeviceDisappeared to " + association.getPackageName() + ")");
+ boundService.mServiceConnector.run(service ->
+ service.onDeviceDisappeared(association));
+ }
+ }
+ }
+
+ void onDeviceNotifyDisappearedAndUnbind(AssociationInfo association,
+ Context context, Handler handler) {
+ for (BoundService boundService : getDeviceListenerServiceConnector(
+ association, context, handler)) {
+ if (boundService.mIsPrimary) {
+ Slog.i(LOG_TAG,
+ "Sending onDeviceDisappeared to " + association.getPackageName() + ")");
+ boundService.mServiceConnector.post(
+ service -> {
+ service.onDeviceDisappeared(association);
+ }).thenRun(() -> unbindDevicePresenceListener(
+ association.getPackageName(), association.getUserId()));
+ }
}
}
@@ -93,17 +116,6 @@
}
}
- private ServiceConnector<ICompanionDeviceService> getPrimaryServiceConnector(
- AssociationInfo association, Context context, Handler handler) {
- for (BoundService boundService: getDeviceListenerServiceConnector(association, context,
- handler)) {
- if (boundService.mIsPrimary) {
- return boundService.mServiceConnector;
- }
- }
- return null;
- }
-
private List<BoundService> getDeviceListenerServiceConnector(AssociationInfo a, Context context,
Handler handler) {
return mBoundServices.forUser(a.getUserId()).computeIfAbsent(
@@ -140,18 +152,22 @@
protected long getAutoDisconnectTimeoutMs() {
// Service binding is managed manually based on corresponding device
// being nearby
- return Long.MAX_VALUE;
+ return -1;
}
@Override
public void binderDied() {
super.binderDied();
-
- // Re-connect to the service if process gets killed
- handler.postDelayed(
- this::connect,
- CompanionDeviceManagerService
- .DEVICE_LISTENER_DIED_REBIND_TIMEOUT_MS);
+ if (a.isSelfManaged()) {
+ mBoundServices.forUser(a.getUserId()).remove(a.getPackageName());
+ mService.mPresentSelfManagedDevices.remove(a.getId());
+ } else {
+ // Re-connect to the service if process gets killed
+ handler.postDelayed(
+ this::connect,
+ CompanionDeviceManagerService
+ .DEVICE_LISTENER_DIED_REBIND_TIMEOUT_MS);
+ }
}
};
@@ -191,6 +207,13 @@
}
}
+ if (packageResolveInfos.size() > 1 && primaryCount == 0) {
+ Slog.e(LOG_TAG, "Must have exactly one primary CompanionDeviceService "
+ + "to be bound when declare more than one CompanionDeviceService but "
+ + association.getPackageName() + " has " + primaryCount);
+ return false;
+ }
+
if (packageResolveInfos.size() == 1 && primaryCount != 0) {
Slog.w(LOG_TAG, "Do not need the primary metadata if there's only one"
+ " CompanionDeviceService " + "but " + association.getPackageName()
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
new file mode 100644
index 0000000..a6a8793
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+package com.android.server.companion.virtual;
+
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+import android.annotation.NonNull;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.os.Build;
+import android.os.UserHandle;
+import android.window.DisplayWindowPolicyController;
+
+import java.util.List;
+
+
+/**
+ * A controller to control the policies of the windows that can be displayed on the virtual display.
+ */
+class GenericWindowPolicyController extends DisplayWindowPolicyController {
+
+ /**
+ * If required, allow the secure activity to display on remote device since
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ public static final long ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE = 201712607L;
+
+ GenericWindowPolicyController(int windowFlags, int systemWindowFlags) {
+ setInterestedWindowFlags(windowFlags, systemWindowFlags);
+ }
+
+ @Override
+ public boolean canContainActivities(@NonNull List<ActivityInfo> activities) {
+ // Can't display all the activities if any of them don't want to be displayed.
+ final int activityCount = activities.size();
+ for (int i = 0; i < activityCount; i++) {
+ final ActivityInfo aInfo = activities.get(i);
+ if ((aInfo.flags & ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES) == 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags,
+ int systemWindowFlags) {
+ if ((activityInfo.flags & ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES) == 0) {
+ return false;
+ }
+ if (!CompatChanges.isChangeEnabled(ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE,
+ activityInfo.packageName,
+ UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid))) {
+ // TODO(b/201712607): Add checks for the apps that use SurfaceView#setSecure.
+ if ((windowFlags & FLAG_SECURE) != 0) {
+ return false;
+ }
+ if ((systemWindowFlags & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void onTopActivityChanged(ComponentName topActivity, int uid) {
+
+ }
+
+ @Override
+ public void onRunningAppsChanged(int[] runningUids) {
+
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
new file mode 100644
index 0000000..067edcc
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -0,0 +1,283 @@
+/*
+ * 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.
+ */
+
+package com.android.server.companion.virtual;
+
+import android.annotation.NonNull;
+import android.graphics.Point;
+import android.hardware.input.VirtualKeyEvent;
+import android.hardware.input.VirtualMouseButtonEvent;
+import android.hardware.input.VirtualMouseRelativeEvent;
+import android.hardware.input.VirtualMouseScrollEvent;
+import android.hardware.input.VirtualTouchEvent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
+import java.util.Map;
+
+/** Controls virtual input devices, including device lifecycle and event dispatch. */
+class InputController {
+
+ private final Object mLock;
+
+ /* Token -> file descriptor associations. */
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ final Map<IBinder, Integer> mInputDeviceFds = new ArrayMap<>();
+
+ private final NativeWrapper mNativeWrapper;
+
+ InputController(@NonNull Object lock) {
+ this(lock, new NativeWrapper());
+ }
+
+ @VisibleForTesting
+ InputController(@NonNull Object lock, @NonNull NativeWrapper nativeWrapper) {
+ mLock = lock;
+ mNativeWrapper = nativeWrapper;
+ }
+
+ void close() {
+ synchronized (mLock) {
+ for (int fd : mInputDeviceFds.values()) {
+ mNativeWrapper.closeUinput(fd);
+ }
+ mInputDeviceFds.clear();
+ }
+ }
+
+ void createKeyboard(@NonNull String deviceName,
+ int vendorId,
+ int productId,
+ @NonNull IBinder deviceToken) {
+ final int fd = mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId);
+ if (fd < 0) {
+ throw new RuntimeException(
+ "A native error occurred when creating keyboard: " + -fd);
+ }
+ synchronized (mLock) {
+ mInputDeviceFds.put(deviceToken, fd);
+ }
+ try {
+ deviceToken.linkToDeath(new BinderDeathRecipient(deviceToken), /* flags= */ 0);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Could not create virtual keyboard", e);
+ }
+ }
+
+ void createMouse(@NonNull String deviceName,
+ int vendorId,
+ int productId,
+ @NonNull IBinder deviceToken) {
+ final int fd = mNativeWrapper.openUinputMouse(deviceName, vendorId, productId);
+ if (fd < 0) {
+ throw new RuntimeException(
+ "A native error occurred when creating mouse: " + -fd);
+ }
+ synchronized (mLock) {
+ mInputDeviceFds.put(deviceToken, fd);
+ }
+ try {
+ deviceToken.linkToDeath(new BinderDeathRecipient(deviceToken), /* flags= */ 0);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Could not create virtual mouse", e);
+ }
+ }
+
+ void createTouchscreen(@NonNull String deviceName,
+ int vendorId,
+ int productId,
+ @NonNull IBinder deviceToken,
+ @NonNull Point screenSize) {
+ final int fd = mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId,
+ screenSize.y, screenSize.x);
+ if (fd < 0) {
+ throw new RuntimeException(
+ "A native error occurred when creating touchscreen: " + -fd);
+ }
+ synchronized (mLock) {
+ mInputDeviceFds.put(deviceToken, fd);
+ }
+ try {
+ deviceToken.linkToDeath(new BinderDeathRecipient(deviceToken), /* flags= */ 0);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Could not create virtual touchscreen", e);
+ }
+ }
+
+ void unregisterInputDevice(@NonNull IBinder token) {
+ synchronized (mLock) {
+ final Integer fd = mInputDeviceFds.remove(token);
+ if (fd == null) {
+ throw new IllegalArgumentException(
+ "Could not unregister input device for given token");
+ }
+ mNativeWrapper.closeUinput(fd);
+ }
+ }
+
+ boolean sendKeyEvent(@NonNull IBinder token, @NonNull VirtualKeyEvent event) {
+ synchronized (mLock) {
+ final Integer fd = mInputDeviceFds.get(token);
+ if (fd == null) {
+ throw new IllegalArgumentException(
+ "Could not send key event to input device for given token");
+ }
+ return mNativeWrapper.writeKeyEvent(fd, event.getKeyCode(), event.getAction());
+ }
+ }
+
+ boolean sendButtonEvent(@NonNull IBinder token, @NonNull VirtualMouseButtonEvent event) {
+ synchronized (mLock) {
+ final Integer fd = mInputDeviceFds.get(token);
+ if (fd == null) {
+ throw new IllegalArgumentException(
+ "Could not send button event to input device for given token");
+ }
+ return mNativeWrapper.writeButtonEvent(fd, event.getButtonCode(), event.getAction());
+ }
+ }
+
+ boolean sendTouchEvent(@NonNull IBinder token, @NonNull VirtualTouchEvent event) {
+ synchronized (mLock) {
+ final Integer fd = mInputDeviceFds.get(token);
+ if (fd == null) {
+ throw new IllegalArgumentException(
+ "Could not send touch event to input device for given token");
+ }
+ return mNativeWrapper.writeTouchEvent(fd, event.getPointerId(), event.getToolType(),
+ event.getAction(), event.getX(), event.getY(), event.getPressure(),
+ event.getMajorAxisSize());
+ }
+ }
+
+ boolean sendRelativeEvent(@NonNull IBinder token, @NonNull VirtualMouseRelativeEvent event) {
+ synchronized (mLock) {
+ final Integer fd = mInputDeviceFds.get(token);
+ if (fd == null) {
+ throw new IllegalArgumentException(
+ "Could not send relative event to input device for given token");
+ }
+ return mNativeWrapper.writeRelativeEvent(fd, event.getRelativeX(),
+ event.getRelativeY());
+ }
+ }
+
+ boolean sendScrollEvent(@NonNull IBinder token, @NonNull VirtualMouseScrollEvent event) {
+ synchronized (mLock) {
+ final Integer fd = mInputDeviceFds.get(token);
+ if (fd == null) {
+ throw new IllegalArgumentException(
+ "Could not send scroll event to input device for given token");
+ }
+ return mNativeWrapper.writeScrollEvent(fd, event.getXAxisMovement(),
+ event.getYAxisMovement());
+ }
+ }
+
+ public void dump(@NonNull PrintWriter fout) {
+ fout.println(" InputController: ");
+ synchronized (mLock) {
+ fout.println(" Active file descriptors: ");
+ for (int inputDeviceFd : mInputDeviceFds.values()) {
+ fout.println(inputDeviceFd);
+ }
+ }
+ }
+
+ private static native int nativeOpenUinputKeyboard(String deviceName, int vendorId,
+ int productId);
+ private static native int nativeOpenUinputMouse(String deviceName, int vendorId,
+ int productId);
+ private static native int nativeOpenUinputTouchscreen(String deviceName, int vendorId,
+ int productId, int height, int width);
+ private static native boolean nativeCloseUinput(int fd);
+ private static native boolean nativeWriteKeyEvent(int fd, int androidKeyCode, int action);
+ private static native boolean nativeWriteButtonEvent(int fd, int buttonCode, int action);
+ private static native boolean nativeWriteTouchEvent(int fd, int pointerId, int toolType,
+ int action, float locationX, float locationY, float pressure, float majorAxisSize);
+ private static native boolean nativeWriteRelativeEvent(int fd, float relativeX,
+ float relativeY);
+ private static native boolean nativeWriteScrollEvent(int fd, float xAxisMovement,
+ float yAxisMovement);
+
+ /** Wrapper around the static native methods for tests. */
+ @VisibleForTesting
+ protected static class NativeWrapper {
+ public int openUinputKeyboard(String deviceName, int vendorId, int productId) {
+ return nativeOpenUinputKeyboard(deviceName, vendorId,
+ productId);
+ }
+
+ public int openUinputMouse(String deviceName, int vendorId, int productId) {
+ return nativeOpenUinputMouse(deviceName, vendorId,
+ productId);
+ }
+
+ public int openUinputTouchscreen(String deviceName, int vendorId, int productId, int height,
+ int width) {
+ return nativeOpenUinputTouchscreen(deviceName, vendorId,
+ productId, height, width);
+ }
+
+ public boolean closeUinput(int fd) {
+ return nativeCloseUinput(fd);
+ }
+
+ public boolean writeKeyEvent(int fd, int androidKeyCode, int action) {
+ return nativeWriteKeyEvent(fd, androidKeyCode, action);
+ }
+
+ public boolean writeButtonEvent(int fd, int buttonCode, int action) {
+ return nativeWriteButtonEvent(fd, buttonCode, action);
+ }
+
+ public boolean writeTouchEvent(int fd, int pointerId, int toolType, int action,
+ float locationX, float locationY, float pressure, float majorAxisSize) {
+ return nativeWriteTouchEvent(fd, pointerId, toolType,
+ action, locationX, locationY,
+ pressure, majorAxisSize);
+ }
+
+ public boolean writeRelativeEvent(int fd, float relativeX, float relativeY) {
+ return nativeWriteRelativeEvent(fd, relativeX, relativeY);
+ }
+
+ public boolean writeScrollEvent(int fd, float xAxisMovement, float yAxisMovement) {
+ return nativeWriteScrollEvent(fd, xAxisMovement,
+ yAxisMovement);
+ }
+ }
+
+ private final class BinderDeathRecipient implements IBinder.DeathRecipient {
+
+ private final IBinder mDeviceToken;
+
+ BinderDeathRecipient(IBinder deviceToken) {
+ mDeviceToken = deviceToken;
+ }
+
+ @Override
+ public void binderDied() {
+ unregisterInputDevice(mDeviceToken);
+ }
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
new file mode 100644
index 0000000..022da43
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -0,0 +1,278 @@
+/*
+ * 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.
+ */
+
+package com.android.server.companion.virtual;
+
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+import android.annotation.NonNull;
+import android.companion.AssociationInfo;
+import android.companion.virtual.IVirtualDevice;
+import android.content.Context;
+import android.graphics.Point;
+import android.hardware.input.VirtualKeyEvent;
+import android.hardware.input.VirtualMouseButtonEvent;
+import android.hardware.input.VirtualMouseRelativeEvent;
+import android.hardware.input.VirtualMouseScrollEvent;
+import android.hardware.input.VirtualTouchEvent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.window.DisplayWindowPolicyController;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+
+final class VirtualDeviceImpl extends IVirtualDevice.Stub
+ implements IBinder.DeathRecipient {
+
+ private final Object mVirtualDeviceLock = new Object();
+
+ private final Context mContext;
+ private final AssociationInfo mAssociationInfo;
+ private final int mOwnerUid;
+ private final GenericWindowPolicyController mGenericWindowPolicyController;
+ private final InputController mInputController;
+ @VisibleForTesting
+ final List<Integer> mVirtualDisplayIds = new ArrayList<>();
+ private final OnDeviceCloseListener mListener;
+
+ VirtualDeviceImpl(Context context, AssociationInfo associationInfo,
+ IBinder token, int ownerUid, OnDeviceCloseListener listener) {
+ this(context, associationInfo, token, ownerUid, /* inputController= */ null, listener);
+ }
+
+ @VisibleForTesting
+ VirtualDeviceImpl(Context context, AssociationInfo associationInfo, IBinder token,
+ int ownerUid, InputController inputController, OnDeviceCloseListener listener) {
+ mContext = context;
+ mAssociationInfo = associationInfo;
+ mGenericWindowPolicyController = new GenericWindowPolicyController(FLAG_SECURE,
+ SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ mOwnerUid = ownerUid;
+ if (inputController == null) {
+ mInputController = new InputController(mVirtualDeviceLock);
+ } else {
+ mInputController = inputController;
+ }
+ mListener = listener;
+ try {
+ token.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public int getAssociationId() {
+ return mAssociationInfo.getId();
+ }
+
+ @Override // Binder call
+ public void close() {
+ mListener.onClose(mAssociationInfo.getId());
+ mInputController.close();
+ }
+
+ @Override
+ public void binderDied() {
+ close();
+ }
+
+ @Override // Binder call
+ public void createVirtualKeyboard(
+ int displayId,
+ @NonNull String deviceName,
+ int vendorId,
+ int productId,
+ @NonNull IBinder deviceToken) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
+ "Permission required to create a virtual keyboard");
+ synchronized (mVirtualDeviceLock) {
+ if (!mVirtualDisplayIds.contains(displayId)) {
+ throw new SecurityException(
+ "Cannot create a virtual keyboard for a display not associated with "
+ + "this virtual device");
+ }
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mInputController.createKeyboard(deviceName, vendorId, productId, deviceToken);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public void createVirtualMouse(
+ int displayId,
+ @NonNull String deviceName,
+ int vendorId,
+ int productId,
+ @NonNull IBinder deviceToken) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
+ "Permission required to create a virtual mouse");
+ synchronized (mVirtualDeviceLock) {
+ if (!mVirtualDisplayIds.contains(displayId)) {
+ throw new SecurityException(
+ "Cannot create a virtual mouse for a display not associated with this "
+ + "virtual device");
+ }
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mInputController.createMouse(deviceName, vendorId, productId, deviceToken);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public void createVirtualTouchscreen(
+ int displayId,
+ @NonNull String deviceName,
+ int vendorId,
+ int productId,
+ @NonNull IBinder deviceToken,
+ @NonNull Point screenSize) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
+ "Permission required to create a virtual touchscreen");
+ synchronized (mVirtualDeviceLock) {
+ if (!mVirtualDisplayIds.contains(displayId)) {
+ throw new SecurityException(
+ "Cannot create a virtual touchscreen for a display not associated with "
+ + "this virtual device");
+ }
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mInputController.createTouchscreen(deviceName, vendorId, productId,
+ deviceToken, screenSize);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public void unregisterInputDevice(IBinder token) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
+ "Permission required to unregister this input device");
+
+ final long binderToken = Binder.clearCallingIdentity();
+ try {
+ mInputController.unregisterInputDevice(token);
+ } finally {
+ Binder.restoreCallingIdentity(binderToken);
+ }
+ }
+
+ @Override // Binder call
+ public boolean sendKeyEvent(IBinder token, VirtualKeyEvent event) {
+ final long binderToken = Binder.clearCallingIdentity();
+ try {
+ return mInputController.sendKeyEvent(token, event);
+ } finally {
+ Binder.restoreCallingIdentity(binderToken);
+ }
+ }
+
+ @Override // Binder call
+ public boolean sendButtonEvent(IBinder token, VirtualMouseButtonEvent event) {
+ final long binderToken = Binder.clearCallingIdentity();
+ try {
+ return mInputController.sendButtonEvent(token, event);
+ } finally {
+ Binder.restoreCallingIdentity(binderToken);
+ }
+ }
+
+ @Override // Binder call
+ public boolean sendTouchEvent(IBinder token, VirtualTouchEvent event) {
+ final long binderToken = Binder.clearCallingIdentity();
+ try {
+ return mInputController.sendTouchEvent(token, event);
+ } finally {
+ Binder.restoreCallingIdentity(binderToken);
+ }
+ }
+
+ @Override // Binder call
+ public boolean sendRelativeEvent(IBinder token, VirtualMouseRelativeEvent event) {
+ final long binderToken = Binder.clearCallingIdentity();
+ try {
+ return mInputController.sendRelativeEvent(token, event);
+ } finally {
+ Binder.restoreCallingIdentity(binderToken);
+ }
+ }
+
+ @Override // Binder call
+ public boolean sendScrollEvent(IBinder token, VirtualMouseScrollEvent event) {
+ final long binderToken = Binder.clearCallingIdentity();
+ try {
+ return mInputController.sendScrollEvent(token, event);
+ } finally {
+ Binder.restoreCallingIdentity(binderToken);
+ }
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ fout.println(" VirtualDevice: ");
+ fout.println(" mVirtualDisplayIds: ");
+ synchronized (mVirtualDeviceLock) {
+ for (int id : mVirtualDisplayIds) {
+ fout.println(" " + id);
+ }
+ }
+ mInputController.dump(fout);
+ }
+
+ DisplayWindowPolicyController onVirtualDisplayCreatedLocked(int displayId) {
+ if (mVirtualDisplayIds.contains(displayId)) {
+ throw new IllegalStateException(
+ "Virtual device already have a virtual display with ID " + displayId);
+ }
+ mVirtualDisplayIds.add(displayId);
+ return mGenericWindowPolicyController;
+ }
+
+ void onVirtualDisplayRemovedLocked(int displayId) {
+ if (!mVirtualDisplayIds.contains(displayId)) {
+ throw new IllegalStateException(
+ "Virtual device doesn't have a virtual display with ID " + displayId);
+ }
+ mVirtualDisplayIds.remove(displayId);
+ }
+
+ int getOwnerUid() {
+ return mOwnerUid;
+ }
+
+ interface OnDeviceCloseListener {
+ void onClose(int associationId);
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
deleted file mode 100644
index ee09832..0000000
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ /dev/null
@@ -1,36 +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.
- */
-
-package com.android.server.companion.virtual;
-
-/**
- * Virtual device manager local service interface.
- */
-public abstract class VirtualDeviceManagerInternal {
-
- /**
- * Returns true if the given {@code uid} is the owner of any virtual devices that are
- * currently active.
- */
- public abstract boolean isAppOwnerOfAnyVirtualDevice(int uid);
-
- /**
- * Returns true if the given {@code uid} is currently running on any virtual devices. This is
- * determined by whether the app has any activities in the task stack on a virtual-device-owned
- * display.
- */
- public abstract boolean isAppRunningOnAnyVirtualDevice(int uid);
-}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 8592c05..46e75f7 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -31,6 +31,7 @@
import android.util.ExceptionUtils;
import android.util.Slog;
import android.util.SparseArray;
+import android.window.DisplayWindowPolicyController;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
@@ -42,12 +43,12 @@
import java.util.concurrent.ConcurrentHashMap;
-/** @hide */
@SuppressLint("LongLogTag")
public class VirtualDeviceManagerService extends SystemService {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "VirtualDeviceManagerService";
+
private final Object mVirtualDeviceManagerLock = new Object();
private final VirtualDeviceManagerImpl mImpl;
@@ -84,6 +85,15 @@
publishLocalService(VirtualDeviceManagerInternal.class, new LocalService());
}
+ @GuardedBy("mVirtualDeviceManagerLock")
+ private boolean isValidVirtualDeviceLocked(IVirtualDevice virtualDevice) {
+ try {
+ return mVirtualDevices.contains(virtualDevice.getAssociationId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
@Override
public void onUserStarting(@NonNull TargetUser user) {
super.onUserStarting(user);
@@ -117,43 +127,9 @@
}
}
- private class VirtualDeviceImpl extends IVirtualDevice.Stub implements IBinder.DeathRecipient {
-
- private final AssociationInfo mAssociationInfo;
- private final int mOwnerUid;
-
- private VirtualDeviceImpl(int ownerUid, IBinder token, AssociationInfo associationInfo) {
- mOwnerUid = ownerUid;
- mAssociationInfo = associationInfo;
- try {
- token.linkToDeath(this, 0);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- mVirtualDevices.put(associationInfo.getId(), this);
- }
-
- @Override
- public int getAssociationId() {
- return mAssociationInfo.getId();
- }
-
- @Override
- public void close() {
- synchronized (mVirtualDeviceManagerLock) {
- mVirtualDevices.remove(mAssociationInfo.getId());
- }
- }
-
- @Override
- public void binderDied() {
- close();
- }
- }
-
class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub {
- @Override
+ @Override // Binder call
public IVirtualDevice createVirtualDevice(
IBinder token, String packageName, int associationId) {
getContext().enforceCallingOrSelfPermission(
@@ -175,7 +151,18 @@
"Virtual device for association ID " + associationId
+ " already exists");
}
- return new VirtualDeviceImpl(callingUid, token, associationInfo);
+ VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(),
+ associationInfo, token, callingUid,
+ new VirtualDeviceImpl.OnDeviceCloseListener() {
+ @Override
+ public void onClose(int associationId) {
+ synchronized (mVirtualDeviceManagerLock) {
+ mVirtualDevices.remove(associationId);
+ }
+ }
+ });
+ mVirtualDevices.put(associationInfo.getId(), virtualDevice);
+ return virtualDevice;
}
}
@@ -220,8 +207,7 @@
fout.println("Created virtual devices: ");
synchronized (mVirtualDeviceManagerLock) {
for (int i = 0; i < mVirtualDevices.size(); i++) {
- VirtualDeviceImpl virtualDevice = mVirtualDevices.valueAt(i);
- fout.printf("%d: %s\n", mVirtualDevices.keyAt(i), virtualDevice);
+ mVirtualDevices.valueAt(i).dump(fd, fout, args);
}
}
}
@@ -230,11 +216,33 @@
private final class LocalService extends VirtualDeviceManagerInternal {
@Override
+ public boolean isValidVirtualDevice(IVirtualDevice virtualDevice) {
+ synchronized (mVirtualDeviceManagerLock) {
+ return isValidVirtualDeviceLocked(virtualDevice);
+ }
+ }
+
+ @Override
+ public DisplayWindowPolicyController onVirtualDisplayCreated(IVirtualDevice virtualDevice,
+ int displayId) {
+ synchronized (mVirtualDeviceManagerLock) {
+ return ((VirtualDeviceImpl) virtualDevice).onVirtualDisplayCreatedLocked(displayId);
+ }
+ }
+
+ @Override
+ public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) {
+ synchronized (mVirtualDeviceManagerLock) {
+ ((VirtualDeviceImpl) virtualDevice).onVirtualDisplayRemovedLocked(displayId);
+ }
+ }
+
+ @Override
public boolean isAppOwnerOfAnyVirtualDevice(int uid) {
synchronized (mVirtualDeviceManagerLock) {
int size = mVirtualDevices.size();
for (int i = 0; i < size; i++) {
- if (mVirtualDevices.valueAt(i).mOwnerUid == uid) {
+ if (mVirtualDevices.valueAt(i).getOwnerUid() == uid) {
return true;
}
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index ba6854b..9641eb5 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -121,7 +121,7 @@
"java/com/android/server/am/EventLogTags.logtags",
"java/com/android/server/wm/EventLogTags.logtags",
"java/com/android/server/policy/EventLogTags.logtags",
- ":services.connectivity-nsd-sources",
+ ":services.connectivity-tiramisu-sources",
],
libs: [
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 121a0bc..6fe2806 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -28,10 +28,6 @@
import android.content.ContentResolver;
import android.content.Intent;
import android.content.IntentSender;
-import android.content.pm.PackageManager.ApplicationInfoFlags;
-import android.content.pm.PackageManager.ComponentInfoFlags;
-import android.content.pm.PackageManager.PackageInfoFlags;
-import android.content.pm.PackageManager.ResolveInfoFlags;
import android.content.pm.SigningDetails.CertCapabilities;
import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.component.ParsedMainComponent;
@@ -199,7 +195,7 @@
* @see PackageManager#getPackageInfo(String, int)
*/
public abstract PackageInfo getPackageInfo(String packageName,
- @PackageInfoFlags long flags, int filterCallingUid, int userId);
+ @PackageManager.PackageInfoFlagsBits long flags, int filterCallingUid, int userId);
/**
* Retrieve CE data directory inode number of an application.
@@ -228,7 +224,8 @@
* deleted with {@code DELETE_KEEP_DATA} flag set).
*/
public abstract List<ApplicationInfo> getInstalledApplications(
- @ApplicationInfoFlags long flags, @UserIdInt int userId, int callingUid);
+ @PackageManager.ApplicationInfoFlagsBits long flags, @UserIdInt int userId,
+ int callingUid);
/**
* Retrieve launcher extras for a suspended package provided to the system in
@@ -325,7 +322,8 @@
* @see PackageManager#getPackageUidAsUser(String, int, int)
* @return The app's uid, or < 0 if the package was not found in that user
*/
- public abstract int getPackageUid(String packageName, @PackageInfoFlags long flags, int userId);
+ public abstract int getPackageUid(String packageName,
+ @PackageManager.PackageInfoFlagsBits long flags, int userId);
/**
* Retrieve all of the information we know about a particular package/application.
@@ -334,7 +332,7 @@
* @see PackageManager#getApplicationInfo(String, int)
*/
public abstract ApplicationInfo getApplicationInfo(String packageName,
- @ApplicationInfoFlags long flags, int filterCallingUid, int userId);
+ @PackageManager.ApplicationInfoFlagsBits long flags, int filterCallingUid, int userId);
/**
* Retrieve all of the information we know about a particular activity class.
@@ -343,7 +341,7 @@
* @see PackageManager#getActivityInfo(ComponentName, int)
*/
public abstract ActivityInfo getActivityInfo(ComponentName component,
- @ComponentInfoFlags long flags, int filterCallingUid, int userId);
+ @PackageManager.ComponentInfoFlagsBits long flags, int filterCallingUid, int userId);
/**
* Retrieve all activities that can be performed for the given intent.
@@ -354,22 +352,24 @@
* @see PackageManager#queryIntentActivities(Intent, int)
*/
public abstract List<ResolveInfo> queryIntentActivities(
- Intent intent, @Nullable String resolvedType, @ResolveInfoFlags long flags,
- int filterCallingUid, int userId);
+ Intent intent, @Nullable String resolvedType,
+ @PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId);
/**
* Retrieve all receivers that can handle a broadcast of the given intent.
*/
public abstract List<ResolveInfo> queryIntentReceivers(Intent intent,
- String resolvedType, @ResolveInfoFlags long flags, int filterCallingUid, int userId);
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
+ int filterCallingUid, int userId);
/**
* Retrieve all services that can be performed for the given intent.
* @see PackageManager#queryIntentServices(Intent, int)
*/
public abstract List<ResolveInfo> queryIntentServices(
- Intent intent, @ResolveInfoFlags long flags, int callingUid, int userId);
+ Intent intent, @PackageManager.ResolveInfoFlagsBits long flags, int callingUid,
+ int userId);
/**
* Interface to {@link com.android.server.pm.PackageManagerService#getHomeActivitiesAsUser}.
@@ -593,20 +593,21 @@
* Resolves an activity intent, allowing instant apps to be resolved.
*/
public abstract ResolveInfo resolveIntent(Intent intent, String resolvedType,
- @ResolveInfoFlags long flags, @PrivateResolveFlags long privateResolveFlags, int userId,
- boolean resolveForStart, int filterCallingUid);
+ @PackageManager.ResolveInfoFlagsBits long flags,
+ @PrivateResolveFlags long privateResolveFlags, int userId, boolean resolveForStart,
+ int filterCallingUid);
/**
* Resolves a service intent, allowing instant apps to be resolved.
*/
public abstract ResolveInfo resolveService(Intent intent, String resolvedType,
- @ResolveInfoFlags long flags, int userId, int callingUid);
+ @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid);
/**
* Resolves a content provider intent.
*/
- public abstract ProviderInfo resolveContentProvider(String name, @ComponentInfoFlags long flags,
- int userId, int callingUid);
+ public abstract ProviderInfo resolveContentProvider(String name,
+ @PackageManager.ComponentInfoFlagsBits long flags, int userId, int callingUid);
/**
* Track the creator of a new isolated uid.
@@ -878,7 +879,7 @@
/** Returns {@code true} if the specified component is enabled and matches the given flags. */
public abstract boolean isEnabledAndMatches(@NonNull ParsedMainComponent component,
- @ComponentInfoFlags long flags, int userId);
+ @PackageManager.ComponentInfoFlagsBits long flags, int userId);
/** Returns {@code true} if the given user requires extra badging for icons. */
public abstract boolean userNeedsBadging(int userId);
diff --git a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
index 263ff18..380b1f3 100644
--- a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
+++ b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
@@ -112,9 +112,9 @@
void handleAirplaneModeChange() {
if (shouldSkipAirplaneModeChange()) {
Log.i(TAG, "Ignore airplane mode change");
- // We have to store Bluetooth state here, so if user turns off Bluetooth
- // after airplane mode is turned on, we don't forget to turn on Bluetooth
- // when airplane mode turns off.
+ // Airplane mode enabled when Bluetooth is being used for audio/headering aid.
+ // Bluetooth is not disabled in such case, only state is changed to
+ // BLUETOOTH_ON_AIRPLANE mode.
mAirplaneHelper.setSettingsInt(Settings.Global.BLUETOOTH_ON,
BluetoothManagerService.BLUETOOTH_ON_AIRPLANE);
if (shouldPopToast()) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 8860a81..450e988 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1850,6 +1850,10 @@
mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
mEnable = true;
+ if (isBle == 0) {
+ persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
+ }
+
// Use service interface to get the exact state
try {
mBluetoothLock.readLock().lock();
@@ -1863,7 +1867,6 @@
} else {
Slog.w(TAG, "BT Enable in BLE_ON State, going to ON");
mBluetooth.onLeServiceUp(mContext.getAttributionSource());
- persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
}
break;
case BluetoothAdapter.STATE_BLE_TURNING_ON:
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 25b36e8..34c21f2 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -88,6 +88,20 @@
private static final int EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD = 5;
/**
+ * Default value of the power button "cooldown" period after the Emergency gesture is triggered.
+ * See {@link Settings.Secure#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS}
+ */
+ private static final int EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_DEFAULT = 3000;
+
+ /**
+ * Maximum value of the power button "cooldown" period after the Emergency gesture is triggered.
+ * The value read from {@link Settings.Secure#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS}
+ * is capped at this maximum.
+ */
+ @VisibleForTesting
+ static final int EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX = 5000;
+
+ /**
* Number of taps required to launch camera shortcut.
*/
private static final int CAMERA_POWER_TAP_COUNT_THRESHOLD = 2;
@@ -163,7 +177,14 @@
*/
private boolean mEmergencyGestureEnabled;
+ /**
+ * Power button cooldown period in milliseconds, after emergency gesture is triggered. A zero
+ * value means the cooldown period is disabled.
+ */
+ private int mEmergencyGesturePowerButtonCooldownPeriodMs;
+
private long mLastPowerDown;
+ private long mLastEmergencyGestureTriggered;
private int mPowerButtonConsecutiveTaps;
private int mPowerButtonSlowConsecutiveTaps;
private final UiEventLogger mUiEventLogger;
@@ -231,6 +252,7 @@
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
updateEmergencyGestureEnabled();
+ updateEmergencyGesturePowerButtonCooldownPeriodMs();
mUserId = ActivityManager.getCurrentUser();
mContext.registerReceiver(mUserReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED));
@@ -261,6 +283,10 @@
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.EMERGENCY_GESTURE_ENABLED),
false, mSettingObserver, mUserId);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS),
+ false, mSettingObserver, mUserId);
}
private void updateCameraRegistered() {
@@ -294,6 +320,14 @@
}
}
+ @VisibleForTesting
+ void updateEmergencyGesturePowerButtonCooldownPeriodMs() {
+ int cooldownPeriodMs = getEmergencyGesturePowerButtonCooldownPeriodMs(mContext, mUserId);
+ synchronized (this) {
+ mEmergencyGesturePowerButtonCooldownPeriodMs = cooldownPeriodMs;
+ }
+ }
+
private void unregisterCameraLaunchGesture() {
if (mCameraLaunchRegistered) {
mCameraLaunchRegistered = false;
@@ -428,6 +462,20 @@
}
/**
+ * Gets power button cooldown period in milliseconds after emergency gesture is triggered. The
+ * value is capped at a maximum
+ * {@link GestureLauncherService#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX}. If the
+ * value is zero, it means the cooldown period is disabled.
+ */
+ @VisibleForTesting
+ static int getEmergencyGesturePowerButtonCooldownPeriodMs(Context context, int userId) {
+ int cooldown = Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
+ EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_DEFAULT, userId);
+ return Math.min(cooldown, EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX);
+ }
+
+ /**
* Whether to enable the camera launch gesture.
*/
private static boolean isCameraLaunchEnabled(Resources resources) {
@@ -475,10 +523,24 @@
*/
public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive,
MutableBoolean outLaunched) {
+ if (mEmergencyGestureEnabled && mEmergencyGesturePowerButtonCooldownPeriodMs >= 0
+ && event.getEventTime() - mLastEmergencyGestureTriggered
+ < mEmergencyGesturePowerButtonCooldownPeriodMs) {
+ Slog.i(TAG, String.format(
+ "Suppressing power button: within %dms cooldown period after Emergency "
+ + "Gesture. Begin=%dms, end=%dms.",
+ mEmergencyGesturePowerButtonCooldownPeriodMs,
+ mLastEmergencyGestureTriggered,
+ mLastEmergencyGestureTriggered + mEmergencyGesturePowerButtonCooldownPeriodMs));
+ outLaunched.value = false;
+ return true;
+ }
+
if (event.isLongPress()) {
// Long presses are sent as a second key down. If the long press threshold is set lower
// than the double tap of sequence interval thresholds, this could cause false double
// taps or consecutive taps, so we want to ignore the long press event.
+ outLaunched.value = false;
return false;
}
boolean launchCamera = false;
@@ -542,6 +604,12 @@
Slog.i(TAG, "Emergency gesture detected, launching.");
launchEmergencyGesture = handleEmergencyGesture();
mUiEventLogger.log(GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER);
+ // Record emergency trigger time if emergency UI was launched
+ if (launchEmergencyGesture) {
+ synchronized (this) {
+ mLastEmergencyGestureTriggered = event.getEventTime();
+ }
+ }
}
mMetricsLogger.histogram("power_consecutive_short_tap_count",
mPowerButtonSlowConsecutiveTaps);
@@ -670,6 +738,7 @@
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
updateEmergencyGestureEnabled();
+ updateEmergencyGesturePowerButtonCooldownPeriodMs();
}
}
};
@@ -680,6 +749,7 @@
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
updateEmergencyGestureEnabled();
+ updateEmergencyGesturePowerButtonCooldownPeriodMs();
}
}
};
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 4775127..91cd2f6 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -152,6 +152,8 @@
int callerUid;
int callerPid;
+ boolean renounceFineLocationAccess;
+ boolean renounceCoarseLocationAccess;
Set<Integer> eventList;
@@ -995,14 +997,25 @@
}
@Override
- public void listenWithEventList(int subId, String callingPackage, String callingFeatureId,
- IPhoneStateListener callback, int[] events, boolean notifyNow) {
+ public void listenWithEventList(boolean renounceFineLocationAccess,
+ boolean renounceCoarseLocationAccess, int subId, String callingPackage,
+ String callingFeatureId, IPhoneStateListener callback,
+ int[] events, boolean notifyNow) {
Set<Integer> eventList = Arrays.stream(events).boxed().collect(Collectors.toSet());
- listen(callingPackage, callingFeatureId, callback, eventList, notifyNow, subId);
+ listen(renounceFineLocationAccess, renounceFineLocationAccess, callingPackage,
+ callingFeatureId, callback, eventList, notifyNow, subId);
}
private void listen(String callingPackage, @Nullable String callingFeatureId,
IPhoneStateListener callback, Set<Integer> events, boolean notifyNow, int subId) {
+ listen(false, false, callingPackage,
+ callingFeatureId, callback, events, notifyNow, subId);
+ }
+
+ private void listen(boolean renounceFineLocationAccess,
+ boolean renounceCoarseLocationAccess, String callingPackage,
+ @Nullable String callingFeatureId, IPhoneStateListener callback,
+ Set<Integer> events, boolean notifyNow, int subId) {
int callerUserId = UserHandle.getCallingUserId();
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
String str = "listen: E pkg=" + pii(callingPackage) + " uid=" + Binder.getCallingUid()
@@ -1047,6 +1060,8 @@
r.callback = callback;
r.callingPackage = callingPackage;
r.callingFeatureId = callingFeatureId;
+ r.renounceCoarseLocationAccess = renounceCoarseLocationAccess;
+ r.renounceFineLocationAccess = renounceFineLocationAccess;
r.callerUid = Binder.getCallingUid();
r.callerPid = Binder.getCallingPid();
// Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
@@ -3199,6 +3214,9 @@
* If you don't need app compat logic, use {@link #checkFineLocationAccess(Record)}.
*/
private boolean checkFineLocationAccess(Record r, int minSdk) {
+ if (r.renounceFineLocationAccess) {
+ return false;
+ }
LocationAccessPolicy.LocationPermissionQuery query =
new LocationAccessPolicy.LocationPermissionQuery.Builder()
.setCallingPackage(r.callingPackage)
@@ -3225,6 +3243,9 @@
* If you don't need app compat logic, use {@link #checkCoarseLocationAccess(Record)}.
*/
private boolean checkCoarseLocationAccess(Record r, int minSdk) {
+ if (r.renounceCoarseLocationAccess) {
+ return false;
+ }
LocationAccessPolicy.LocationPermissionQuery query =
new LocationAccessPolicy.LocationPermissionQuery.Builder()
.setCallingPackage(r.callingPackage)
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a33aa60..4727b16 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -93,6 +93,7 @@
import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
@@ -307,6 +308,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
+import android.util.FeatureFlagUtils;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
@@ -14598,6 +14600,8 @@
private void checkExcessivePowerUsage() {
updateCpuStatsNow();
+ final boolean monitorPhantomProcs = mSystemReady && FeatureFlagUtils.isEnabled(mContext,
+ SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
synchronized (mProcLock) {
final boolean doCpuKills = mLastPowerCheckUptime != 0;
final long curUptime = SystemClock.uptimeMillis();
@@ -14623,9 +14627,11 @@
updateAppProcessCpuTimeLPr(uptimeSince, doCpuKills, checkDur, cpuLimit, app);
- // Also check the phantom processes if there is any
- updatePhantomProcessCpuTimeLPr(
- uptimeSince, doCpuKills, checkDur, cpuLimit, app);
+ if (monitorPhantomProcs) {
+ // Also check the phantom processes if there is any
+ updatePhantomProcessCpuTimeLPr(
+ uptimeSince, doCpuKills, checkDur, cpuLimit, app);
+ }
}
});
}
@@ -15659,6 +15665,11 @@
}
}
+ @Override
+ public void enableBinderTracing() {
+ Binder.enableTracingForUid(Binder.getCallingUid());
+ }
+
@VisibleForTesting
public final class LocalService extends ActivityManagerInternal
implements ActivityManagerLocal {
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 221de8d..d6a4cf6 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -19,6 +19,7 @@
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.Process.FIRST_APPLICATION_UID;
+import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
@@ -77,6 +78,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.DebugUtils;
+import android.util.FeatureFlagUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -1805,6 +1807,8 @@
}
void updateCpuStatsNow() {
+ final boolean monitorPhantomProcs = mService.mSystemReady && FeatureFlagUtils.isEnabled(
+ mService.mContext, SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
synchronized (mProcessCpuTracker) {
mProcessCpuMutexFree.set(false);
final long now = SystemClock.uptimeMillis();
@@ -1843,7 +1847,7 @@
}
}
- if (haveNewCpuStats) {
+ if (monitorPhantomProcs && haveNewCpuStats) {
mService.mPhantomProcessList.updateProcessCpuStatesLocked(mProcessCpuTracker);
}
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
index b07684c..2ec1aed 100644
--- a/services/core/java/com/android/server/am/PhantomProcessList.java
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -18,6 +18,7 @@
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
+import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -28,6 +29,7 @@
import android.os.Handler;
import android.os.Process;
import android.os.StrictMode;
+import android.util.FeatureFlagUtils;
import android.util.Slog;
import android.util.SparseArray;
@@ -419,6 +421,10 @@
* order of the oom adjs of their parent process.
*/
void trimPhantomProcessesIfNecessary() {
+ if (!mService.mSystemReady || !FeatureFlagUtils.isEnabled(mService.mContext,
+ SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS)) {
+ return;
+ }
synchronized (mService.mProcLock) {
synchronized (mLock) {
mTrimPhantomProcessScheduled = false;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 1a89ae7..2def50e 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -371,6 +371,16 @@
private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
/**
+ * Native heap allocations in AppZygote process and its descendants will now have a
+ * non-zero tag in the most significant byte.
+ * @see <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged
+ * Pointers</a>
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S)
+ private static final long NATIVE_HEAP_POINTER_TAGGING_APP_ZYGOTE = 207557677;
+
+ /**
* Enable asynchronous (ASYNC) memory tag checking in this process. This
* flag will only have an effect on hardware supporting the ARM Memory
* Tagging Extension (MTE).
@@ -1741,6 +1751,16 @@
return level;
}
+ private int decideTaggingLevelForAppZygote(ProcessRecord app) {
+ int level = decideTaggingLevel(app);
+ // TBI ("fake" pointer tagging) in AppZygote is controlled by a separate compat feature.
+ if (!mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING_APP_ZYGOTE, app.info)
+ && level == Zygote.MEMORY_TAG_LEVEL_TBI) {
+ level = Zygote.MEMORY_TAG_LEVEL_NONE;
+ }
+ return level;
+ }
+
private int decideGwpAsanLevel(ProcessRecord app) {
// Look at the process attribute first.
if (app.processInfo != null
@@ -2271,7 +2291,7 @@
// not the calling one.
appInfo.packageName = app.getHostingRecord().getDefiningPackageName();
appInfo.uid = uid;
- int runtimeFlags = decideTaggingLevel(app);
+ int runtimeFlags = decideTaggingLevelForAppZygote(app);
appZygote = new AppZygote(appInfo, uid, firstUid, lastUid, runtimeFlags);
mAppZygotes.put(app.info.processName, uid, appZygote);
zygoteProcessList = new ArrayList<ProcessRecord>();
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index e79cba1..a7864b9 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -145,9 +145,11 @@
// giving up on them and unfreezing the screen.
static final int USER_SWITCH_TIMEOUT_MS = 3 * 1000;
- // Amount of time we wait for observers to handle a user switch before we log a warning.
- // Must be smaller than USER_SWITCH_TIMEOUT_MS.
- private static final int USER_SWITCH_WARNING_TIMEOUT_MS = 500;
+ /**
+ * Amount of time we wait for an observer to handle a user switch before we log a warning. This
+ * wait time is per observer.
+ */
+ private static final int LONG_USER_SWITCH_OBSERVER_WARNING_TIME_MS = 500;
// ActivityManager thread message constants
static final int REPORT_USER_SWITCH_MSG = 10;
@@ -1916,6 +1918,7 @@
final AtomicInteger waitingCallbacksCount = new AtomicInteger(observerCount);
final long dispatchStartedTime = SystemClock.elapsedRealtime();
for (int i = 0; i < observerCount; i++) {
+ final long dispatchStartedTimeForObserver = SystemClock.elapsedRealtime();
try {
// Prepend with unique prefix to guarantee that keys are unique
final String name = "#" + i + " " + mUserSwitchObservers.getBroadcastCookie(i);
@@ -1926,13 +1929,20 @@
@Override
public void sendResult(Bundle data) throws RemoteException {
synchronized (mLock) {
- long delay = SystemClock.elapsedRealtime() - dispatchStartedTime;
- if (delay > USER_SWITCH_TIMEOUT_MS) {
- Slogf.e(TAG, "User switch timeout: observer " + name
- + " sent result after " + delay + " ms");
- } else if (delay > USER_SWITCH_WARNING_TIMEOUT_MS) {
+ long delayForObserver = SystemClock.elapsedRealtime()
+ - dispatchStartedTimeForObserver;
+ if (delayForObserver > LONG_USER_SWITCH_OBSERVER_WARNING_TIME_MS) {
Slogf.w(TAG, "User switch slowed down by observer " + name
- + ": result sent after " + delay + " ms");
+ + ": result took " + delayForObserver
+ + " ms to process.");
+ }
+
+ long totalDelay = SystemClock.elapsedRealtime()
+ - dispatchStartedTime;
+ if (totalDelay > USER_SWITCH_TIMEOUT_MS) {
+ Slogf.e(TAG, "User switch timeout: observer " + name
+ + "'s result was received " + totalDelay
+ + " ms after dispatchUserSwitch.");
}
curWaitingUserSwitchCallbacks.remove(name);
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 20606f0..8dce8e9 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -385,9 +385,8 @@
}
public boolean isValid() {
- return (mGameMode == GameManager.GAME_MODE_PERFORMANCE
- || mGameMode == GameManager.GAME_MODE_BATTERY)
- && (!mAllowDownscale || getCompatChangeId() != 0);
+ return mGameMode == GameManager.GAME_MODE_PERFORMANCE
+ || mGameMode == GameManager.GAME_MODE_BATTERY;
}
/**
@@ -839,7 +838,7 @@
}
long scaleId = modeConfig.getCompatChangeId();
if (scaleId == 0) {
- Slog.w(TAG, "Invalid downscaling change id " + scaleId + " for "
+ Slog.i(TAG, "Invalid downscaling change id " + scaleId + " for "
+ packageName);
return;
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index e4ac7be..2fcdd61 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -327,7 +327,7 @@
}
boolean isBtScoRequested = isBluetoothScoRequested();
- if (isBtScoRequested && !wasBtScoRequested) {
+ if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive())) {
if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for pid: "
+ pid);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index aa33644..f857064 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -8252,6 +8252,9 @@
public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,
IAudioFocusDispatcher fd, String clientId, String callingPackageName,
String attributionTag, int flags, IAudioPolicyCallback pcb, int sdk) {
+ if ((flags & AudioManager.AUDIOFOCUS_FLAG_TEST) != 0) {
+ throw new IllegalArgumentException("Invalid test flag");
+ }
final int uid = Binder.getCallingUid();
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "focus")
.setUid(uid)
@@ -8310,7 +8313,7 @@
/** see {@link AudioManager#requestAudioFocusForTest(AudioFocusRequest, String, int, int)} */
public int requestAudioFocusForTest(AudioAttributes aa, int durationHint, IBinder cb,
IAudioFocusDispatcher fd, String clientId, String callingPackageName,
- int fakeUid, int sdk) {
+ int flags, int fakeUid, int sdk) {
if (!enforceQueryAudioStateForTest("focus request")) {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
@@ -8320,7 +8323,7 @@
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
- clientId, callingPackageName, null, AudioManager.AUDIOFOCUS_FLAG_TEST,
+ clientId, callingPackageName, null, flags,
sdk, false /*forceDuck*/, fakeUid);
}
@@ -10139,6 +10142,27 @@
return AudioManager.SUCCESS;
}
+ /** @see AudioPolicy#getFocusStack() */
+ public List<AudioFocusInfo> getFocusStack() {
+ enforceModifyAudioRoutingPermission();
+ return mMediaFocusControl.getFocusStack();
+ }
+
+ /** @see AudioPolicy#sendFocusLoss */
+ public boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser,
+ @NonNull IAudioPolicyCallback apcb) {
+ Objects.requireNonNull(focusLoser);
+ Objects.requireNonNull(apcb);
+ enforceModifyAudioRoutingPermission();
+ if (!mAudioPolicies.containsKey(apcb.asBinder())) {
+ throw new IllegalStateException("Only registered AudioPolicy can change focus");
+ }
+ if (!mAudioPolicies.get(apcb.asBinder()).mHasFocusListener) {
+ throw new IllegalStateException("AudioPolicy must have focus listener to change focus");
+ }
+ return mMediaFocusControl.sendFocusLoss(focusLoser);
+ }
+
/** see AudioManager.hasRegisteredDynamicPolicy */
public boolean hasRegisteredDynamicPolicy() {
synchronized (mAudioPolicies) {
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index bf49ac8..69a4c23 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -27,6 +27,7 @@
import android.media.AudioSystem;
import android.media.IAudioFocusDispatcher;
import android.media.MediaMetrics;
+import android.media.audiopolicy.AudioPolicy;
import android.media.audiopolicy.IAudioPolicyCallback;
import android.os.Binder;
import android.os.Build;
@@ -221,6 +222,51 @@
}
}
+ /**
+ * Return a copy of the focus stack for external consumption (composed of AudioFocusInfo
+ * instead of FocusRequester instances)
+ * @return a SystemApi-friendly version of the focus stack, in the same order (last entry
+ * is top of focus stack, i.e. latest focus owner)
+ * @see AudioPolicy#getFocusStack()
+ */
+ @NonNull List<AudioFocusInfo> getFocusStack() {
+ synchronized (mAudioFocusLock) {
+ final ArrayList<AudioFocusInfo> stack = new ArrayList<>(mFocusStack.size());
+ for (FocusRequester fr : mFocusStack) {
+ stack.add(fr.toAudioFocusInfo());
+ }
+ return stack;
+ }
+ }
+
+ /**
+ * Send AUDIOFOCUS_LOSS to a specific stack entry.
+ * Note this method is supporting an external API, and is restricted to LOSS in order to
+ * prevent allowing the stack to be in an invalid state (e.g. entry inside stack has focus)
+ * @param focusLoser the stack entry that is exiting the stack through a focus loss
+ * @return false if the focusLoser wasn't found in the stack, true otherwise
+ * @see AudioPolicy#sendFocusLoss(AudioFocusInfo)
+ */
+ boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser) {
+ synchronized (mAudioFocusLock) {
+ FocusRequester loserToRemove = null;
+ for (FocusRequester fr : mFocusStack) {
+ if (fr.getClientId().equals(focusLoser.getClientId())) {
+ fr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null,
+ false /*forceDuck*/);
+ loserToRemove = fr;
+ break;
+ }
+ }
+ if (loserToRemove != null) {
+ mFocusStack.remove(loserToRemove);
+ loserToRemove.release();
+ return true;
+ }
+ }
+ return false;
+ }
+
@GuardedBy("mAudioFocusLock")
private void notifyTopOfAudioFocusStack() {
// notify the top of the stack it gained focus
@@ -461,13 +507,19 @@
* at the top of the focus stack
* Push the focus requester onto the audio focus stack at the first position immediately
* following the locked focus owners.
+ * Propagate through the stack the changes that the new (future) focus owner causes.
+ * @param nfr the future focus owner that will gain focus when the locked focus owners are
+ * removed from the stack
* @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or
* {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED}
*/
@GuardedBy("mAudioFocusLock")
- private int pushBelowLockedFocusOwners(FocusRequester nfr) {
+ private int pushBelowLockedFocusOwnersAndPropagate(FocusRequester nfr) {
+ if (DEBUG) {
+ Log.v(TAG, "pushBelowLockedFocusOwnersAndPropagate client=" + nfr.getClientId());
+ }
int lastLockedFocusOwnerIndex = mFocusStack.size();
- for (int index = mFocusStack.size()-1; index >= 0; index--) {
+ for (int index = mFocusStack.size() - 1; index >= 0; index--) {
if (isLockedFocusOwner(mFocusStack.elementAt(index))) {
lastLockedFocusOwnerIndex = index;
}
@@ -480,10 +532,33 @@
propagateFocusLossFromGain_syncAf(nfr.getGainRequest(), nfr, false /*forceDuck*/);
mFocusStack.push(nfr);
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
- } else {
- mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex);
- return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
}
+
+ if (DEBUG) {
+ Log.v(TAG, "> lastLockedFocusOwnerIndex=" + lastLockedFocusOwnerIndex);
+ }
+ mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex);
+
+ // propagate potential focus loss (and removal from stack) after the newly
+ // inserted FocusRequester (at index lastLockedFocusOwnerIndex-1)
+ final List<String> clientsToRemove = new LinkedList<String>();
+ for (int index = lastLockedFocusOwnerIndex - 1; index >= 0; index--) {
+ final boolean isDefinitiveLoss =
+ mFocusStack.elementAt(index).handleFocusLossFromGain(
+ nfr.getGainRequest(), nfr, false /*forceDuck*/);
+ if (isDefinitiveLoss) {
+ clientsToRemove.add(mFocusStack.elementAt(index).getClientId());
+ }
+ }
+ for (String clientToRemove : clientsToRemove) {
+ if (DEBUG) {
+ Log.v(TAG, "> removing focus client " + clientToRemove);
+ }
+ removeFocusStackEntry(clientToRemove, false /*signal*/,
+ true /*notifyFocusFollowers*/);
+ }
+
+ return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
}
/**
@@ -868,7 +943,7 @@
* @param forceDuck only true if
* {@link android.media.AudioFocusRequest.Builder#setFocusGain(int)} was set to true for
* accessibility.
- * @param testUid ignored if flags is not AudioManager.AUDIOFOCUS_FLAG_TEST (strictly equals to)
+ * @param testUid ignored if flags doesn't contain AudioManager.AUDIOFOCUS_FLAG_TEST
* otherwise the UID being injected for testing
* @return
*/
@@ -1029,7 +1104,7 @@
if (focusGrantDelayed) {
// focusGrantDelayed being true implies we can't reassign focus right now
// which implies the focus stack is not empty.
- final int requestResult = pushBelowLockedFocusOwners(nfr);
+ final int requestResult = pushBelowLockedFocusOwnersAndPropagate(nfr);
if (requestResult != AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), requestResult);
}
diff --git a/services/core/java/com/android/server/biometrics/OWNERS b/services/core/java/com/android/server/biometrics/OWNERS
index 4eac972..f05f403 100644
--- a/services/core/java/com/android/server/biometrics/OWNERS
+++ b/services/core/java/com/android/server/biometrics/OWNERS
@@ -6,3 +6,4 @@
ilyamaty@google.com
joshmccloskey@google.com
jbolinger@google.com
+graciecheng@google.com
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 7341e74..358263d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -503,10 +503,14 @@
protected int getShowOverlayReason() {
if (isKeyguard()) {
return BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
- } else if (isSettings()) {
- return BiometricOverlayConstants.REASON_AUTH_SETTINGS;
} else if (isBiometricPrompt()) {
+ // BP reason always takes precedent over settings, since callers from within
+ // settings can always invoke BP.
return BiometricOverlayConstants.REASON_AUTH_BP;
+ } else if (isSettings()) {
+ // This is pretty much only for FingerprintManager#authenticate usage from
+ // FingerprintSettings.
+ return BiometricOverlayConstants.REASON_AUTH_SETTINGS;
} else {
return BiometricOverlayConstants.REASON_AUTH_OTHER;
}
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 5ce72c2..3120dc5 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -25,7 +25,6 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.compat.CompatChanges;
-import android.app.TaskStackListener;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.Overridable;
@@ -35,6 +34,7 @@
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.hardware.CameraSessionStats;
import android.hardware.CameraStreamStats;
@@ -84,7 +84,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -309,8 +308,6 @@
private final DisplayWindowListener mDisplayWindowListener = new DisplayWindowListener();
- private final TaskStateHandler mTaskStackListener = new TaskStateHandler();
-
public static final class TaskInfo {
public int frontTaskId;
public boolean isResizeable;
@@ -320,54 +317,6 @@
public int userId;
}
- private final class TaskStateHandler extends TaskStackListener {
- private final Object mMapLock = new Object();
-
- // maps the package name to its corresponding current top level task id
- @GuardedBy("mMapLock")
- private final ArrayMap<String, TaskInfo> mTaskInfoMap = new ArrayMap<>();
-
- @Override
- public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
- synchronized (mMapLock) {
- TaskInfo info = new TaskInfo();
- info.frontTaskId = taskInfo.taskId;
- info.isResizeable =
- (taskInfo.topActivityInfo.resizeMode != RESIZE_MODE_UNRESIZEABLE);
- info.displayId = taskInfo.displayId;
- info.userId = taskInfo.userId;
- info.isFixedOrientationLandscape = ActivityInfo.isFixedOrientationLandscape(
- taskInfo.topActivityInfo.screenOrientation);
- info.isFixedOrientationPortrait = ActivityInfo.isFixedOrientationPortrait(
- taskInfo.topActivityInfo.screenOrientation);
- mTaskInfoMap.put(taskInfo.topActivityInfo.packageName, info);
- }
- }
-
- @Override
- public void onTaskRemoved(int taskId) {
- synchronized (mMapLock) {
- for (Map.Entry<String, TaskInfo> entry : mTaskInfoMap.entrySet()){
- if (entry.getValue().frontTaskId == taskId) {
- mTaskInfoMap.remove(entry.getKey());
- break;
- }
- }
- }
- }
-
- public @Nullable TaskInfo getFrontTaskInfo(String packageName) {
- synchronized (mMapLock) {
- if (mTaskInfoMap.containsKey(packageName)) {
- return mTaskInfoMap.get(packageName);
- }
- }
-
- Log.e(TAG, "Top task with package name: " + packageName + " not found!");
- return null;
- }
- }
-
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -490,18 +439,53 @@
private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() {
@Override
- public int getRotateAndCropOverride(String packageName, int lensFacing) {
+ public int getRotateAndCropOverride(String packageName, int lensFacing, int userId) {
if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " +
" camera service UID!");
return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
}
+ TaskInfo taskInfo = null;
+ ParceledListSlice<ActivityManager.RecentTaskInfo> recentTasks = null;
+
+ try {
+ recentTasks = ActivityTaskManager.getService().getRecentTasks(/*maxNum*/1,
+ /*flags*/ 0, userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to query recent tasks!");
+ return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+ }
+
+ if ((recentTasks != null) && (!recentTasks.getList().isEmpty())) {
+ ActivityManager.RecentTaskInfo task = recentTasks.getList().get(0);
+ if (packageName.equals(task.topActivityInfo.packageName)) {
+ taskInfo = new TaskInfo();
+ taskInfo.frontTaskId = task.taskId;
+ taskInfo.isResizeable =
+ (task.topActivityInfo.resizeMode != RESIZE_MODE_UNRESIZEABLE);
+ taskInfo.displayId = task.displayId;
+ taskInfo.userId = task.userId;
+ taskInfo.isFixedOrientationLandscape =
+ ActivityInfo.isFixedOrientationLandscape(
+ task.topActivityInfo.screenOrientation);
+ taskInfo.isFixedOrientationPortrait =
+ ActivityInfo.isFixedOrientationPortrait(
+ task.topActivityInfo.screenOrientation);
+ } else {
+ Log.e(TAG, "Recent task package name: " + task.topActivityInfo.packageName
+ + " doesn't match with camera client package name: " + packageName);
+ return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+ }
+ } else {
+ Log.e(TAG, "Recent task list is empty!");
+ return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+ }
+
// TODO: Modify the sensor orientation in camera characteristics along with any 3A
// regions in capture requests/results to account for thea physical rotation. The
// former is somewhat tricky as it assumes that camera clients always check for the
// current value by retrieving the camera characteristics from the camera device.
- TaskInfo taskInfo = mTaskStackListener.getFrontTaskInfo(packageName);
if ((taskInfo != null) && (CompatChanges.isChangeEnabled(
OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS, packageName,
UserHandle.getUserHandleForUid(taskInfo.userId)))) {
@@ -673,12 +657,6 @@
CameraStatsJobService.schedule(mContext);
try {
- ActivityTaskManager.getService().registerTaskStackListener(mTaskStackListener);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to register task stack listener!");
- }
-
- try {
int[] displayIds = WindowManagerGlobal.getWindowManagerService()
.registerDisplayWindowListener(mDisplayWindowListener);
for (int i = 0; i < displayIds.length; i++) {
diff --git a/services/core/java/com/android/server/communal/CommunalManagerService.java b/services/core/java/com/android/server/communal/CommunalManagerService.java
index 8d9b13e..3bf6ca2 100644
--- a/services/core/java/com/android/server/communal/CommunalManagerService.java
+++ b/services/core/java/com/android/server/communal/CommunalManagerService.java
@@ -30,6 +30,7 @@
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.communal.ICommunalManager;
+import android.app.communal.ICommunalModeListener;
import android.app.compat.CompatChanges;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -42,6 +43,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
@@ -77,6 +80,8 @@
private final PackageReceiver mPackageReceiver;
private final PackageManager mPackageManager;
private final DreamManagerInternal mDreamManagerInternal;
+ private final RemoteCallbackList<ICommunalModeListener> mListeners =
+ new RemoteCallbackList<>();
private final ActivityInterceptorCallback mActivityInterceptorCallback =
new ActivityInterceptorCallback() {
@@ -129,7 +134,7 @@
@Override
public void onStart() {
- publishBinderService(Context.COMMUNAL_MANAGER_SERVICE, mBinderService);
+ publishBinderService(Context.COMMUNAL_SERVICE, mBinderService);
}
@Override
@@ -242,6 +247,27 @@
return !isAppAllowed(appInfo);
}
+ private void dispatchCommunalMode(boolean isShowing) {
+ synchronized (mListeners) {
+ int i = mListeners.beginBroadcast();
+ while (i > 0) {
+ i--;
+ try {
+ mListeners.getBroadcastItem(i).onCommunalModeChanged(isShowing);
+ } catch (RemoteException e) {
+ // Handled by the RemoteCallbackList.
+ }
+ }
+ mListeners.finishBroadcast();
+ }
+ }
+
+ private void enforceReadPermission() {
+ mContext.enforceCallingPermission(Manifest.permission.READ_COMMUNAL_STATE,
+ Manifest.permission.READ_COMMUNAL_STATE
+ + "permission required to read communal state.");
+ }
+
private final class BinderService extends ICommunalManager.Stub {
/**
* Sets whether or not we are in communal mode.
@@ -252,7 +278,43 @@
mContext.enforceCallingPermission(Manifest.permission.WRITE_COMMUNAL_STATE,
Manifest.permission.WRITE_COMMUNAL_STATE
+ "permission required to modify communal state.");
+ if (mCommunalViewIsShowing.get() == isShowing) {
+ return;
+ }
mCommunalViewIsShowing.set(isShowing);
+ dispatchCommunalMode(isShowing);
+ }
+
+ /**
+ * Checks whether or not we are in communal mode.
+ */
+ @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
+ @Override
+ public boolean isCommunalMode() {
+ enforceReadPermission();
+ return mCommunalViewIsShowing.get();
+ }
+
+ /**
+ * Adds a callback to execute when communal state changes.
+ */
+ @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
+ public void addCommunalModeListener(ICommunalModeListener listener) {
+ enforceReadPermission();
+ synchronized (mListeners) {
+ mListeners.register(listener);
+ }
+ }
+
+ /**
+ * Removes an added callback that execute when communal state changes.
+ */
+ @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
+ public void removeCommunalModeListener(ICommunalModeListener listener) {
+ enforceReadPermission();
+ synchronized (mListeners) {
+ mListeners.unregister(listener);
+ }
}
}
diff --git a/services/core/java/com/android/server/communal/OWNERS b/services/core/java/com/android/server/communal/OWNERS
index e499b160..b02883d 100644
--- a/services/core/java/com/android/server/communal/OWNERS
+++ b/services/core/java/com/android/server/communal/OWNERS
@@ -1,3 +1,4 @@
brycelee@google.com
justinkoh@google.com
-lusilva@google.com
\ No newline at end of file
+lusilva@google.com
+xilei@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
new file mode 100644
index 0000000..39fa3f2
--- /dev/null
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+package com.android.server.companion.virtual;
+
+import android.companion.virtual.IVirtualDevice;
+import android.window.DisplayWindowPolicyController;
+
+/**
+ * Virtual device manager local service interface.
+ * Only for use within system server.
+ */
+public abstract class VirtualDeviceManagerInternal {
+
+ /**
+ * Validate the virtual device.
+ */
+ public abstract boolean isValidVirtualDevice(IVirtualDevice virtualDevice);
+
+ /**
+ * Notify a virtual display is created.
+ *
+ * @param virtualDevice The virtual device where the virtual display located.
+ * @param displayId The display id of the created virtual display.
+ *
+ * @return The {@link DisplayWindowPolicyController} of the virtual device.
+ */
+ public abstract DisplayWindowPolicyController onVirtualDisplayCreated(
+ IVirtualDevice virtualDevice, int displayId);
+
+ /**
+ * Notify a virtual display is removed.
+ *
+ * @param virtualDevice The virtual device where the virtual display located.
+ * @param displayId The display id of the removed virtual display.
+ */
+ public abstract void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId);
+
+ /**
+ * Returns true if the given {@code uid} is the owner of any virtual devices that are
+ * currently active.
+ */
+ public abstract boolean isAppOwnerOfAnyVirtualDevice(int uid);
+
+ /**
+ * Returns true if the given {@code uid} is currently running on any virtual devices. This is
+ * determined by whether the app has any activities in the task stack on a virtual-device-owned
+ * display.
+ */
+ public abstract boolean isAppRunningOnAnyVirtualDevice(int uid);
+}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index e9af6011..c04032f 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -87,7 +87,10 @@
private final Sensor mLightSensor;
// The mapper to translate ambient lux to screen brightness in the range [0, 1.0].
- private final BrightnessMappingStrategy mBrightnessMapper;
+ @Nullable
+ private BrightnessMappingStrategy mCurrentBrightnessMapper;
+ private final BrightnessMappingStrategy mInteractiveModeBrightnessMapper;
+ private final BrightnessMappingStrategy mIdleModeBrightnessMapper;
// The minimum and maximum screen brightnesses.
private final float mScreenBrightnessRangeMinimum;
@@ -215,36 +218,41 @@
private final Injector mInjector;
AutomaticBrightnessController(Callbacks callbacks, Looper looper,
- SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper,
+ SensorManager sensorManager, Sensor lightSensor,
+ BrightnessMappingStrategy interactiveModeBrightnessMapper,
int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds, Context context,
- HighBrightnessModeController hbmController) {
- this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper,
+ HighBrightnessModeController hbmController,
+ BrightnessMappingStrategy idleModeBrightnessMapper) {
+ this(new Injector(), callbacks, looper, sensorManager, lightSensor,
+ interactiveModeBrightnessMapper,
lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
ambientBrightnessThresholds, screenBrightnessThresholds, context,
- hbmController
+ hbmController, idleModeBrightnessMapper
);
}
@VisibleForTesting
AutomaticBrightnessController(Injector injector, Callbacks callbacks, Looper looper,
- SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper,
+ SensorManager sensorManager, Sensor lightSensor,
+ BrightnessMappingStrategy interactiveModeBrightnessMapper,
int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds, Context context,
- HighBrightnessModeController hbmController) {
+ HighBrightnessModeController hbmController,
+ BrightnessMappingStrategy idleModeBrightnessMapper) {
mInjector = injector;
mContext = context;
mCallbacks = callbacks;
mSensorManager = sensorManager;
- mBrightnessMapper = mapper;
+ mCurrentBrightnessMapper = interactiveModeBrightnessMapper;
mScreenBrightnessRangeMinimum = brightnessMin;
mScreenBrightnessRangeMaximum = brightnessMax;
mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
@@ -277,6 +285,10 @@
mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
mHbmController = hbmController;
+ mInteractiveModeBrightnessMapper = interactiveModeBrightnessMapper;
+ mIdleModeBrightnessMapper = idleModeBrightnessMapper;
+ // Initialize to active (normal) screen brightness mode
+ switchToInteractiveScreenBrightnessMode();
}
/**
@@ -291,7 +303,12 @@
if (mLoggingEnabled == loggingEnabled) {
return false;
}
- mBrightnessMapper.setLoggingEnabled(loggingEnabled);
+ if (mInteractiveModeBrightnessMapper != null) {
+ mInteractiveModeBrightnessMapper.setLoggingEnabled(loggingEnabled);
+ }
+ if (mIdleModeBrightnessMapper != null) {
+ mIdleModeBrightnessMapper.setLoggingEnabled(loggingEnabled);
+ }
mLoggingEnabled = loggingEnabled;
return true;
}
@@ -311,7 +328,7 @@
}
public float getAutomaticScreenBrightnessAdjustment() {
- return mBrightnessMapper.getAutoBrightnessAdjustment();
+ return mCurrentBrightnessMapper.getAutoBrightnessAdjustment();
}
public void configure(boolean enable, @Nullable BrightnessConfiguration configuration,
@@ -350,15 +367,20 @@
}
public boolean hasUserDataPoints() {
- return mBrightnessMapper.hasUserDataPoints();
+ return mCurrentBrightnessMapper.hasUserDataPoints();
}
+ // Used internally to establish whether we have deviated from the default config.
public boolean isDefaultConfig() {
- return mBrightnessMapper.isDefaultConfig();
+ if (isInIdleMode()) {
+ return false;
+ }
+ return mInteractiveModeBrightnessMapper.isDefaultConfig();
}
+ // Called from APIs to get the configuration.
public BrightnessConfiguration getDefaultConfig() {
- return mBrightnessMapper.getDefaultConfig();
+ return mInteractiveModeBrightnessMapper.getDefaultConfig();
}
/**
@@ -379,7 +401,7 @@
}
if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) {
mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_SHORT_TERM_MODEL,
- mBrightnessMapper.getShortTermModelTimeout());
+ mCurrentBrightnessMapper.getShortTermModelTimeout());
} else if (isInteractivePolicy(policy) && !isInteractivePolicy(oldPolicy)) {
mHandler.removeMessages(MSG_INVALIDATE_SHORT_TERM_MODEL);
}
@@ -398,7 +420,7 @@
// and we can't use this data to add a new control point to the short-term model.
return false;
}
- mBrightnessMapper.addUserDataPoint(mAmbientLux, brightness);
+ mCurrentBrightnessMapper.addUserDataPoint(mAmbientLux, brightness);
mShortTermModelValid = true;
mShortTermModelAnchor = mAmbientLux;
if (mLoggingEnabled) {
@@ -408,7 +430,7 @@
}
public void resetShortTermModel() {
- mBrightnessMapper.clearUserDataPoints();
+ mCurrentBrightnessMapper.clearUserDataPoints();
mShortTermModelValid = true;
mShortTermModelAnchor = -1;
}
@@ -421,13 +443,19 @@
}
public boolean setBrightnessConfiguration(BrightnessConfiguration configuration) {
- if (mBrightnessMapper.setBrightnessConfiguration(configuration)) {
- resetShortTermModel();
+ if (mInteractiveModeBrightnessMapper.setBrightnessConfiguration(configuration)) {
+ if (!isInIdleMode()) {
+ resetShortTermModel();
+ }
return true;
}
return false;
}
+ public boolean isInIdleMode() {
+ return mCurrentBrightnessMapper.isForIdleMode();
+ }
+
public void dump(PrintWriter pw) {
pw.println();
pw.println("Automatic Brightness Controller Configuration:");
@@ -461,7 +489,12 @@
pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
pw.println(" mDisplayPolicy=" + DisplayPowerRequest.policyToString(mDisplayPolicy));
- pw.println(" mShortTermModelTimeout=" + mBrightnessMapper.getShortTermModelTimeout());
+ pw.println(" mShortTermModelTimeout(active)="
+ + mInteractiveModeBrightnessMapper.getShortTermModelTimeout());
+ if (mIdleModeBrightnessMapper != null) {
+ pw.println(" mShortTermModelTimeout(idle)="
+ + mIdleModeBrightnessMapper.getShortTermModelTimeout());
+ }
pw.println(" mShortTermModelAnchor=" + mShortTermModelAnchor);
pw.println(" mShortTermModelValid=" + mShortTermModelValid);
pw.println(" mBrightnessAdjustmentSamplePending=" + mBrightnessAdjustmentSamplePending);
@@ -472,9 +505,15 @@
pw.println(" mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
pw.println(" mForegroundAppCategory=" + mForegroundAppCategory);
pw.println(" mPendingForegroundAppCategory=" + mPendingForegroundAppCategory);
+ pw.println(" Idle mode active=" + mCurrentBrightnessMapper.isForIdleMode());
pw.println();
- mBrightnessMapper.dump(pw);
+ pw.println(" mActiveMapper=");
+ mInteractiveModeBrightnessMapper.dump(pw);
+ if (mIdleModeBrightnessMapper != null) {
+ pw.println(" mIdleMapper=");
+ mIdleModeBrightnessMapper.dump(pw);
+ }
pw.println();
mAmbientBrightnessThresholds.dump(pw);
@@ -544,7 +583,7 @@
}
private boolean setAutoBrightnessAdjustment(float adjustment) {
- return mBrightnessMapper.setAutoBrightnessAdjustment(adjustment);
+ return mCurrentBrightnessMapper.setAutoBrightnessAdjustment(adjustment);
}
private void setAmbientLux(float lux) {
@@ -562,7 +601,8 @@
// If the short term model was invalidated and the change is drastic enough, reset it.
if (!mShortTermModelValid && mShortTermModelAnchor != -1) {
- if (mBrightnessMapper.shouldResetShortTermModel(mAmbientLux, mShortTermModelAnchor)) {
+ if (mCurrentBrightnessMapper.shouldResetShortTermModel(
+ mAmbientLux, mShortTermModelAnchor)) {
resetShortTermModel();
} else {
mShortTermModelValid = true;
@@ -743,7 +783,7 @@
return;
}
- float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
+ float value = mCurrentBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
mForegroundAppCategory);
float newScreenAutoBrightness = clampScreenBrightness(value);
@@ -909,6 +949,41 @@
updateAutoBrightness(true /* sendUpdate */, false /* isManuallySet */);
}
+ void switchToIdleMode() {
+ if (mIdleModeBrightnessMapper == null) {
+ return;
+ }
+ if (mCurrentBrightnessMapper.isForIdleMode()) {
+ return;
+ }
+ Slog.i(TAG, "Switching to Idle Screen Brightness Mode");
+ mCurrentBrightnessMapper = mIdleModeBrightnessMapper;
+ resetShortTermModel();
+ update();
+ }
+
+ void switchToInteractiveScreenBrightnessMode() {
+ if (!mCurrentBrightnessMapper.isForIdleMode()) {
+ return;
+ }
+ Slog.i(TAG, "Switching to Interactive Screen Brightness Mode");
+ mCurrentBrightnessMapper = mInteractiveModeBrightnessMapper;
+ resetShortTermModel();
+ update();
+ }
+
+ public float convertToNits(float brightness) {
+ if (mCurrentBrightnessMapper != null) {
+ return mCurrentBrightnessMapper.convertToNits(brightness);
+ } else {
+ return -1.0f;
+ }
+ }
+
+ public void recalculateSplines(boolean applyAdjustment, float[] adjustment) {
+ mCurrentBrightnessMapper.recalculateSplines(applyAdjustment, adjustment);
+ }
+
private final class AutomaticBrightnessHandler extends Handler {
public AutomaticBrightnessHandler(Looper looper) {
super(looper, null, true /*async*/);
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index beb4d5b..240168b 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -135,7 +135,7 @@
builder.setShortTermModelLowerLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
return new PhysicalMappingStrategy(builder.build(), nitsRange, brightnessRange,
- autoBrightnessAdjustmentMaxGamma);
+ autoBrightnessAdjustmentMaxGamma, isForIdleMode);
} else if (isValidMapping(luxLevels, brightnessLevelsBacklight) && !isForIdleMode) {
return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight,
autoBrightnessAdjustmentMaxGamma, shortTermModelTimeout);
@@ -355,6 +355,12 @@
public abstract void dump(PrintWriter pw);
/**
+ * We can designate a mapping strategy to be used for idle screen brightness mode.
+ * @return whether this mapping strategy is to be used for idle screen brightness mode.
+ */
+ public abstract boolean isForIdleMode();
+
+ /**
* Check if the short term model should be reset given the anchor lux the last
* brightness change was made at and the current ambient lux.
*/
@@ -711,6 +717,11 @@
pw.println(" mUserBrightness=" + mUserBrightness);
}
+ @Override
+ public boolean isForIdleMode() {
+ return false;
+ }
+
private void computeSpline() {
Pair<float[], float[]> curve = getAdjustedCurve(mLux, mBrightness, mUserLux,
mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma);
@@ -758,9 +769,10 @@
private float mAutoBrightnessAdjustment;
private float mUserLux;
private float mUserBrightness;
+ private final boolean mIsForIdleMode;
public PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits,
- float[] brightness, float maxGamma) {
+ float[] brightness, float maxGamma, boolean isForIdleMode) {
Preconditions.checkArgument(nits.length != 0 && brightness.length != 0,
"Nits and brightness arrays must not be empty!");
@@ -772,6 +784,7 @@
Preconditions.checkArrayElementsInRange(brightness,
PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, "brightness");
+ mIsForIdleMode = isForIdleMode;
mMaxGamma = maxGamma;
mAutoBrightnessAdjustment = 0;
mUserLux = -1;
@@ -933,6 +946,11 @@
pw.println(" mBrightnessRangeAdjustmentApplied=" + mBrightnessRangeAdjustmentApplied);
}
+ @Override
+ public boolean isForIdleMode() {
+ return mIsForIdleMode;
+ }
+
private void computeNitsBrightnessSplines(float[] nits) {
mNitsToBrightnessSpline = Spline.createSpline(nits, mBrightness);
mBrightnessToNitsSpline = Spline.createSpline(mBrightness, nits);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 77ab813..3f55848 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -42,6 +42,7 @@
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.compat.CompatChanges;
+import android.companion.virtual.IVirtualDevice;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.Context;
@@ -126,6 +127,7 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.display.DisplayDeviceConfig.SensorData;
import com.android.server.display.utils.SensorUtils;
import com.android.server.wm.SurfaceAnimationThread;
@@ -244,9 +246,12 @@
public final SparseArray<CallbackRecord> mCallbacks =
new SparseArray<CallbackRecord>();
- /** All {@link DisplayWindowPolicyController}s indexed by {@link DisplayInfo#displayId}. */
- final SparseArray<DisplayWindowPolicyController> mDisplayWindowPolicyController =
- new SparseArray<>();
+ /**
+ * All {@link IVirtualDevice} and {@link DisplayWindowPolicyController}s indexed by
+ * {@link DisplayInfo#displayId}.
+ */
+ final SparseArray<Pair<IVirtualDevice, DisplayWindowPolicyController>>
+ mDisplayWindowPolicyControllers = new SparseArray<>();
// List of all currently registered display adapters.
private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
@@ -1180,8 +1185,8 @@
}
private int createVirtualDisplayInternal(VirtualDisplayConfig virtualDisplayConfig,
- IVirtualDisplayCallback callback, IMediaProjection projection, String packageName,
- DisplayWindowPolicyController controller) {
+ IVirtualDisplayCallback callback, IMediaProjection projection,
+ IVirtualDevice virtualDevice, String packageName) {
final int callingUid = Binder.getCallingUid();
if (!validatePackageName(callingUid, packageName)) {
throw new SecurityException("packageName must match the calling uid");
@@ -1226,6 +1231,14 @@
}
}
+ if (virtualDevice != null) {
+ final VirtualDeviceManagerInternal vdm =
+ getLocalService(VirtualDeviceManagerInternal.class);
+ if (!vdm.isValidVirtualDevice(virtualDevice)) {
+ throw new SecurityException("Invalid virtual device");
+ }
+ }
+
if (callingUid != Process.SYSTEM_UID
&& (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
if (!canProjectVideo(projection)) {
@@ -1306,8 +1319,8 @@
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
- return createVirtualDisplayLocked(callback, projection, callingUid, packageName,
- surface, flags, virtualDisplayConfig, controller);
+ return createVirtualDisplayLocked(callback, projection, virtualDevice, callingUid,
+ packageName, surface, flags, virtualDisplayConfig);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -1315,9 +1328,9 @@
}
private int createVirtualDisplayLocked(IVirtualDisplayCallback callback,
- IMediaProjection projection, int callingUid, String packageName, Surface surface,
- int flags, VirtualDisplayConfig virtualDisplayConfig,
- DisplayWindowPolicyController controller) {
+ IMediaProjection projection, IVirtualDevice virtualDevice,
+ int callingUid, String packageName, Surface surface,
+ int flags, VirtualDisplayConfig virtualDisplayConfig) {
if (mVirtualDisplayAdapter == null) {
Slog.w(TAG, "Rejecting request to create private virtual display "
+ "because the virtual display adapter is not available.");
@@ -1344,10 +1357,16 @@
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
if (display != null) {
- if (controller != null) {
- mDisplayWindowPolicyController.put(display.getDisplayIdLocked(), controller);
+ final int displayId = display.getDisplayIdLocked();
+ if (virtualDevice != null) {
+ final VirtualDeviceManagerInternal vdm =
+ getLocalService(VirtualDeviceManagerInternal.class);
+ final DisplayWindowPolicyController controller =
+ vdm.onVirtualDisplayCreated(virtualDevice, displayId);
+ mDisplayWindowPolicyControllers.put(displayId,
+ Pair.create(virtualDevice, controller));
}
- return display.getDisplayIdLocked();
+ return displayId;
}
// Something weird happened and the logical display was not created.
@@ -1391,7 +1410,13 @@
if (device != null) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
if (display != null) {
- mDisplayWindowPolicyController.delete(display.getDisplayIdLocked());
+ final int displayId = display.getDisplayIdLocked();
+ if (mDisplayWindowPolicyControllers.contains(displayId)) {
+ Pair<IVirtualDevice, DisplayWindowPolicyController> pair =
+ mDisplayWindowPolicyControllers.removeReturnOld(displayId);
+ getLocalService(VirtualDeviceManagerInternal.class)
+ .onVirtualDisplayRemoved(pair.first, displayId);
+ }
}
// TODO: multi-display - handle virtual displays the same as other display adapters.
mDisplayDeviceRepo.onDisplayDeviceEvent(device,
@@ -2345,13 +2370,13 @@
pw.println();
mPersistentDataStore.dump(pw);
- final int displayWindowPolicyControllerCount = mDisplayWindowPolicyController.size();
+ final int displayWindowPolicyControllerCount = mDisplayWindowPolicyControllers.size();
pw.println();
pw.println("Display Window Policy Controllers: size="
+ displayWindowPolicyControllerCount);
for (int i = 0; i < displayWindowPolicyControllerCount; i++) {
- pw.print("Display " + mDisplayWindowPolicyController.keyAt(i) + ":");
- mDisplayWindowPolicyController.valueAt(i).dump(" ", pw);
+ pw.print("Display " + mDisplayWindowPolicyControllers.keyAt(i) + ":");
+ mDisplayWindowPolicyControllers.valueAt(i).second.dump(" ", pw);
}
}
pw.println();
@@ -2922,9 +2947,10 @@
@Override // Binder call
public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
- IVirtualDisplayCallback callback, IMediaProjection projection, String packageName) {
+ IVirtualDisplayCallback callback, IMediaProjection projection,
+ IVirtualDevice virtualDeviceToken, String packageName) {
return createVirtualDisplayInternal(virtualDisplayConfig, callback, projection,
- packageName, null /* controller */);
+ virtualDeviceToken, packageName);
}
@Override // Binder call
@@ -3690,17 +3716,12 @@
}
@Override
- public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
- IVirtualDisplayCallback callback, IMediaProjection projection, String packageName,
- DisplayWindowPolicyController controller) {
- return createVirtualDisplayInternal(virtualDisplayConfig, callback, projection,
- packageName, controller);
- }
-
- @Override
public DisplayWindowPolicyController getDisplayWindowPolicyController(int displayId) {
synchronized (mSyncRoot) {
- return mDisplayWindowPolicyController.get(displayId);
+ if (mDisplayWindowPolicyControllers.contains(displayId)) {
+ return mDisplayWindowPolicyControllers.get(displayId).second;
+ }
+ return null;
}
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 7f78cac..b46ee27 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -386,12 +386,8 @@
private Sensor mLightSensor;
- // The mapper between ambient lux, display backlight values, and display brightness.
- // This mapper holds the current one that is being used. We will switch between the idle
- // mapper and active mapper here.
- @Nullable
- private BrightnessMappingStrategy mCurrentBrightnessMapper;
-
+ // The mappers between ambient lux, display backlight values, and display brightness.
+ // We will switch between the idle mapper and active mapper in AutomaticBrightnessController.
// Mapper used for active (normal) screen brightness mode
@Nullable
private BrightnessMappingStrategy mInteractiveModeBrightnessMapper;
@@ -399,6 +395,11 @@
@Nullable
private BrightnessMappingStrategy mIdleModeBrightnessMapper;
+ // If these are both true, and mIdleModeBrightnessMapper != null,
+ // then we are in idle screen brightness mode.
+ private boolean mIsDreaming;
+ private boolean mIsDocked;
+
// The current brightness configuration.
@Nullable
private BrightnessConfiguration mBrightnessConfiguration;
@@ -610,8 +611,14 @@
}
private void handleRbcChanged(boolean strengthChanged, boolean justActivated) {
- if (mCurrentBrightnessMapper == null) {
- Log.w(TAG, "No brightness mapping available to recalculate splines");
+ if (mAutomaticBrightnessController == null) {
+ return;
+ }
+ if ((!mAutomaticBrightnessController.isInIdleMode()
+ && mInteractiveModeBrightnessMapper == null)
+ || (mAutomaticBrightnessController.isInIdleMode()
+ && mIdleModeBrightnessMapper == null)) {
+ Log.w(TAG, "No brightness mapping available to recalculate splines for this mode");
return;
}
@@ -619,7 +626,7 @@
for (int i = 0; i < mNitsRange.length; i++) {
adjustedNits[i] = mCdsi.getReduceBrightColorsAdjustedBrightnessNits(mNitsRange[i]);
}
- mCurrentBrightnessMapper.recalculateSplines(mCdsi.isReduceBrightColorsActivated(),
+ mAutomaticBrightnessController.recalculateSplines(mCdsi.isReduceBrightColorsActivated(),
adjustedNits);
mPendingRbcOnOrChanged = strengthChanged || justActivated;
@@ -886,9 +893,8 @@
mIdleModeBrightnessMapper = BrightnessMappingStrategy.createForIdleMode(resources,
mDisplayDeviceConfig);
}
- mCurrentBrightnessMapper = mInteractiveModeBrightnessMapper;
- if (mCurrentBrightnessMapper != null) {
+ if (mInteractiveModeBrightnessMapper != null) {
final float dozeScaleFactor = resources.getFraction(
com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
1, 1);
@@ -939,12 +945,13 @@
mAutomaticBrightnessController.stop();
}
mAutomaticBrightnessController = new AutomaticBrightnessController(this,
- handler.getLooper(), mSensorManager, mLightSensor, mCurrentBrightnessMapper,
- lightSensorWarmUpTimeConfig, PowerManager.BRIGHTNESS_MIN,
- PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate,
- initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
- autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
- screenBrightnessThresholds, mContext, mHbmController);
+ handler.getLooper(), mSensorManager, mLightSensor,
+ mInteractiveModeBrightnessMapper, lightSensorWarmUpTimeConfig,
+ PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, dozeScaleFactor,
+ lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
+ darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
+ ambientBrightnessThresholds, screenBrightnessThresholds, mContext,
+ mHbmController, mIdleModeBrightnessMapper);
} else {
mUseSoftwareAutoBrightnessConfig = false;
}
@@ -974,6 +981,16 @@
}
}
+ public void setAutomaticScreenBrightnessMode(boolean isIdle) {
+ if (mAutomaticBrightnessController != null) {
+ if (isIdle) {
+ mAutomaticBrightnessController.switchToIdleMode();
+ } else {
+ mAutomaticBrightnessController.switchToInteractiveScreenBrightnessMode();
+ }
+ }
+ }
+
private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
@@ -1422,9 +1439,14 @@
}
}
- if (!brightnessIsTemporary) {
+ // Report brightness to brightnesstracker:
+ // If brightness is not temporary (ie the slider has been released)
+ // AND if we are not in idle screen brightness mode.
+ if (!brightnessIsTemporary
+ && (mAutomaticBrightnessController != null
+ && !mAutomaticBrightnessController.isInIdleMode())) {
if (userInitiatedChange && (mAutomaticBrightnessController == null
- || !mAutomaticBrightnessController.hasValidAmbientLux())) {
+ || !mAutomaticBrightnessController.hasValidAmbientLux())) {
// If we don't have a valid lux reading we can't report a valid
// slider event so notify as if the system changed the brightness.
userInitiatedChange = false;
@@ -2162,11 +2184,10 @@
}
private float convertToNits(float brightness) {
- if (mCurrentBrightnessMapper != null) {
- return mCurrentBrightnessMapper.convertToNits(brightness);
- } else {
- return -1.0f;
+ if (mAutomaticBrightnessController == null) {
+ return -1f;
}
+ return mAutomaticBrightnessController.convertToNits(brightness);
}
private void updatePendingProximityRequestsLocked() {
@@ -2315,11 +2336,6 @@
pw.println(" mReportedToPolicy=" +
reportedToPolicyToString(mReportedScreenStateToPolicy));
- if (mIdleModeBrightnessMapper != null) {
- pw.println(" mIdleModeBrightnessMapper= ");
- mIdleModeBrightnessMapper.dump(pw);
- }
-
if (mScreenBrightnessRampAnimator != null) {
pw.println(" mScreenBrightnessRampAnimator.isAnimating()=" +
mScreenBrightnessRampAnimator.isAnimating());
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 05e1bdd..3d91fee 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -39,6 +39,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArrayMap;
+import android.util.EventLog;
import android.util.Slog;
import android.view.IWindowManager;
import android.view.WindowManager;
@@ -49,6 +50,7 @@
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.UnbindReason;
import com.android.internal.view.IInputMethod;
+import com.android.server.EventLogTags;
import com.android.server.wm.WindowManagerInternal;
/**
@@ -58,6 +60,9 @@
static final boolean DEBUG = false;
private static final String TAG = InputMethodBindingController.class.getSimpleName();
+ /** Time in milliseconds that the IME service has to bind before it is reconnected. */
+ static final long TIME_TO_RECONNECT = 3 * 1000;
+
@NonNull private final InputMethodManagerService mService;
@NonNull private final Context mContext;
@NonNull private final ArrayMap<String, InputMethodInfo> mMethodMap;
@@ -239,7 +244,7 @@
}
/**
- * Indicates whether {@link #getVisibleConnection} is currently in use.
+ * Indicates whether {@link #mVisibleConnection} is currently in use.
*/
boolean isVisibleBound() {
return mVisibleBound;
@@ -248,11 +253,6 @@
/**
* Used to bring IME service up to visible adjustment while it is being shown.
*/
- @NonNull
- ServiceConnection getVisibleConnection() {
- return mVisibleConnection;
- }
-
private final ServiceConnection mVisibleConnection = new ServiceConnection() {
@Override public void onBindingDied(ComponentName name) {
synchronized (mMethodMap) {
@@ -334,7 +334,7 @@
// We consider this to be a new bind attempt, since the system
// should now try to restart the service for us.
mLastBindTime = SystemClock.uptimeMillis();
- mService.clearClientSessionsLocked();
+ clearCurMethodAndSessionsLocked();
mService.clearInputShowRequestLocked();
mService.unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME);
}
@@ -358,11 +358,12 @@
}
mCurId = null;
- mService.clearClientSessionsLocked();
+ clearCurMethodAndSessionsLocked();
}
@GuardedBy("mMethodMap")
- void clearCurMethodLocked() {
+ private void clearCurMethodAndSessionsLocked() {
+ mService.clearClientSessionsLocked();
mCurMethod = null;
mCurMethodUid = Process.INVALID_UID;
}
@@ -382,7 +383,7 @@
@GuardedBy("mMethodMap")
@NonNull
- InputBindResult bindCurrentMethodLocked(int displayIdToShowIme) {
+ InputBindResult bindCurrentMethodLocked() {
InputMethodInfo info = mMethodMap.get(mSelectedMethodId);
if (info == null) {
throw new IllegalArgumentException("Unknown id: " + mSelectedMethodId);
@@ -394,7 +395,7 @@
mCurId = info.getId();
mLastBindTime = SystemClock.uptimeMillis();
- addFreshWindowTokenLocked(displayIdToShowIme);
+ addFreshWindowTokenLocked();
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
null, null, mCurId, mCurSeq, false);
@@ -419,7 +420,8 @@
}
@GuardedBy("mMethodMap")
- private void addFreshWindowTokenLocked(int displayIdToShowIme) {
+ private void addFreshWindowTokenLocked() {
+ int displayIdToShowIme = mService.getDisplayIdToShowIme();
mCurToken = new Binder();
mService.setCurTokenDisplayId(displayIdToShowIme);
@@ -438,7 +440,7 @@
}
@GuardedBy("mMethodMap")
- void unbindMainConnectionLocked() {
+ private void unbindMainConnectionLocked() {
mContext.unbindService(mMainConnection);
mHasConnection = false;
}
@@ -460,17 +462,61 @@
}
@GuardedBy("mMethodMap")
- boolean bindCurrentInputMethodServiceVisibleConnectionLocked() {
+ private boolean bindCurrentInputMethodServiceVisibleConnectionLocked() {
mVisibleBound = bindCurrentInputMethodServiceLocked(mVisibleConnection,
IME_VISIBLE_BIND_FLAGS);
return mVisibleBound;
}
@GuardedBy("mMethodMap")
- boolean bindCurrentInputMethodServiceMainConnectionLocked() {
+ private boolean bindCurrentInputMethodServiceMainConnectionLocked() {
mHasConnection = bindCurrentInputMethodServiceLocked(mMainConnection,
mImeConnectionBindFlags);
return mHasConnection;
}
+ /**
+ * Bind the IME so that it can be shown.
+ *
+ * <p>
+ * Performs a rebind if no binding is achieved in {@link #TIME_TO_RECONNECT} milliseconds.
+ */
+ @GuardedBy("mMethodMap")
+ void setCurrentMethodVisibleLocked() {
+ if (mCurMethod != null) {
+ if (DEBUG) Slog.d(TAG, "setCurrentMethodVisibleLocked: mCurToken=" + mCurToken);
+ if (mHasConnection && !mVisibleBound) {
+ bindCurrentInputMethodServiceVisibleConnectionLocked();
+ }
+ return;
+ }
+
+ long bindingDuration = SystemClock.uptimeMillis() - mLastBindTime;
+ if (mHasConnection && bindingDuration >= TIME_TO_RECONNECT) {
+ // The client has asked to have the input method shown, but
+ // we have been sitting here too long with a connection to the
+ // service and no interface received, so let's disconnect/connect
+ // to try to prod things along.
+ EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, getSelectedMethodId(),
+ bindingDuration, 1);
+ Slog.w(TAG, "Force disconnect/connect to the IME in setCurrentMethodVisibleLocked()");
+ unbindMainConnectionLocked();
+ bindCurrentInputMethodServiceMainConnectionLocked();
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Can't show input: connection = " + mHasConnection + ", time = "
+ + (TIME_TO_RECONNECT - bindingDuration));
+ }
+ }
+ }
+
+ /**
+ * Remove the binding needed for the IME to be shown.
+ */
+ @GuardedBy("mMethodMap")
+ void setCurrentMethodNotVisibleLocked() {
+ if (mVisibleBound) {
+ unbindVisibleConnectionLocked();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 3c6b096..bff4f27 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -49,6 +49,8 @@
import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
+import static com.android.server.inputmethod.InputMethodBindingController.TIME_TO_RECONNECT;
+
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.Manifest;
@@ -111,12 +113,10 @@
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
-import android.text.style.SuggestionSpan;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
-import android.util.LruCache;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Printer;
@@ -250,10 +250,6 @@
static final int MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE = 7000;
- static final long TIME_TO_RECONNECT = 3 * 1000;
-
- static final int SECURE_SUGGESTION_SPANS_MAX_SIZE = 20;
-
private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
private static final String HANDLER_THREAD_NAME = "android.imms";
@@ -301,8 +297,6 @@
// lock for this class.
final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
- private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans =
- new LruCache<>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
final InputMethodSubtypeSwitchingController mSwitchingController;
/**
@@ -312,13 +306,16 @@
private int mMethodMapUpdateCount = 0;
/**
- * Indicates whether {@link InputMethodBindingController#getVisibleConnection} is currently
- * in use.
+ * The display id for which the latest startInput was called.
*/
- private boolean isVisibleBound() {
- return mBindingController.isVisibleBound();
+ @GuardedBy("mMethodMap")
+ int getDisplayIdToShowIme() {
+ return mDisplayIdToShowIme;
}
+ @GuardedBy("mMethodMap")
+ private int mDisplayIdToShowIme = INVALID_DISPLAY;
+
// Ongoing notification
private NotificationManager mNotificationManager;
KeyguardManager mKeyguardManager;
@@ -2354,10 +2351,10 @@
}
// Compute the final shown display ID with validated cs.selfReportedDisplayId for this
// session & other conditions.
- final int displayIdToShowIme = computeImeDisplayIdForTarget(cs.selfReportedDisplayId,
+ mDisplayIdToShowIme = computeImeDisplayIdForTarget(cs.selfReportedDisplayId,
mImeDisplayValidator);
- if (displayIdToShowIme == INVALID_DISPLAY) {
+ if (mDisplayIdToShowIme == INVALID_DISPLAY) {
mImeHiddenByDisplayPolicy = true;
hideCurrentInputLocked(mCurFocusedWindow, 0, null,
SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE);
@@ -2378,7 +2375,7 @@
// Check if the input method is changing.
// We expect the caller has already verified that the client is allowed to access this
// display ID.
- if (isSelectedMethodBound(displayIdToShowIme)) {
+ if (isSelectedMethodBound()) {
if (cs.curSession != null) {
// Fast case: if we are already connected to the input method,
// then just return it.
@@ -2394,13 +2391,13 @@
mBindingController.unbindCurrentMethodLocked();
- return mBindingController.bindCurrentMethodLocked(displayIdToShowIme);
+ return mBindingController.bindCurrentMethodLocked();
}
- private boolean isSelectedMethodBound(int displayIdToShowIme) {
+ private boolean isSelectedMethodBound() {
String curId = getCurId();
return curId != null && curId.equals(getSelectedMethodId())
- && displayIdToShowIme == mCurTokenDisplayId;
+ && mDisplayIdToShowIme == mCurTokenDisplayId;
}
@GuardedBy("mMethodMap")
@@ -2590,7 +2587,6 @@
finishSessionLocked(mEnabledSession);
mEnabledSession = null;
- mBindingController.clearCurMethodLocked();
scheduleNotifyImeUidToAudioService(Process.INVALID_UID);
}
hideStatusBarIconLocked();
@@ -3047,42 +3043,20 @@
return false;
}
- boolean res = false;
- IInputMethod curMethod = getCurMethod();
- if (curMethod != null) {
- if (DEBUG) Slog.d(TAG, "showCurrentInputLocked: mCurToken=" + getCurToken());
+ mBindingController.setCurrentMethodVisibleLocked();
+ if (getCurMethod() != null) {
// create a placeholder token for IMS so that IMS cannot inject windows into client app.
Binder showInputToken = new Binder();
mShowRequestWindowMap.put(showInputToken, windowToken);
+ IInputMethod curMethod = getCurMethod();
executeOrSendMessage(curMethod, mCaller.obtainMessageIIOOO(MSG_SHOW_SOFT_INPUT,
getImeShowFlagsLocked(), reason, curMethod, resultReceiver,
showInputToken));
mInputShown = true;
- if (hasConnection() && !isVisibleBound()) {
- mBindingController.bindCurrentInputMethodServiceVisibleConnectionLocked();
- }
- res = true;
- } else {
- long bindingDuration = SystemClock.uptimeMillis() - getLastBindTime();
- if (hasConnection() && bindingDuration >= TIME_TO_RECONNECT) {
- // The client has asked to have the input method shown, but
- // we have been sitting here too long with a connection to the
- // service and no interface received, so let's disconnect/connect
- // to try to prod things along.
- EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, getSelectedMethodId(),
- bindingDuration, 1);
- Slog.w(TAG, "Force disconnect/connect to the IME in showCurrentInputLocked()");
- mBindingController.unbindMainConnectionLocked();
- mBindingController.bindCurrentInputMethodServiceMainConnectionLocked();
- } else {
- if (DEBUG) {
- Slog.d(TAG, "Can't show input: connection = " + hasConnection() + ", time = "
- + (TIME_TO_RECONNECT - bindingDuration));
- }
- }
+ return true;
}
- return res;
+ return false;
}
@Override
@@ -3166,9 +3140,7 @@
} else {
res = false;
}
- if (hasConnection() && isVisibleBound()) {
- mBindingController.unbindVisibleConnectionLocked();
- }
+ mBindingController.setCurrentMethodNotVisibleLocked();
mInputShown = false;
mShowRequested = false;
mShowExplicitlyRequested = false;
@@ -3522,10 +3494,6 @@
return mWindowManagerInternal.shouldRestoreImeVisibility(windowToken);
}
- private boolean isImeVisible() {
- return (mImeWindowVis & InputMethodService.IME_VISIBLE) != 0;
- }
-
@GuardedBy("mMethodMap")
private boolean canShowInputMethodPickerLocked(IInputMethodClient client) {
// TODO(yukawa): multi-display support.
@@ -5114,7 +5082,8 @@
+ " client=" + mCurFocusedWindowClient);
focusedWindowClient = mCurFocusedWindowClient;
p.println(" mCurId=" + getCurId() + " mHaveConnection=" + hasConnection()
- + " mBoundToMethod=" + mBoundToMethod + " mVisibleBound=" + isVisibleBound());
+ + " mBoundToMethod=" + mBoundToMethod + " mVisibleBound="
+ + mBindingController.isVisibleBound());
p.println(" mCurToken=" + getCurToken());
p.println(" mCurTokenDisplayId=" + mCurTokenDisplayId);
p.println(" mCurHostInputToken=" + mCurHostInputToken);
diff --git a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
new file mode 100644
index 0000000..b2bd47b
--- /dev/null
+++ b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
@@ -0,0 +1,615 @@
+/*
+ * 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.
+ */
+
+package com.android.server.locales;
+
+import static android.os.UserHandle.USER_NULL;
+
+import static com.android.server.locales.LocaleManagerService.DEBUG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.backup.BackupManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.os.BestClock;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.HandlerThread;
+import android.os.LocaleList;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import java.time.Clock;
+import java.time.Duration;
+import java.time.ZoneOffset;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Helper class for managing backup and restore of app-specific locales.
+ */
+class LocaleManagerBackupHelper {
+ private static final String TAG = "LocaleManagerBkpHelper"; // must be < 23 chars
+
+ // Tags and attributes for xml.
+ private static final String LOCALES_XML_TAG = "locales";
+ private static final String PACKAGE_XML_TAG = "package";
+ private static final String ATTR_PACKAGE_NAME = "name";
+ private static final String ATTR_LOCALES = "locales";
+ private static final String ATTR_CREATION_TIME_MILLIS = "creationTimeMillis";
+
+ private static final String STAGE_FILE_NAME = "staged_locales";
+ private static final String SYSTEM_BACKUP_PACKAGE_KEY = "android";
+
+ private static final Pattern STAGE_FILE_NAME_PATTERN = Pattern.compile(
+ TextUtils.formatSimple("(^%s_)(\\d+)(\\.xml$)", STAGE_FILE_NAME));
+ private static final int USER_ID_GROUP_INDEX_IN_PATTERN = 2;
+ private static final Duration STAGE_FILE_RETENTION_PERIOD = Duration.ofDays(3);
+
+ private final LocaleManagerService mLocaleManagerService;
+ private final PackageManagerInternal mPackageManagerInternal;
+ private final File mStagedLocalesDir;
+ private final Clock mClock;
+ private final Context mContext;
+ private final Object mStagedDataLock = new Object();
+
+ // Staged data map keyed by user-id to handle multi-user scenario / work profiles. We are using
+ // SparseArray because it is more memory-efficient than a HashMap.
+ private final SparseArray<StagedData> mStagedData = new SparseArray<>();
+
+ private final PackageMonitor mPackageMonitor;
+ private final BroadcastReceiver mUserMonitor;
+
+ LocaleManagerBackupHelper(LocaleManagerService localeManagerService,
+ PackageManagerInternal pmInternal) {
+ this(localeManagerService.mContext, localeManagerService, pmInternal,
+ new File(Environment.getDataSystemCeDirectory(),
+ "app_locales"), getDefaultClock());
+ }
+
+ private static @NonNull Clock getDefaultClock() {
+ return new BestClock(ZoneOffset.UTC, SystemClock.currentNetworkTimeClock(),
+ Clock.systemUTC());
+ }
+
+ @VisibleForTesting LocaleManagerBackupHelper(Context context,
+ LocaleManagerService localeManagerService,
+ PackageManagerInternal pmInternal, File stagedLocalesDir, Clock clock) {
+ mContext = context;
+ mLocaleManagerService = localeManagerService;
+ mPackageManagerInternal = pmInternal;
+ mClock = clock;
+ mStagedLocalesDir = stagedLocalesDir;
+
+ loadAllStageFiles();
+
+ HandlerThread broadcastHandlerThread = new HandlerThread(TAG,
+ Process.THREAD_PRIORITY_BACKGROUND);
+ broadcastHandlerThread.start();
+
+ mPackageMonitor = new PackageMonitorImpl();
+ mPackageMonitor.register(context, broadcastHandlerThread.getLooper(),
+ UserHandle.ALL,
+ true);
+ mUserMonitor = new UserMonitor();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_USER_REMOVED);
+ context.registerReceiverAsUser(mUserMonitor, UserHandle.ALL, filter,
+ null, broadcastHandlerThread.getThreadHandler());
+ }
+
+ @VisibleForTesting
+ BroadcastReceiver getUserMonitor() {
+ return mUserMonitor;
+ }
+
+ @VisibleForTesting
+ PackageMonitor getPackageMonitor() {
+ return mPackageMonitor;
+ }
+
+ /**
+ * Loads the staged data into memory by reading all the files in the staged directory.
+ *
+ * <p><b>Note:</b> We don't ned to hold the lock here because this is only called in the
+ * constructor (before any broadcast receivers are registered).
+ */
+ private void loadAllStageFiles() {
+ File[] files = mStagedLocalesDir.listFiles();
+ if (files == null) {
+ return;
+ }
+ for (File file : files) {
+ String fileName = file.getName();
+ Matcher matcher = STAGE_FILE_NAME_PATTERN.matcher(fileName);
+ if (!matcher.matches()) {
+ file.delete();
+ Slog.w(TAG, TextUtils.formatSimple("Deleted %s. Reason: %s.", fileName,
+ "Unrecognized file"));
+ continue;
+ }
+ try {
+ final int userId = Integer.parseInt(matcher.group(USER_ID_GROUP_INDEX_IN_PATTERN));
+ StagedData stagedData = readStageFile(file);
+ if (stagedData != null) {
+ mStagedData.put(userId, stagedData);
+ } else {
+ file.delete();
+ Slog.w(TAG, TextUtils.formatSimple("Deleted %s. Reason: %s.", fileName,
+ "Could not read file"));
+ }
+ } catch (NumberFormatException e) {
+ file.delete();
+ Slog.w(TAG, TextUtils.formatSimple("Deleted %s. Reason: %s.", fileName,
+ "Could not parse user id from file name"));
+ }
+ }
+ }
+
+ /**
+ * Loads the stage file from the disk and parses it into a list of app backups.
+ */
+ private @Nullable StagedData readStageFile(@NonNull File file) {
+ InputStream stagedDataInputStream = null;
+ AtomicFile stageFile = new AtomicFile(file);
+ try {
+ stagedDataInputStream = stageFile.openRead();
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(stagedDataInputStream, StandardCharsets.UTF_8.name());
+
+ XmlUtils.beginDocument(parser, LOCALES_XML_TAG);
+ long creationTimeMillis = parser.getAttributeLong(/* namespace= */ null,
+ ATTR_CREATION_TIME_MILLIS);
+ return new StagedData(creationTimeMillis, readFromXml(parser));
+ } catch (IOException | XmlPullParserException e) {
+ Slog.e(TAG, "Could not parse stage file ", e);
+ } finally {
+ IoUtils.closeQuietly(stagedDataInputStream);
+ }
+ return null;
+ }
+
+ /**
+ * @see LocaleManagerInternal#getBackupPayload(int userId)
+ */
+ public byte[] getBackupPayload(int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "getBackupPayload invoked for user id " + userId);
+ }
+
+ synchronized (mStagedDataLock) {
+ cleanStagedDataForOldEntriesLocked();
+ }
+
+ HashMap<String, String> pkgStates = new HashMap<>();
+ for (ApplicationInfo appInfo : mPackageManagerInternal.getInstalledApplications(/*flags*/0,
+ userId, Binder.getCallingUid())) {
+ try {
+ LocaleList appLocales = mLocaleManagerService.getApplicationLocales(
+ appInfo.packageName,
+ userId);
+ // Backup locales only for apps which do have app-specific overrides.
+ if (!appLocales.isEmpty()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Add package=" + appInfo.packageName + " locales="
+ + appLocales.toLanguageTags() + " to backup payload");
+ }
+ pkgStates.put(appInfo.packageName, appLocales.toLanguageTags());
+ }
+ } catch (RemoteException | IllegalArgumentException e) {
+ Slog.e(TAG, "Exception when getting locales for package: " + appInfo.packageName,
+ e);
+ }
+ }
+
+ if (pkgStates.isEmpty()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Final payload=null");
+ }
+ // Returning null here will ensure deletion of the entry for LMS from the backup data.
+ return null;
+ }
+
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ // Passing arbitrary value for creationTimeMillis since it is ignored when forStage
+ // is false.
+ writeToXml(out, pkgStates, /* forStage= */ false, /* creationTimeMillis= */ -1);
+ } catch (IOException e) {
+ Slog.e(TAG, "Could not write to xml for backup ", e);
+ return null;
+ }
+
+ if (DEBUG) {
+ try {
+ Slog.d(TAG, "Final payload=" + out.toString("UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ Slog.w(TAG, "Could not encode payload to UTF-8", e);
+ }
+ }
+ return out.toByteArray();
+ }
+
+ private void cleanStagedDataForOldEntriesLocked() {
+ for (int i = 0; i < mStagedData.size(); i++) {
+ int userId = mStagedData.keyAt(i);
+ StagedData stagedData = mStagedData.get(userId);
+ if (stagedData.mCreationTimeMillis
+ < mClock.millis() - STAGE_FILE_RETENTION_PERIOD.toMillis()) {
+ deleteStagedDataLocked(userId);
+ }
+ }
+ }
+
+ /**
+ * @see LocaleManagerInternal#stageAndApplyRestoredPayload(byte[] payload, int userId)
+ */
+ public void stageAndApplyRestoredPayload(byte[] payload, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "stageAndApplyRestoredPayload user=" + userId + " payload="
+ + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
+ }
+ if (payload == null) {
+ Slog.e(TAG, "stageAndApplyRestoredPayload: no payload to restore for user " + userId);
+ return;
+ }
+
+ final ByteArrayInputStream inputStream = new ByteArrayInputStream(payload);
+
+ HashMap<String, String> pkgStates = new HashMap<>();
+ try {
+ // Parse the input blob into a list of BackupPackageState.
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(inputStream, StandardCharsets.UTF_8.name());
+
+ XmlUtils.beginDocument(parser, LOCALES_XML_TAG);
+ pkgStates = readFromXml(parser);
+ } catch (IOException | XmlPullParserException e) {
+ Slog.e(TAG, "Could not parse payload ", e);
+ }
+
+ // We need a lock here to prevent race conditions when accessing the stage file.
+ // It might happen that a restore was triggered (manually using bmgr cmd) and at the same
+ // time a new package is added. We want to ensure that both these operations aren't
+ // performed simultaneously.
+ synchronized (mStagedDataLock) {
+ // Backups for apps which are yet to be installed.
+ mStagedData.put(userId, new StagedData(mClock.millis(), new HashMap<>()));
+
+ for (String pkgName : pkgStates.keySet()) {
+ String languageTags = pkgStates.get(pkgName);
+ // Check if the application is already installed for the concerned user.
+ if (isPackageInstalledForUser(pkgName, userId)) {
+ // Don't apply the restore if the locales have already been set for the app.
+ checkExistingLocalesAndApplyRestore(pkgName, languageTags, userId);
+ } else {
+ // Stage the data if the app isn't installed.
+ mStagedData.get(userId).mPackageStates.put(pkgName, languageTags);
+ if (DEBUG) {
+ Slog.d(TAG, "Add locales=" + languageTags
+ + " package=" + pkgName + " for lazy restore.");
+ }
+ }
+ }
+
+ writeStageFileLocked(userId);
+ }
+ }
+
+ /**
+ * Notifies the backup manager to include the "android" package in the next backup pass.
+ */
+ public void notifyBackupManager() {
+ BackupManager.dataChanged(SYSTEM_BACKUP_PACKAGE_KEY);
+ }
+
+ private boolean isPackageInstalledForUser(String packageName, int userId) {
+ PackageInfo pkgInfo = null;
+ try {
+ pkgInfo = mContext.getPackageManager().getPackageInfoAsUser(
+ packageName, /* flags= */ 0, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ if (DEBUG) {
+ Slog.d(TAG, "Could not get package info for " + packageName, e);
+ }
+ }
+ return pkgInfo != null;
+ }
+
+ /**
+ * Checks if locales already exist for the application and applies the restore accordingly.
+ * <p>
+ * The user might change the locales for an application before the restore is applied. In this
+ * case, we want to keep the user settings and discard the restore.
+ */
+ private void checkExistingLocalesAndApplyRestore(@NonNull String pkgName,
+ @NonNull String languageTags, int userId) {
+ try {
+ LocaleList currLocales = mLocaleManagerService.getApplicationLocales(
+ pkgName,
+ userId);
+ if (!currLocales.isEmpty()) {
+ return;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not check for current locales before restoring", e);
+ }
+
+ // Restore the locale immediately
+ try {
+ mLocaleManagerService.setApplicationLocales(pkgName, userId,
+ LocaleList.forLanguageTags(languageTags));
+ if (DEBUG) {
+ Slog.d(TAG, "Restored locales=" + languageTags + " for package=" + pkgName);
+ }
+ } catch (RemoteException | IllegalArgumentException e) {
+ Slog.e(TAG, "Could not restore locales for " + pkgName, e);
+ }
+ }
+
+ /**
+ * Converts the list of app backups into xml and writes it onto the disk.
+ */
+ private void writeStageFileLocked(int userId) {
+ StagedData stagedData = mStagedData.get(userId);
+ if (stagedData.mPackageStates.isEmpty()) {
+ deleteStagedDataLocked(userId);
+ return;
+ }
+
+ final FileOutputStream stagedDataOutputStream;
+ AtomicFile stageFile = new AtomicFile(
+ new File(mStagedLocalesDir,
+ TextUtils.formatSimple("%s_%d.xml", STAGE_FILE_NAME, userId)));
+ try {
+ stagedDataOutputStream = stageFile.startWrite();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to save stage file");
+ return;
+ }
+
+ try {
+ writeToXml(stagedDataOutputStream, stagedData.mPackageStates, /* forStage= */ true,
+ stagedData.mCreationTimeMillis);
+ stageFile.finishWrite(stagedDataOutputStream);
+ if (DEBUG) {
+ Slog.d(TAG, "Stage file written.");
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Could not write stage file", e);
+ stageFile.failWrite(stagedDataOutputStream);
+ }
+ }
+
+ private void deleteStagedDataLocked(@UserIdInt int userId) {
+ AtomicFile stageFile = getStageFileIfExistsLocked(userId);
+ if (stageFile != null) {
+ stageFile.delete();
+ }
+ mStagedData.remove(userId);
+ }
+
+ private @Nullable AtomicFile getStageFileIfExistsLocked(@UserIdInt int userId) {
+ final File stageFile = new File(mStagedLocalesDir,
+ TextUtils.formatSimple("%s_%d.xml", STAGE_FILE_NAME, userId));
+ return stageFile.isFile() ? new AtomicFile(stageFile)
+ : null;
+ }
+
+ /**
+ * Parses the backup data from the serialized xml input stream.
+ */
+ private @NonNull HashMap<String, String> readFromXml(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ HashMap<String, String> packageStates = new HashMap<>();
+ int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (parser.getName().equals(PACKAGE_XML_TAG)) {
+ String packageName = parser.getAttributeValue(/* namespace= */ null,
+ ATTR_PACKAGE_NAME);
+ String languageTags = parser.getAttributeValue(/* namespace= */ null, ATTR_LOCALES);
+
+ if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(languageTags)) {
+ packageStates.put(packageName, languageTags);
+ }
+ }
+ }
+ return packageStates;
+ }
+
+ /**
+ * Converts the list of app backup data into a serialized xml stream.
+ *
+ * @param forStage Flag to indicate whether this method is called for the purpose of
+ * staging the data. Note that if this is false, {@code creationTimeMillis} is ignored because
+ * we only need it for the stage data.
+ * @param creationTimeMillis The timestamp when the stage data was created. This is required
+ * to determine when to delete the stage data.
+ */
+ private static void writeToXml(OutputStream stream,
+ @NonNull HashMap<String, String> pkgStates, boolean forStage, long creationTimeMillis)
+ throws IOException {
+ if (pkgStates.isEmpty()) {
+ // No need to write anything at all if pkgStates is empty.
+ return;
+ }
+
+ TypedXmlSerializer out = Xml.newFastSerializer();
+ out.setOutput(stream, StandardCharsets.UTF_8.name());
+ out.startDocument(/* encoding= */ null, /* standalone= */ true);
+ out.startTag(/* namespace= */ null, LOCALES_XML_TAG);
+
+ if (forStage) {
+ out.attribute(/* namespace= */ null, ATTR_CREATION_TIME_MILLIS,
+ Long.toString(creationTimeMillis));
+ }
+
+ for (String pkg : pkgStates.keySet()) {
+ out.startTag(/* namespace= */ null, PACKAGE_XML_TAG);
+ out.attribute(/* namespace= */ null, ATTR_PACKAGE_NAME, pkg);
+ out.attribute(/* namespace= */ null, ATTR_LOCALES, pkgStates.get(pkg));
+ out.endTag(/*namespace= */ null, PACKAGE_XML_TAG);
+ }
+
+ out.endTag(/* namespace= */ null, LOCALES_XML_TAG);
+ out.endDocument();
+ }
+
+ private static class StagedData {
+ final long mCreationTimeMillis;
+ final HashMap<String, String> mPackageStates;
+
+ StagedData(long creationTimeMillis, HashMap<String, String> pkgStates) {
+ mCreationTimeMillis = creationTimeMillis;
+ mPackageStates = pkgStates;
+ }
+ }
+
+ /**
+ * Broadcast listener to capture user removed event.
+ *
+ * <p>The stage file is deleted when a user is removed.
+ */
+ private final class UserMonitor extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ try {
+ String action = intent.getAction();
+ if (action.equals(Intent.ACTION_USER_REMOVED)) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
+ synchronized (mStagedDataLock) {
+ deleteStagedDataLocked(userId);
+ }
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception in user monitor.", e);
+ }
+ }
+ }
+
+ /**
+ * Helper to monitor package states.
+ *
+ * <p>We're interested in package added, package data cleared and package removed events.
+ */
+ private final class PackageMonitorImpl extends PackageMonitor {
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ try {
+ synchronized (mStagedDataLock) {
+ int userId = UserHandle.getUserId(uid);
+ if (mStagedData.contains(userId)) {
+ // Perform lazy restore only if the staged data exists.
+ doLazyRestoreLocked(packageName, userId);
+ }
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception in onPackageAdded.", e);
+ }
+ }
+
+ @Override
+ public void onPackageDataCleared(String packageName, int uid) {
+ try {
+ notifyBackupManager();
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception in onPackageDataCleared.", e);
+ }
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ try {
+ notifyBackupManager();
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception in onPackageRemoved.", e);
+ }
+ }
+ }
+
+ /**
+ * Performs lazy restore from the staged data.
+ *
+ * <p>This is invoked by the package monitor on the package added callback.
+ */
+ private void doLazyRestoreLocked(String packageName, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "doLazyRestore package=" + packageName + " user=" + userId);
+ }
+
+ // Check if the package is installed indeed
+ if (!isPackageInstalledForUser(packageName, userId)) {
+ Slog.e(TAG, packageName + " not installed for user " + userId
+ + ". Could not restore locales from stage file");
+ return;
+ }
+
+ StagedData stagedData = mStagedData.get(userId);
+ for (String pkgName : stagedData.mPackageStates.keySet()) {
+ String languageTags = stagedData.mPackageStates.get(pkgName);
+
+ if (pkgName.equals(packageName)) {
+
+ checkExistingLocalesAndApplyRestore(pkgName, languageTags, userId);
+
+ // Remove the restored entry from the staged data list.
+ stagedData.mPackageStates.remove(pkgName);
+ // Update the file on the disk.
+ writeStageFileLocked(userId);
+
+ // No need to loop further after restoring locales because the staged data will
+ // contain at most one entry for the newly added package.
+ break;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/locales/LocaleManagerInternal.java b/services/core/java/com/android/server/locales/LocaleManagerInternal.java
new file mode 100644
index 0000000..2db92a5
--- /dev/null
+++ b/services/core/java/com/android/server/locales/LocaleManagerInternal.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package com.android.server.locales;
+
+import android.annotation.Nullable;
+
+/**
+ * System-server internal interface to the {@link LocaleManagerService}.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class LocaleManagerInternal {
+ /**
+ * Returns the app-specific locales to be backed up as a data-blob.
+ */
+ public abstract @Nullable byte[] getBackupPayload(int userId);
+
+ /**
+ * Restores the app-locales that were previously backed up.
+ *
+ * <p>This method will parse the input data blob and restore the locales for apps which are
+ * present on the device. It will stage the locale data for the apps which are not installed
+ * at the time this is called, to be referenced later when the app is installed.
+ */
+ public abstract void stageAndApplyRestoredPayload(byte[] payload, int userId);
+}
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index 457c2fd..6aabdb5 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -20,6 +20,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.ILocaleManager;
@@ -29,6 +30,7 @@
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.LocaleList;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
@@ -51,11 +53,14 @@
*/
public class LocaleManagerService extends SystemService {
private static final String TAG = "LocaleManagerService";
- private final Context mContext;
+ final Context mContext;
private final LocaleManagerService.LocaleManagerBinderService mBinderService;
private ActivityTaskManagerInternal mActivityTaskManagerInternal;
private ActivityManagerInternal mActivityManagerInternal;
private PackageManagerInternal mPackageManagerInternal;
+
+ private LocaleManagerBackupHelper mBackupHelper;
+
public static final boolean DEBUG = false;
public LocaleManagerService(Context context) {
@@ -65,23 +70,48 @@
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ mBackupHelper = new LocaleManagerBackupHelper(this,
+ mPackageManagerInternal);
}
@VisibleForTesting
LocaleManagerService(Context context, ActivityTaskManagerInternal activityTaskManagerInternal,
ActivityManagerInternal activityManagerInternal,
- PackageManagerInternal packageManagerInternal) {
+ PackageManagerInternal packageManagerInternal,
+ LocaleManagerBackupHelper localeManagerBackupHelper) {
super(context);
mContext = context;
mBinderService = new LocaleManagerBinderService();
mActivityTaskManagerInternal = activityTaskManagerInternal;
mActivityManagerInternal = activityManagerInternal;
mPackageManagerInternal = packageManagerInternal;
+ mBackupHelper = localeManagerBackupHelper;
}
@Override
public void onStart() {
publishBinderService(Context.LOCALE_SERVICE, mBinderService);
+ LocalServices.addService(LocaleManagerInternal.class, new LocaleManagerInternalImpl());
+ }
+
+ private final class LocaleManagerInternalImpl extends LocaleManagerInternal {
+
+ @Override
+ public @Nullable byte[] getBackupPayload(int userId) {
+ checkCallerIsSystem();
+ return mBackupHelper.getBackupPayload(userId);
+ }
+
+ @Override
+ public void stageAndApplyRestoredPayload(byte[] payload, int userId) {
+ mBackupHelper.stageAndApplyRestoredPayload(payload, userId);
+ }
+
+ private void checkCallerIsSystem() {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Caller is not system.");
+ }
+ }
}
private final class LocaleManagerBinderService extends ILocaleManager.Stub {
@@ -110,6 +140,7 @@
(new LocaleManagerShellCommand(mBinderService))
.exec(this, in, out, err, args, callback, resultReceiver);
}
+
}
/**
@@ -161,6 +192,8 @@
notifyAppWhoseLocaleChanged(appPackageName, userId, locales);
notifyInstallerOfAppWhoseLocaleChanged(appPackageName, userId, locales);
notifyRegisteredReceivers(appPackageName, userId, locales);
+
+ mBackupHelper.notifyBackupManager();
}
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index efd3037..0fd7cc1 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -427,6 +427,11 @@
onClientExit();
}
+ @Override
+ public int getId() {
+ return mHostEndPointId;
+ }
+
/**
* Invoked when the underlying binder of this broker has died at the client process.
*/
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 0ce24dd..ede8b32 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -177,7 +177,7 @@
protected interface LocationTransport {
void deliverOnLocationChanged(LocationResult locationResult,
- @Nullable Runnable onCompleteCallback) throws Exception;
+ @Nullable IRemoteCallback onCompleteCallback) throws Exception;
void deliverOnFlushComplete(int requestCode) throws Exception;
}
@@ -197,9 +197,8 @@
@Override
public void deliverOnLocationChanged(LocationResult locationResult,
- @Nullable Runnable onCompleteCallback) throws RemoteException {
- mListener.onLocationChanged(locationResult.asList(),
- SingleUseCallback.wrap(onCompleteCallback));
+ @Nullable IRemoteCallback onCompleteCallback) throws RemoteException {
+ mListener.onLocationChanged(locationResult.asList(), onCompleteCallback);
}
@Override
@@ -227,7 +226,7 @@
@Override
public void deliverOnLocationChanged(LocationResult locationResult,
- @Nullable Runnable onCompleteCallback)
+ @Nullable IRemoteCallback onCompleteCallback)
throws PendingIntent.CanceledException {
BroadcastOptions options = BroadcastOptions.makeBasic();
options.setDontSendToRestrictedApps(true);
@@ -243,20 +242,34 @@
intent.putExtra(KEY_LOCATIONS, locationResult.asList().toArray(new Location[0]));
}
+ PendingIntent.OnFinished onFinished = null;
+
// send() SHOULD only run the completion callback if it completes successfully. however,
- // b/199464864 (which could not be fixed in the S timeframe) means that it's possible
+ // b/201299281 (which could not be fixed in the S timeframe) means that it's possible
// for send() to throw an exception AND run the completion callback. if this happens, we
// would over-release the wakelock... we take matters into our own hands to ensure that
// the completion callback can only be run if send() completes successfully. this means
// the completion callback may be run inline - but as we've never specified what thread
// the callback is run on, this is fine.
- GatedCallback gatedCallback = new GatedCallback(onCompleteCallback);
+ GatedCallback gatedCallback;
+ if (onCompleteCallback != null) {
+ gatedCallback = new GatedCallback(() -> {
+ try {
+ onCompleteCallback.sendResult(null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ });
+ onFinished = (pI, i, rC, rD, rE) -> gatedCallback.run();
+ } else {
+ gatedCallback = new GatedCallback(null);
+ }
mPendingIntent.send(
mContext,
0,
intent,
- (pI, i, rC, rD, rE) -> gatedCallback.run(),
+ onFinished,
null,
null,
options.toBundle());
@@ -293,7 +306,7 @@
@Override
public void deliverOnLocationChanged(@Nullable LocationResult locationResult,
- @Nullable Runnable onCompleteCallback)
+ @Nullable IRemoteCallback onCompleteCallback)
throws RemoteException {
// ILocationCallback doesn't currently support completion callbacks
Preconditions.checkState(onCompleteCallback == null);
@@ -714,6 +727,13 @@
final PowerManager.WakeLock mWakeLock;
+ // b/206340085 - if we allocate a new wakelock releaser object for every delivery we
+ // increase the risk of resource starvation. if a client stops processing deliveries the
+ // system server binder allocation pool will be starved as we continue to queue up
+ // deliveries, each with a new allocation. in order to mitigate this, we use a single
+ // releaser object per registration rather than per delivery.
+ final ExternalWakeLockReleaser mWakeLockReleaser;
+
private volatile ProviderTransport mProviderTransport;
private int mNumLocationsDelivered = 0;
private long mExpirationRealtimeMs = Long.MAX_VALUE;
@@ -727,6 +747,7 @@
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
mWakeLock.setReferenceCounted(true);
mWakeLock.setWorkSource(request.getWorkSource());
+ mWakeLockReleaser = new ExternalWakeLockReleaser(identity, mWakeLock);
}
@Override
@@ -943,7 +964,7 @@
}
listener.deliverOnLocationChanged(deliverLocationResult,
- mUseWakeLock ? mWakeLock::release : null);
+ mUseWakeLock ? mWakeLockReleaser : null);
EVENT_LOG.logProviderDeliveredLocations(mName, locationResult.size(),
getIdentity());
}
@@ -2761,7 +2782,7 @@
@GuardedBy("this")
private boolean mRun;
- GatedCallback(Runnable callback) {
+ GatedCallback(@Nullable Runnable callback) {
mCallback = callback;
}
@@ -2796,4 +2817,24 @@
}
}
}
+
+ private static class ExternalWakeLockReleaser extends IRemoteCallback.Stub {
+
+ private final CallerIdentity mIdentity;
+ private final PowerManager.WakeLock mWakeLock;
+
+ ExternalWakeLockReleaser(CallerIdentity identity, PowerManager.WakeLock wakeLock) {
+ mIdentity = identity;
+ mWakeLock = Objects.requireNonNull(wakeLock);
+ }
+
+ @Override
+ public void sendResult(Bundle data) {
+ try {
+ mWakeLock.release();
+ } catch (RuntimeException e) {
+ Log.e(TAG, "wakelock over-released by " + mIdentity, e);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
index 22a675a..5e38bca 100644
--- a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
@@ -23,6 +23,8 @@
import static com.android.server.location.LocationManagerService.TAG;
import static com.android.server.location.eventlog.LocationEventLog.EVENT_LOG;
+import static java.lang.Math.max;
+
import android.annotation.Nullable;
import android.location.Location;
import android.location.LocationResult;
@@ -53,6 +55,7 @@
implements DeviceIdleHelper.DeviceIdleListener, DeviceIdleInternal.StationaryListener {
private static final long MAX_STATIONARY_LOCATION_AGE_MS = 30000;
+ private static final long MIN_INTERVAL_MS = 1000;
final Object mLock = new Object();
@@ -179,7 +182,7 @@
&& mLastLocation != null
&& mLastLocation.getElapsedRealtimeAgeMillis(mDeviceStationaryRealtimeMs)
<= MAX_STATIONARY_LOCATION_AGE_MS) {
- throttlingIntervalMs = mIncomingRequest.getIntervalMillis();
+ throttlingIntervalMs = max(mIncomingRequest.getIntervalMillis(), MIN_INTERVAL_MS);
}
ProviderRequest newRequest;
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index e64ec77..24008d0 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -27,6 +27,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
+import android.os.Binder;
import android.os.RemoteException;
import android.permission.IPermissionManager;
import android.util.ArrayMap;
@@ -73,7 +74,12 @@
*/
public boolean hasPermission(int uid) {
assertFlag();
- return mPmi.checkUidPermission(uid, NOTIFICATION_PERMISSION) == PERMISSION_GRANTED;
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ return mPmi.checkUidPermission(uid, NOTIFICATION_PERMISSION) == PERMISSION_GRANTED;
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
}
/**
@@ -161,6 +167,7 @@
public void setNotificationPermission(String packageName, @UserIdInt int userId, boolean grant,
boolean userSet) {
assertFlag();
+ final long callingId = Binder.clearCallingIdentity();
try {
if (grant) {
mPermManager.grantRuntimePermission(packageName, NOTIFICATION_PERMISSION, userId);
@@ -174,38 +181,55 @@
}
} catch (RemoteException e) {
Slog.e(TAG, "Could not reach system server", e);
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
}
}
public void setNotificationPermission(PackagePermission pkgPerm) {
assertFlag();
- setNotificationPermission(
- pkgPerm.packageName, pkgPerm.userId, pkgPerm.granted, pkgPerm.userSet);
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ setNotificationPermission(
+ pkgPerm.packageName, pkgPerm.userId, pkgPerm.granted, pkgPerm.userSet);
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
}
public boolean isPermissionFixed(String packageName, @UserIdInt int userId) {
assertFlag();
+ final long callingId = Binder.clearCallingIdentity();
try {
- int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION,
- userId);
- return (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
- || (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0;
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not reach system server", e);
+ try {
+ int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION,
+ userId);
+ return (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
+ || (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not reach system server", e);
+ }
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
}
- return false;
}
boolean isPermissionUserSet(String packageName, @UserIdInt int userId) {
assertFlag();
+ final long callingId = Binder.clearCallingIdentity();
try {
- int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION,
- userId);
- return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0;
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not reach system server", e);
+ try {
+ int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION,
+ userId);
+ return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not reach system server", e);
+ }
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
}
- return false;
}
private void assertFlag() {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 189ac58..7d4877c 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -65,6 +65,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.Preconditions;
@@ -380,18 +381,16 @@
}
if (migrateToPermission) {
- boolean hasChangedChannel = false;
- for (NotificationChannel channel : r.channels.values()) {
- if (channel.getUserLockedFields() != 0) {
- hasChangedChannel = true;
- break;
- }
+ r.importance = appImportance;
+ if (r.uid != UNKNOWN_UID) {
+ // Don't call into permission system until we have a valid uid
+ PackagePermission pkgPerm = new PackagePermission(
+ r.pkg, UserHandle.getUserId(r.uid),
+ r.importance != IMPORTANCE_NONE,
+ hasUserConfiguredSettings(r));
+ pkgPerms.add(pkgPerm);
}
- PackagePermission pkgPerm = new PackagePermission(
- r.pkg, userId, appImportance != IMPORTANCE_NONE,
- hasChangedChannel || appImportance == IMPORTANCE_NONE);
- pkgPerms.add(pkgPerm);
- } else {
+ } else if (!mPermissionHelper.isMigrationEnabled()) {
r.importance = appImportance;
}
}
@@ -401,11 +400,27 @@
}
if (migrateToPermission) {
for (PackagePermission p : pkgPerms) {
- mPermissionHelper.setNotificationPermission(p);
+ try {
+ mPermissionHelper.setNotificationPermission(p);
+ } catch (Exception e) {
+ Slog.e(TAG, "could not migrate setting for " + p.packageName, e);
+ }
}
}
}
+ @GuardedBy("mPackagePreferences")
+ private boolean hasUserConfiguredSettings(PackagePreferences p){
+ boolean hasChangedChannel = false;
+ for (NotificationChannel channel : p.channels.values()) {
+ if (channel.getUserLockedFields() != 0) {
+ hasChangedChannel = true;
+ break;
+ }
+ }
+ return hasChangedChannel || p.importance == IMPORTANCE_NONE;
+ }
+
private boolean isShortcutOk(NotificationChannel channel) {
boolean isInvalidShortcutChannel =
channel.getConversationId() != null &&
@@ -2526,6 +2541,17 @@
synchronized (mPackagePreferences) {
mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r);
}
+ if (mPermissionHelper.isMigrationEnabled()) {
+ try {
+ PackagePermission p = new PackagePermission(
+ r.pkg, UserHandle.getUserId(r.uid),
+ r.importance != IMPORTANCE_NONE,
+ hasUserConfiguredSettings(r));
+ mPermissionHelper.setNotificationPermission(p);
+ } catch (Exception e) {
+ Slog.e(TAG, "could not migrate setting for " + r.pkg, e);
+ }
+ }
updated = true;
} catch (PackageManager.NameNotFoundException e) {
// noop
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index 1e2eb5d..e99512d 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -293,6 +293,9 @@
String primaryCpuAbi = AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting);
if (primaryCpuAbi != null && !VMRuntime.is64BitAbi(primaryCpuAbi)) {
final String nativeLibPath = pkg.getNativeLibraryDir();
+ if (!(new File(nativeLibPath).exists())) {
+ return;
+ }
try {
mInstaller.linkNativeLibraryDirectory(volumeUuid, packageName,
nativeLibPath, userId);
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index 3e849f8..dd66130 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -133,7 +133,7 @@
}
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
@NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType,
- @PackageManager.ResolveInfoFlags long flags,
+ @PackageManager.ResolveInfoFlagsBits long flags,
@PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
@@ -338,8 +338,8 @@
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@NonNull
- int[] getPackageGids(@NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
- @UserIdInt int userId);
+ int[] getPackageGids(@NonNull String packageName,
+ @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
int getTargetSdkVersion(@NonNull String packageName);
@@ -351,12 +351,12 @@
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
ActivityInfo getReceiverInfo(@NonNull ComponentName component,
- @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId);
+ @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
ParceledListSlice<SharedLibraryInfo> getSharedLibraries(@NonNull String packageName,
- @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId);
+ @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
boolean canRequestPackageInstalls(@NonNull String packageName, int callingUid,
@@ -369,18 +369,18 @@
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
List<VersionedPackage> getPackagesUsingSharedLibrary(@NonNull SharedLibraryInfo libInfo,
- @PackageManager.PackageInfoFlags long flags, int callingUid, @UserIdInt int userId);
+ @PackageManager.PackageInfoFlagsBits long flags, int callingUid, @UserIdInt int userId);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
- @NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+ @NonNull String packageName, @PackageManager.PackageInfoFlagsBits long flags,
@UserIdInt int userId);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
ProviderInfo getProviderInfo(@NonNull ComponentName component,
- @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId);
+ @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
@@ -439,17 +439,18 @@
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@NonNull
ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(@NonNull String[] permissions,
- @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId);
+ @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@NonNull
- List<ApplicationInfo> getInstalledApplications(@PackageManager.ApplicationInfoFlags long flags,
- @UserIdInt int userId, int callingUid);
+ List<ApplicationInfo> getInstalledApplications(
+ @PackageManager.ApplicationInfoFlagsBits long flags, @UserIdInt int userId,
+ int callingUid);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
ProviderInfo resolveContentProvider(@NonNull String name,
- @PackageManager.ResolveInfoFlags long flags, @UserIdInt int userId, int callingUid);
+ @PackageManager.ResolveInfoFlagsBits long flags, @UserIdInt int userId, int callingUid);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
@@ -463,7 +464,7 @@
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@NonNull
ParceledListSlice<ProviderInfo> queryContentProviders(@Nullable String processName, int uid,
- @PackageManager.ComponentInfoFlags long flags, @Nullable String metaDataKey);
+ @PackageManager.ComponentInfoFlagsBits long flags, @Nullable String metaDataKey);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
@@ -560,7 +561,7 @@
boolean canQueryPackage(int callingUid, @Nullable String targetPackageName);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
- int getPackageUid(@NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+ int getPackageUid(@NonNull String packageName, @PackageManager.PackageInfoFlagsBits long flags,
@UserIdInt int userId);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 59250c5..627970e 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -429,7 +429,7 @@
}
public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
@PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
int filterCallingUid, int userId, boolean resolveForStart,
boolean allowDynamicSplits) {
@@ -541,14 +541,14 @@
}
public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
return queryIntentActivitiesInternal(
intent, resolvedType, flags, 0 /*privateResolveFlags*/, Binder.getCallingUid(),
userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);
}
public final @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId,
int callingUid, boolean includeInstantApps) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
enforceCrossUserOrProfilePermission(callingUid,
@@ -625,7 +625,7 @@
}
protected @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId,
int callingUid, String instantAppPkgName) {
// reader
String pkgName = intent.getPackage();
@@ -653,7 +653,7 @@
}
public @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(
- Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits,
String pkgName, String instantAppPkgName) {
// reader
@@ -786,7 +786,7 @@
}
public final ActivityInfo getActivityInfo(ComponentName component,
- @PackageManager.ResolveInfoFlags long flags, int userId) {
+ @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId);
}
@@ -797,7 +797,7 @@
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
public final ActivityInfo getActivityInfoInternal(ComponentName component,
- @PackageManager.ResolveInfoFlags long flags, int filterCallingUid, int userId) {
+ @PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId);
@@ -811,7 +811,7 @@
}
protected ActivityInfo getActivityInfoInternalBody(ComponentName component,
- @PackageManager.ResolveInfoFlags long flags, int filterCallingUid, int userId) {
+ @PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) {
ParsedActivity a = mComponentResolver.getActivity(component);
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
@@ -879,7 +879,7 @@
}
public final ApplicationInfo getApplicationInfo(String packageName,
- @PackageManager.ApplicationInfoFlags long flags, int userId) {
+ @PackageManager.ApplicationInfoFlagsBits long flags, int userId) {
return getApplicationInfoInternal(packageName, flags, Binder.getCallingUid(), userId);
}
@@ -890,7 +890,7 @@
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
public final ApplicationInfo getApplicationInfoInternal(String packageName,
- @PackageManager.ApplicationInfoFlags long flags,
+ @PackageManager.ApplicationInfoFlagsBits long flags,
int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForApplication(flags, userId);
@@ -905,7 +905,7 @@
}
protected ApplicationInfo getApplicationInfoInternalBody(String packageName,
- @PackageManager.ApplicationInfoFlags long flags,
+ @PackageManager.ApplicationInfoFlagsBits long flags,
int filterCallingUid, int userId) {
// writer
// Normalize package name to handle renamed packages and static libs
@@ -1161,7 +1161,7 @@
}
public final CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags, int sourceUserId,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int sourceUserId,
int parentUserId) {
if (!mUserManager.hasUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
sourceUserId)) {
@@ -1426,7 +1426,7 @@
}
private List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result,
- Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
int userId, boolean resolveForStart, boolean isRequesterInstantApp) {
// first, check to see if we've got an instant app already installed
final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0;
@@ -1532,7 +1532,7 @@
}
public final PackageInfo generatePackageInfo(PackageStateInternal ps,
- @PackageManager.PackageInfoFlags long flags, int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, int userId) {
if (!mUserManager.exists(userId)) return null;
if (ps == null) {
return null;
@@ -1607,7 +1607,7 @@
}
public final PackageInfo getPackageInfo(String packageName,
- @PackageManager.PackageInfoFlags long flags, int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, int userId) {
return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
flags, Binder.getCallingUid(), userId);
}
@@ -1808,7 +1808,7 @@
@Nullable
private CrossProfileDomainInfo createForwardingResolveInfo(
@NonNull CrossProfileIntentFilter filter, @NonNull Intent intent,
- @Nullable String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ @Nullable String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
int sourceUserId) {
int targetUserId = filter.getTargetUserId();
if (!isUserEnabled(targetUserId)) {
@@ -1970,7 +1970,7 @@
}
public final ServiceInfo getServiceInfo(ComponentName component,
- @PackageManager.ResolveInfoFlags long flags, int userId) {
+ @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId);
@@ -1981,7 +1981,7 @@
}
protected ServiceInfo getServiceInfoBody(ComponentName component,
- @PackageManager.ResolveInfoFlags long flags, int userId, int callingUid) {
+ @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid) {
ParsedService s = mComponentResolver.getService(component);
if (DEBUG_PACKAGE_INFO) {
Log.v(
@@ -2233,7 +2233,7 @@
}
private boolean filterStaticSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
- int userId, @PackageManager.ComponentInfoFlags long flags) {
+ int userId, @PackageManager.ComponentInfoFlagsBits long flags) {
// Callers can access only the static shared libs they depend on, otherwise they need to
// explicitly ask for the static shared libraries given the caller is allowed to access
// all static libs.
@@ -2289,7 +2289,7 @@
}
private boolean filterSdkLibPackage(@Nullable PackageStateInternal ps, int uid,
- int userId, @PackageManager.ComponentInfoFlags long flags) {
+ int userId, @PackageManager.ComponentInfoFlagsBits long flags) {
// Callers can access only the SDK libs they depend on, otherwise they need to
// explicitly ask for the SDKs given the caller is allowed to access
// all shared libs.
@@ -2346,7 +2346,7 @@
@Override
public final boolean filterSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
- int userId, @PackageManager.ComponentInfoFlags long flags) {
+ int userId, @PackageManager.ComponentInfoFlagsBits long flags) {
return filterStaticSharedLibPackage(ps, uid, userId, flags) || filterSdkLibPackage(ps, uid,
userId, flags);
}
@@ -2445,7 +2445,7 @@
* activity was not set by the DPC.
*/
public final boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent,
- int userId, String resolvedType, @PackageManager.ResolveInfoFlags long flags) {
+ int userId, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags) {
return intent.isImplicitImageCaptureIntent() && !isPersistentPreferredActivitySetByDpm(
intent, userId, resolvedType, flags);
}
@@ -2486,7 +2486,7 @@
private boolean isInstantAppResolutionAllowed(
Intent intent, List<ResolveInfo> resolvedActivities, int userId,
- boolean skipPackageCheck, @PackageManager.ResolveInfoFlags long flags) {
+ boolean skipPackageCheck, @PackageManager.ResolveInfoFlagsBits long flags) {
if (mInstantAppResolverConnection == null) {
return false;
}
@@ -2526,7 +2526,7 @@
// Or if there's already an ephemeral app installed that handles the action
protected boolean isInstantAppResolutionAllowedBody(
Intent intent, List<ResolveInfo> resolvedActivities, int userId,
- boolean skipPackageCheck, @PackageManager.ResolveInfoFlags long flags) {
+ boolean skipPackageCheck, @PackageManager.ResolveInfoFlagsBits long flags) {
final int count = (resolvedActivities == null ? 0 : resolvedActivities.size());
for (int n = 0; n < count; n++) {
final ResolveInfo info = resolvedActivities.get(n);
@@ -2558,7 +2558,7 @@
}
private boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags) {
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags) {
PersistentPreferredIntentResolver ppir =
mSettings.getPersistentPreferredActivities(userId);
//TODO(b/158003772): Remove double query
@@ -2716,7 +2716,7 @@
}
public int getPackageUidInternal(String packageName,
- @PackageManager.PackageInfoFlags long flags, int userId, int callingUid) {
+ @PackageManager.PackageInfoFlagsBits long flags, int userId, int callingUid) {
// reader
final AndroidPackage p = mPackages.get(packageName);
if (p != null && AndroidPackageUtils.isMatchForSystemOnly(p, flags)) {
@@ -3239,7 +3239,7 @@
// The body of findPreferredActivity.
protected PackageManagerService.FindPreferredActivityBodyResult findPreferredActivityBody(
- Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
List<ResolveInfo> query, boolean always,
boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered,
int callingUid, boolean isDeviceProvisioned) {
@@ -3448,7 +3448,7 @@
}
public final PackageManagerService.FindPreferredActivityBodyResult findPreferredActivityInternal(
- Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
List<ResolveInfo> query, boolean always,
boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
@@ -3465,7 +3465,7 @@
}
public final ResolveInfo findPersistentPreferredActivityLP(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
List<ResolveInfo> query, boolean debug, int userId) {
final int n = query.size();
PersistentPreferredIntentResolver ppir =
@@ -3663,7 +3663,7 @@
@Override
public int[] getPackageGids(@NonNull String packageName,
- @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForPackage(flags, userId);
@@ -3740,7 +3740,7 @@
@Nullable
@Override
public ActivityInfo getReceiverInfo(@NonNull ComponentName component,
- @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
+ @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId);
@@ -3773,7 +3773,7 @@
@Nullable
@Override
public ParceledListSlice<SharedLibraryInfo> getSharedLibraries(@NonNull String packageName,
- @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
if (!mUserManager.exists(userId)) return null;
Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0");
final int callingUid = Binder.getCallingUid();
@@ -3902,7 +3902,8 @@
@Override
public List<VersionedPackage> getPackagesUsingSharedLibrary(@NonNull SharedLibraryInfo libInfo,
- @PackageManager.PackageInfoFlags long flags, int callingUid, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, int callingUid,
+ @UserIdInt int userId) {
List<VersionedPackage> versionedPackages = null;
final ArrayMap<String, ? extends PackageStateInternal> packageStates = getPackageStates();
final int packageCount = packageStates.size();
@@ -3964,7 +3965,7 @@
@Nullable
@Override
public ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
- @NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+ @NonNull String packageName, @PackageManager.PackageInfoFlagsBits long flags,
@UserIdInt int userId) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES,
"getDeclaredSharedLibraries");
@@ -4041,7 +4042,7 @@
@Nullable
@Override
public ProviderInfo getProviderInfo(@NonNull ComponentName component,
- @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
+ @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId);
@@ -4515,7 +4516,7 @@
@NonNull
@Override
public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
- @NonNull String[] permissions, @PackageManager.PackageInfoFlags long flags,
+ @NonNull String[] permissions, @PackageManager.PackageInfoFlagsBits long flags,
@UserIdInt int userId) {
if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForPackage(flags, userId);
@@ -4536,7 +4537,7 @@
}
private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageStateInternal ps,
- String[] permissions, boolean[] tmp, @PackageManager.PackageInfoFlags long flags,
+ String[] permissions, boolean[] tmp, @PackageManager.PackageInfoFlagsBits long flags,
int userId) {
int numMatch = 0;
for (int i=0; i<permissions.length; i++) {
@@ -4578,7 +4579,7 @@
@NonNull
@Override
public List<ApplicationInfo> getInstalledApplications(
- @PackageManager.ApplicationInfoFlags long flags, @UserIdInt int userId,
+ @PackageManager.ApplicationInfoFlagsBits long flags, @UserIdInt int userId,
int callingUid) {
if (getInstantAppPackageName(callingUid) != null) {
return Collections.emptyList();
@@ -4655,7 +4656,8 @@
@Nullable
@Override
public ProviderInfo resolveContentProvider(@NonNull String name,
- @PackageManager.ResolveInfoFlags long flags, @UserIdInt int userId, int callingUid) {
+ @PackageManager.ResolveInfoFlagsBits long flags, @UserIdInt int userId,
+ int callingUid) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId);
final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId);
@@ -4744,7 +4746,8 @@
@NonNull
@Override
public ParceledListSlice<ProviderInfo> queryContentProviders(@Nullable String processName,
- int uid, @PackageManager.ComponentInfoFlags long flags, @Nullable String metaDataKey) {
+ int uid, @PackageManager.ComponentInfoFlagsBits long flags,
+ @Nullable String metaDataKey) {
final int callingUid = Binder.getCallingUid();
final int userId = processName != null ? UserHandle.getUserId(uid)
: UserHandle.getCallingUserId();
@@ -5290,7 +5293,7 @@
@Override
public int getPackageUid(@NonNull String packageName,
- @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
if (!mUserManager.exists(userId)) return -1;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForPackage(flags, userId);
diff --git a/services/core/java/com/android/server/pm/ComputerLocked.java b/services/core/java/com/android/server/pm/ComputerLocked.java
index f180d19..bd730e9 100644
--- a/services/core/java/com/android/server/pm/ComputerLocked.java
+++ b/services/core/java/com/android/server/pm/ComputerLocked.java
@@ -314,7 +314,7 @@
@Override
public int[] getPackageGids(@NonNull String packageName,
- @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
synchronized (mLock) {
return super.getPackageGids(packageName, flags, userId);
}
@@ -339,7 +339,7 @@
@Nullable
@Override
public ActivityInfo getReceiverInfo(@NonNull ComponentName component,
- @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
+ @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
synchronized (mLock) {
return super.getReceiverInfo(component, flags, userId);
}
@@ -348,7 +348,7 @@
@Nullable
@Override
public ParceledListSlice<SharedLibraryInfo> getSharedLibraries(@NonNull String packageName,
- @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
synchronized (mLock) {
return super.getSharedLibraries(packageName, flags, userId);
}
@@ -365,7 +365,8 @@
@Override
public List<VersionedPackage> getPackagesUsingSharedLibrary(@NonNull SharedLibraryInfo libInfo,
- @PackageManager.PackageInfoFlags long flags, int callingUid, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, int callingUid,
+ @UserIdInt int userId) {
synchronized (mLock) {
return super.getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId);
}
@@ -374,7 +375,7 @@
@Nullable
@Override
public ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
- @NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+ @NonNull String packageName, @PackageManager.PackageInfoFlagsBits long flags,
@UserIdInt int userId) {
synchronized (mLock) {
return super.getDeclaredSharedLibraries(packageName, flags, userId);
@@ -384,7 +385,7 @@
@Nullable
@Override
public ProviderInfo getProviderInfo(@NonNull ComponentName component,
- @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
+ @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
synchronized (mLock) {
return super.getProviderInfo(component, flags, userId);
}
@@ -498,7 +499,7 @@
@NonNull
@Override
public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
- @NonNull String[] permissions, @PackageManager.PackageInfoFlags long flags,
+ @NonNull String[] permissions, @PackageManager.PackageInfoFlagsBits long flags,
@UserIdInt int userId) {
synchronized (mLock) {
return super.getPackagesHoldingPermissions(permissions, flags, userId);
@@ -508,7 +509,7 @@
@NonNull
@Override
public List<ApplicationInfo> getInstalledApplications(
- @PackageManager.ApplicationInfoFlags long flags, @UserIdInt int userId,
+ @PackageManager.ApplicationInfoFlagsBits long flags, @UserIdInt int userId,
int callingUid) {
synchronized (mLock) {
return super.getInstalledApplications(flags, userId, callingUid);
@@ -518,7 +519,8 @@
@Nullable
@Override
public ProviderInfo resolveContentProvider(@NonNull String name,
- @PackageManager.ResolveInfoFlags long flags, @UserIdInt int userId, int callingUid) {
+ @PackageManager.ResolveInfoFlagsBits long flags, @UserIdInt int userId,
+ int callingUid) {
synchronized (mLock) {
return super.resolveContentProvider(name, flags, userId, callingUid);
}
@@ -544,7 +546,8 @@
@NonNull
@Override
public ParceledListSlice<ProviderInfo> queryContentProviders(@Nullable String processName,
- int uid, @PackageManager.ComponentInfoFlags long flags, @Nullable String metaDataKey) {
+ int uid, @PackageManager.ComponentInfoFlagsBits long flags,
+ @Nullable String metaDataKey) {
synchronized (mLock) {
return super.queryContentProviders(processName, uid, flags, metaDataKey);
}
@@ -717,7 +720,7 @@
@Override
public int getPackageUid(@NonNull String packageName,
- @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
synchronized (mLock) {
return super.getPackageUid(packageName, flags, userId);
}
diff --git a/services/core/java/com/android/server/pm/ComputerTracker.java b/services/core/java/com/android/server/pm/ComputerTracker.java
index a3cd092..e6ff836 100644
--- a/services/core/java/com/android/server/pm/ComputerTracker.java
+++ b/services/core/java/com/android/server/pm/ComputerTracker.java
@@ -97,7 +97,7 @@
}
public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
@PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
int filterCallingUid, int userId, boolean resolveForStart,
boolean allowDynamicSplits) {
@@ -111,7 +111,7 @@
}
}
public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
ThreadComputer current = snapshot();
try {
return current.mComputer.queryIntentActivitiesInternal(intent, resolvedType, flags,
@@ -121,7 +121,7 @@
}
}
public @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId,
int callingUid, boolean includeInstantApps) {
ThreadComputer current = snapshot();
try {
@@ -132,7 +132,7 @@
}
}
public @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(
- Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits,
String pkgName, String instantAppPkgName) {
ThreadComputer current = live();
@@ -145,7 +145,7 @@
}
}
public ActivityInfo getActivityInfo(ComponentName component,
- @PackageManager.ComponentInfoFlags long flags, int userId) {
+ @PackageManager.ComponentInfoFlagsBits long flags, int userId) {
ThreadComputer current = snapshot();
try {
return current.mComputer.getActivityInfo(component, flags, userId);
@@ -154,7 +154,7 @@
}
}
public ActivityInfo getActivityInfoInternal(ComponentName component,
- @PackageManager.ComponentInfoFlags long flags,
+ @PackageManager.ComponentInfoFlagsBits long flags,
int filterCallingUid, int userId) {
ThreadComputer current = live();
try {
@@ -191,7 +191,7 @@
}
}
public ApplicationInfo getApplicationInfo(String packageName,
- @PackageManager.ApplicationInfoFlags long flags, int userId) {
+ @PackageManager.ApplicationInfoFlagsBits long flags, int userId) {
ThreadComputer current = snapshot();
try {
return current.mComputer.getApplicationInfo(packageName, flags, userId);
@@ -200,7 +200,7 @@
}
}
public ApplicationInfo getApplicationInfoInternal(String packageName,
- @PackageManager.ApplicationInfoFlags long flags, int filterCallingUid, int userId) {
+ @PackageManager.ApplicationInfoFlagsBits long flags, int filterCallingUid, int userId) {
ThreadComputer current = live();
try {
return current.mComputer.getApplicationInfoInternal(packageName, flags,
@@ -227,7 +227,7 @@
}
}
public CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags, int sourceUserId,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int sourceUserId,
int parentUserId) {
ThreadComputer current = live();
try {
@@ -268,7 +268,7 @@
}
}
public PackageInfo generatePackageInfo(PackageStateInternal ps,
- @PackageManager.PackageInfoFlags long flags, int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, int userId) {
ThreadComputer current = live();
try {
return current.mComputer.generatePackageInfo(ps, flags, userId);
@@ -277,7 +277,7 @@
}
}
public PackageInfo getPackageInfo(String packageName,
- @PackageManager.PackageInfoFlags long flags, int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, int userId) {
ThreadComputer current = snapshot();
try {
return current.mComputer.getPackageInfo(packageName, flags, userId);
@@ -341,7 +341,7 @@
}
}
public ServiceInfo getServiceInfo(ComponentName component,
- @PackageManager.ComponentInfoFlags long flags, int userId) {
+ @PackageManager.ComponentInfoFlagsBits long flags, int userId) {
ThreadComputer current = live();
try {
return current.mComputer.getServiceInfo(component, flags, userId);
@@ -446,7 +446,7 @@
}
}
public boolean filterSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
- int userId, @PackageManager.ComponentInfoFlags long flags) {
+ int userId, @PackageManager.ComponentInfoFlagsBits long flags) {
ThreadComputer current = live();
try {
return current.mComputer.filterSharedLibPackage(ps, uid, userId, flags);
@@ -480,7 +480,7 @@
}
}
public boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent,
- int userId, String resolvedType, @PackageManager.ResolveInfoFlags long flags) {
+ int userId, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags) {
ThreadComputer current = live();
try {
return current.mComputer.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent,
@@ -553,7 +553,7 @@
}
}
public int getPackageUidInternal(String packageName,
- @PackageManager.PackageInfoFlags long flags, int userId, int callingUid) {
+ @PackageManager.PackageInfoFlagsBits long flags, int userId, int callingUid) {
ThreadComputer current = live();
try {
return current.mComputer.getPackageUidInternal(packageName, flags, userId,
@@ -648,7 +648,7 @@
}
}
public PackageManagerService.FindPreferredActivityBodyResult findPreferredActivityInternal(
- Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
List<ResolveInfo> query, boolean always, boolean removeMatches, boolean debug,
int userId, boolean queryMayBeFiltered) {
ThreadComputer current = live();
@@ -660,7 +660,7 @@
}
}
public ResolveInfo findPersistentPreferredActivityLP(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
List<ResolveInfo> query, boolean debug, int userId) {
ThreadComputer current = live();
try {
@@ -749,7 +749,7 @@
@Override
public int[] getPackageGids(@NonNull String packageName,
- @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getPackageGids(packageName, flags, userId);
}
@@ -774,7 +774,7 @@
@Nullable
@Override
public ActivityInfo getReceiverInfo(@NonNull ComponentName component,
- @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
+ @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getReceiverInfo(component, flags, userId);
}
@@ -783,7 +783,7 @@
@Nullable
@Override
public ParceledListSlice<SharedLibraryInfo> getSharedLibraries(@NonNull String packageName,
- @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getSharedLibraries(packageName, flags, userId);
}
@@ -808,7 +808,8 @@
@Override
public List<VersionedPackage> getPackagesUsingSharedLibrary(@NonNull SharedLibraryInfo libInfo,
- @PackageManager.PackageInfoFlags long flags, int callingUid, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, int callingUid,
+ @UserIdInt int userId) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getPackagesUsingSharedLibrary(libInfo, flags, callingUid,
userId);
@@ -818,7 +819,7 @@
@Nullable
@Override
public ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
- @NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+ @NonNull String packageName, @PackageManager.PackageInfoFlagsBits long flags,
@UserIdInt int userId) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getDeclaredSharedLibraries(packageName, flags, userId);
@@ -828,7 +829,7 @@
@Nullable
@Override
public ProviderInfo getProviderInfo(@NonNull ComponentName component,
- @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
+ @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getProviderInfo(component, flags, userId);
}
@@ -943,7 +944,7 @@
@NonNull
@Override
public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
- @NonNull String[] permissions, @PackageManager.PackageInfoFlags long flags,
+ @NonNull String[] permissions, @PackageManager.PackageInfoFlagsBits long flags,
@UserIdInt int userId) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getPackagesHoldingPermissions(permissions, flags, userId);
@@ -953,7 +954,7 @@
@NonNull
@Override
public List<ApplicationInfo> getInstalledApplications(
- @PackageManager.ApplicationInfoFlags long flags, @UserIdInt int userId,
+ @PackageManager.ApplicationInfoFlagsBits long flags, @UserIdInt int userId,
int callingUid) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getInstalledApplications(flags, userId, callingUid);
@@ -963,7 +964,8 @@
@Nullable
@Override
public ProviderInfo resolveContentProvider(@NonNull String name,
- @PackageManager.ResolveInfoFlags long flags, @UserIdInt int userId, int callingUid) {
+ @PackageManager.ResolveInfoFlagsBits long flags, @UserIdInt int userId,
+ int callingUid) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.resolveContentProvider(name, flags, userId, callingUid);
}
@@ -990,7 +992,8 @@
@NonNull
@Override
public ParceledListSlice<ProviderInfo> queryContentProviders(@Nullable String processName,
- int uid, @PackageManager.ComponentInfoFlags long flags, @Nullable String metaDataKey) {
+ int uid, @PackageManager.ComponentInfoFlagsBits long flags,
+ @Nullable String metaDataKey) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.queryContentProviders(processName, uid, flags, metaDataKey);
}
@@ -1164,7 +1167,7 @@
@Override
public int getPackageUid(@NonNull String packageName,
- @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getPackageUid(packageName, flags, userId);
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 9c11cd4..6e6773f 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -121,6 +121,7 @@
public void onStart() {
publishBinderService(Context.LAUNCHER_APPS_SERVICE, mLauncherAppsImpl);
mLauncherAppsImpl.registerLoadingProgressForIncrementalApps();
+ LocalServices.addService(LauncherAppsServiceInternal.class, mLauncherAppsImpl.mInternal);
}
static class BroadcastCookie {
@@ -137,6 +138,17 @@
}
}
+ /**
+ * Local system service interface.
+ * @hide Only for use within system server
+ */
+ public abstract static class LauncherAppsServiceInternal {
+ /** Same as startShortcut except supports forwarding of caller uid/pid. */
+ public abstract boolean startShortcut(int callerUid, int callerPid, String callingPackage,
+ String packageName, String featureId, String shortcutId, Rect sourceBounds,
+ Bundle startActivityOptions, int targetUserId);
+ }
+
@VisibleForTesting
static class LauncherAppsImpl extends ILauncherApps.Stub {
private static final boolean DEBUG = false;
@@ -168,6 +180,8 @@
private PackageInstallerService mPackageInstallerService;
+ final LauncherAppsServiceInternal mInternal;
+
public LauncherAppsImpl(Context context) {
mContext = context;
mIPM = AppGlobals.getPackageManager();
@@ -189,6 +203,7 @@
mShortcutServiceInternal.addShortcutChangeCallback(mShortcutChangeHandler);
mCallbackHandler = BackgroundThread.getHandler();
mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ mInternal = new LocalService();
}
@VisibleForTesting
@@ -359,11 +374,15 @@
* group.
*/
private boolean canAccessProfile(int targetUserId, String message) {
- final int callingUserId = injectCallingUserId();
+ return canAccessProfile(injectBinderCallingUid(), injectCallingUserId(),
+ injectBinderCallingPid(), targetUserId, message);
+ }
+
+ private boolean canAccessProfile(int callingUid, int callingUserId, int callingPid,
+ int targetUserId, String message) {
if (targetUserId == callingUserId) return true;
- if (injectHasInteractAcrossUsersFullPermission(injectBinderCallingPid(),
- injectBinderCallingUid())) {
+ if (injectHasInteractAcrossUsersFullPermission(callingPid, callingUid)) {
return true;
}
@@ -379,25 +398,29 @@
injectRestoreCallingIdentity(ident);
}
- return mUserManagerInternal.isProfileAccessible(injectCallingUserId(), targetUserId,
+ return mUserManagerInternal.isProfileAccessible(callingUserId, targetUserId,
message, true);
}
+ private void verifyCallingPackage(String callingPackage) {
+ verifyCallingPackage(callingPackage, injectBinderCallingUid());
+ }
+
@VisibleForTesting // We override it in unit tests
- void verifyCallingPackage(String callingPackage) {
+ void verifyCallingPackage(String callingPackage, int callerUid) {
int packageUid = -1;
try {
packageUid = mIPM.getPackageUid(callingPackage,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_UNINSTALLED_PACKAGES,
- UserHandle.getUserId(getCallingUid()));
+ UserHandle.getUserId(callerUid));
} catch (RemoteException ignore) {
}
if (packageUid < 0) {
Log.e(TAG, "Package not found: " + callingPackage);
}
- if (packageUid != injectBinderCallingUid()) {
+ if (packageUid != callerUid) {
throw new SecurityException("Calling package name mismatch");
}
}
@@ -797,9 +820,15 @@
}
private void ensureShortcutPermission(@NonNull String callingPackage) {
- verifyCallingPackage(callingPackage);
- if (!mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
- callingPackage, injectBinderCallingPid(), injectBinderCallingUid())) {
+ ensureShortcutPermission(injectBinderCallingUid(), injectBinderCallingPid(),
+ callingPackage);
+ }
+
+ private void ensureShortcutPermission(int callerUid, int callerPid,
+ @NonNull String callingPackage) {
+ verifyCallingPackage(callingPackage, callerUid);
+ if (!mShortcutServiceInternal.hasShortcutHostPermission(UserHandle.getUserId(callerUid),
+ callingPackage, callerPid, callerUid)) {
throw new SecurityException("Caller can't access shortcut information");
}
}
@@ -989,20 +1018,28 @@
public boolean startShortcut(String callingPackage, String packageName, String featureId,
String shortcutId, Rect sourceBounds, Bundle startActivityOptions,
int targetUserId) {
- verifyCallingPackage(callingPackage);
+ return startShortcutInner(injectBinderCallingUid(), injectBinderCallingPid(),
+ injectCallingUserId(), callingPackage, packageName, featureId, shortcutId,
+ sourceBounds, startActivityOptions, targetUserId);
+ }
+
+ private boolean startShortcutInner(int callerUid, int callerPid, int callingUserId,
+ String callingPackage, String packageName, String featureId, String shortcutId,
+ Rect sourceBounds, Bundle startActivityOptions, int targetUserId) {
+ verifyCallingPackage(callingPackage, callerUid);
if (!canAccessProfile(targetUserId, "Cannot start activity")) {
return false;
}
// Even without the permission, pinned shortcuts are always launchable.
- if (!mShortcutServiceInternal.isPinnedByCaller(getCallingUserId(),
+ if (!mShortcutServiceInternal.isPinnedByCaller(callingUserId,
callingPackage, packageName, shortcutId, targetUserId)) {
- ensureShortcutPermission(callingPackage);
+ ensureShortcutPermission(callerUid, callerPid, callingPackage);
}
final Intent[] intents = mShortcutServiceInternal.createShortcutIntents(
- getCallingUserId(), callingPackage, packageName, shortcutId, targetUserId,
- injectBinderCallingPid(), injectBinderCallingUid());
+ callingUserId, callingPackage, packageName, shortcutId, targetUserId,
+ callerPid, callerUid);
if (intents == null || intents.length == 0) {
return false;
}
@@ -1023,7 +1060,7 @@
// Replace theme for splash screen
final String splashScreenThemeResName =
- mShortcutServiceInternal.getShortcutStartingThemeResName(getCallingUserId(),
+ mShortcutServiceInternal.getShortcutStartingThemeResName(callingUserId,
callingPackage, packageName, shortcutId, targetUserId);
if (splashScreenThemeResName != null && !splashScreenThemeResName.isEmpty()) {
if (startActivityOptions == null) {
@@ -1809,5 +1846,16 @@
}
}
}
+
+ final class LocalService extends LauncherAppsServiceInternal {
+ @Override
+ public boolean startShortcut(int callerUid, int callerPid, String callingPackage,
+ String packageName, String featureId, String shortcutId, Rect sourceBounds,
+ Bundle startActivityOptions, int targetUserId) {
+ return LauncherAppsImpl.this.startShortcutInner(callerUid, callerPid,
+ UserHandle.getUserId(callerUid), callingPackage, packageName, featureId,
+ shortcutId, sourceBounds, startActivityOptions, targetUserId);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index fa23000..7ceae61 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2582,7 +2582,7 @@
}
private PackageInfo generatePackageInfo(@NonNull PackageStateInternal ps,
- @PackageManager.PackageInfoFlags long flags, int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, int userId) {
return mComputer.generatePackageInfo(ps, flags, userId);
}
@@ -2622,13 +2622,13 @@
@Override
public PackageInfo getPackageInfo(String packageName,
- @PackageManager.PackageInfoFlags long flags, int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, int userId) {
return mComputer.getPackageInfo(packageName, flags, userId);
}
@Override
public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage,
- @PackageManager.PackageInfoFlags long flags, int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, int userId) {
return mComputer.getPackageInfoInternal(versionedPackage.getPackageName(),
versionedPackage.getLongVersionCode(), flags, Binder.getCallingUid(), userId);
}
@@ -2665,7 +2665,7 @@
}
private boolean filterSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
- int userId, @PackageManager.ComponentInfoFlags long flags) {
+ int userId, @PackageManager.ComponentInfoFlagsBits long flags) {
return mComputer.filterSharedLibPackage(ps, uid, userId, flags);
}
@@ -2681,17 +2681,17 @@
@Override
public int getPackageUid(@NonNull String packageName,
- @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
return mComputer.getPackageUid(packageName, flags, userId);
}
private int getPackageUidInternal(String packageName,
- @PackageManager.PackageInfoFlags long flags, int userId, int callingUid) {
+ @PackageManager.PackageInfoFlagsBits long flags, int userId, int callingUid) {
return mComputer.getPackageUidInternal(packageName, flags, userId, callingUid);
}
@Override
- public int[] getPackageGids(String packageName, @PackageManager.PackageInfoFlags long flags,
+ public int[] getPackageGids(String packageName, @PackageManager.PackageInfoFlagsBits long flags,
int userId) {
return mComputer.getPackageGids(packageName, flags, userId);
}
@@ -2706,14 +2706,14 @@
}
private ApplicationInfo generateApplicationInfoFromSettings(String packageName,
- @PackageManager.ApplicationInfoFlags long flags, int filterCallingUid, int userId) {
+ @PackageManager.ApplicationInfoFlagsBits long flags, int filterCallingUid, int userId) {
return mComputer.generateApplicationInfoFromSettings(packageName, flags, filterCallingUid,
userId);
}
@Override
public ApplicationInfo getApplicationInfo(String packageName,
- @PackageManager.ApplicationInfoFlags long flags, int userId) {
+ @PackageManager.ApplicationInfoFlagsBits long flags, int userId) {
return mComputer.getApplicationInfo(packageName, flags, userId);
}
@@ -2724,7 +2724,7 @@
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
private ApplicationInfo getApplicationInfoInternal(String packageName,
- @PackageManager.ApplicationInfoFlags long flags,
+ @PackageManager.ApplicationInfoFlagsBits long flags,
int filterCallingUid, int userId) {
return mComputer.getApplicationInfoInternal(packageName, flags,
filterCallingUid, userId);
@@ -3011,7 +3011,7 @@
@Override
public ActivityInfo getActivityInfo(ComponentName component,
- @PackageManager.ComponentInfoFlags long flags, int userId) {
+ @PackageManager.ComponentInfoFlagsBits long flags, int userId) {
return mComputer.getActivityInfo(component, flags, userId);
}
@@ -3022,7 +3022,7 @@
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
private ActivityInfo getActivityInfoInternal(ComponentName component,
- @PackageManager.ComponentInfoFlags long flags, int filterCallingUid, int userId) {
+ @PackageManager.ComponentInfoFlagsBits long flags, int filterCallingUid, int userId) {
return mComputer.getActivityInfoInternal(component, flags,
filterCallingUid, userId);
}
@@ -3036,42 +3036,42 @@
@Override
public ActivityInfo getReceiverInfo(ComponentName component,
- @PackageManager.ComponentInfoFlags long flags, int userId) {
+ @PackageManager.ComponentInfoFlagsBits long flags, int userId) {
return mComputer.getReceiverInfo(component, flags, userId);
}
@Override
public ParceledListSlice<SharedLibraryInfo> getSharedLibraries(String packageName,
- @PackageManager.PackageInfoFlags long flags, int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, int userId) {
return mComputer.getSharedLibraries(packageName, flags, userId);
}
@Nullable
@Override
public ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
- @NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+ @NonNull String packageName, @PackageManager.PackageInfoFlagsBits long flags,
@NonNull int userId) {
return mComputer.getDeclaredSharedLibraries(packageName, flags, userId);
}
@Nullable
List<VersionedPackage> getPackagesUsingSharedLibrary(
- SharedLibraryInfo libInfo, @PackageManager.PackageInfoFlags long flags, int callingUid,
- int userId) {
+ SharedLibraryInfo libInfo, @PackageManager.PackageInfoFlagsBits long flags,
+ int callingUid, int userId) {
return mComputer.getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId);
}
@Nullable
@Override
public ServiceInfo getServiceInfo(@NonNull ComponentName component,
- @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
+ @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
return mComputer.getServiceInfo(component, flags, userId);
}
@Nullable
@Override
public ProviderInfo getProviderInfo(@NonNull ComponentName component,
- @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
+ @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
return mComputer.getProviderInfo(component, flags, userId);
}
@@ -3360,7 +3360,7 @@
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
- @PackageManager.ResolveInfoFlags long flags, int userId) {
+ @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
return mResolveIntentHelper.resolveIntentInternal(intent, resolvedType, flags,
0 /*privateResolveFlags*/, userId, false, Binder.getCallingUid());
}
@@ -3405,7 +3405,7 @@
*/
@GuardedBy("mLock")
boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags) {
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags) {
return mComputer.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
resolvedType, flags);
}
@@ -3413,7 +3413,7 @@
@GuardedBy("mLock")
ResolveInfo findPersistentPreferredActivityLP(Intent intent,
String resolvedType,
- @PackageManager.ResolveInfoFlags long flags, List<ResolveInfo> query, boolean debug,
+ @PackageManager.ResolveInfoFlagsBits long flags, List<ResolveInfo> query, boolean debug,
int userId) {
return mComputer.findPersistentPreferredActivityLP(intent,
resolvedType, flags, query, debug, userId);
@@ -3427,7 +3427,7 @@
}
FindPreferredActivityBodyResult findPreferredActivityInternal(
- Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
List<ResolveInfo> query, boolean always,
boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
return mComputer.findPreferredActivityInternal(
@@ -3457,7 +3457,7 @@
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivities(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
@@ -3477,13 +3477,13 @@
}
@NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
return mComputer.queryIntentActivitiesInternal(intent,
resolvedType, flags, userId);
}
@NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
@PrivateResolveFlags long privateResolveFlags, int filterCallingUid, int userId,
boolean resolveForStart, boolean allowDynamicSplits) {
return mComputer.queryIntentActivitiesInternal(intent,
@@ -3492,7 +3492,7 @@
}
private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags, int sourceUserId,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int sourceUserId,
int parentUserId) {
return mComputer.getCrossProfileDomainPreferredLpr(intent,
resolvedType, flags, sourceUserId, parentUserId);
@@ -3520,21 +3520,21 @@
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
Intent[] specifics, String[] specificTypes, Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
return new ParceledListSlice<>(mResolveIntentHelper.queryIntentActivityOptionsInternal(
caller, specifics, specificTypes, intent, resolvedType, flags, userId));
}
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
return new ParceledListSlice<>(mResolveIntentHelper.queryIntentReceiversInternal(intent,
resolvedType, flags, userId, Binder.getCallingUid()));
}
@Override
public ResolveInfo resolveService(Intent intent, String resolvedType,
- @PackageManager.ResolveInfoFlags long flags, int userId) {
+ @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
final int callingUid = Binder.getCallingUid();
return mResolveIntentHelper.resolveServiceInternal(intent, resolvedType, flags, userId,
callingUid);
@@ -3542,14 +3542,14 @@
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentServices(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
final int callingUid = Binder.getCallingUid();
return new ParceledListSlice<>(queryIntentServicesInternal(
intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/));
}
@NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId,
int callingUid, boolean includeInstantApps) {
return mComputer.queryIntentServicesInternal(intent,
resolvedType, flags, userId, callingUid,
@@ -3558,27 +3558,27 @@
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentContentProviders(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
return new ParceledListSlice<>(mResolveIntentHelper.queryIntentContentProvidersInternal(
intent, resolvedType, flags, userId));
}
@Override
public ParceledListSlice<PackageInfo> getInstalledPackages(
- @PackageManager.PackageInfoFlags long flags, int userId) {
+ @PackageManager.PackageInfoFlagsBits long flags, int userId) {
return mComputer.getInstalledPackages(flags, userId);
}
@Override
public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
- @NonNull String[] permissions, @PackageManager.PackageInfoFlags long flags,
+ @NonNull String[] permissions, @PackageManager.PackageInfoFlagsBits long flags,
@UserIdInt int userId) {
return mComputer.getPackagesHoldingPermissions(permissions, flags, userId);
}
@Override
public ParceledListSlice<ApplicationInfo> getInstalledApplications(
- @PackageManager.ApplicationInfoFlags long flags, int userId) {
+ @PackageManager.ApplicationInfoFlagsBits long flags, int userId) {
final int callingUid = Binder.getCallingUid();
return new ParceledListSlice<>(
mComputer.getInstalledApplications(flags, userId, callingUid));
@@ -3683,7 +3683,7 @@
@Override
public ProviderInfo resolveContentProvider(String name,
- @PackageManager.ResolveInfoFlags long flags, int userId) {
+ @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
return mComputer.resolveContentProvider(name, flags, userId, Binder.getCallingUid());
}
@@ -3695,7 +3695,8 @@
@NonNull
@Override
public ParceledListSlice<ProviderInfo> queryContentProviders(@Nullable String processName,
- int uid, @PackageManager.ComponentInfoFlags long flags, @Nullable String metaDataKey) {
+ int uid, @PackageManager.ComponentInfoFlagsBits long flags,
+ @Nullable String metaDataKey) {
return mComputer.queryContentProviders(processName, uid, flags, metaDataKey);
}
@@ -7575,7 +7576,7 @@
private class PackageManagerInternalImpl extends PackageManagerInternal {
@Override
public List<ApplicationInfo> getInstalledApplications(
- @PackageManager.ApplicationInfoFlags long flags, int userId, int callingUid) {
+ @PackageManager.ApplicationInfoFlagsBits long flags, int userId, int callingUid) {
return PackageManagerService.this.mComputer.getInstalledApplications(flags, userId,
callingUid);
}
@@ -7770,7 +7771,7 @@
@Override
public PackageInfo getPackageInfo(
- String packageName, @PackageManager.PackageInfoFlags long flags,
+ String packageName, @PackageManager.PackageInfoFlagsBits long flags,
int filterCallingUid, int userId) {
return PackageManagerService.this.mComputer
.getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
@@ -7899,15 +7900,15 @@
}
@Override
- public int getPackageUid(String packageName, @PackageManager.PackageInfoFlags long flags,
- int userId) {
+ public int getPackageUid(String packageName,
+ @PackageManager.PackageInfoFlagsBits long flags, int userId) {
return PackageManagerService.this
.getPackageUidInternal(packageName, flags, userId, Process.SYSTEM_UID);
}
@Override
public ApplicationInfo getApplicationInfo(
- String packageName, @PackageManager.ApplicationInfoFlags long flags,
+ String packageName, @PackageManager.ApplicationInfoFlagsBits long flags,
int filterCallingUid, int userId) {
return PackageManagerService.this
.getApplicationInfoInternal(packageName, flags, filterCallingUid, userId);
@@ -7915,7 +7916,7 @@
@Override
public ActivityInfo getActivityInfo(
- ComponentName component, @PackageManager.ComponentInfoFlags long flags,
+ ComponentName component, @PackageManager.ComponentInfoFlagsBits long flags,
int filterCallingUid, int userId) {
return PackageManagerService.this
.getActivityInfoInternal(component, flags, filterCallingUid, userId);
@@ -7923,7 +7924,7 @@
@Override
public List<ResolveInfo> queryIntentActivities(
- Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
int filterCallingUid, int userId) {
return PackageManagerService.this
.queryIntentActivitiesInternal(intent, resolvedType, flags, 0, filterCallingUid,
@@ -7932,7 +7933,7 @@
@Override
public List<ResolveInfo> queryIntentReceivers(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
int filterCallingUid, int userId) {
return PackageManagerService.this.mResolveIntentHelper.queryIntentReceiversInternal(
intent, resolvedType, flags, userId, filterCallingUid);
@@ -7940,7 +7941,7 @@
@Override
public List<ResolveInfo> queryIntentServices(
- Intent intent, @PackageManager.ResolveInfoFlags long flags, int callingUid,
+ Intent intent, @PackageManager.ResolveInfoFlagsBits long flags, int callingUid,
int userId) {
final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
return PackageManagerService.this
@@ -8209,7 +8210,7 @@
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
- @PackageManager.ResolveInfoFlags long flags,
+ @PackageManager.ResolveInfoFlagsBits long flags,
@PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
boolean resolveForStart, int filterCallingUid) {
return mResolveIntentHelper.resolveIntentInternal(
@@ -8219,14 +8220,14 @@
@Override
public ResolveInfo resolveService(Intent intent, String resolvedType,
- @PackageManager.ResolveInfoFlags long flags, int userId, int callingUid) {
+ @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid) {
return mResolveIntentHelper.resolveServiceInternal(intent, resolvedType, flags, userId,
callingUid);
}
@Override
public ProviderInfo resolveContentProvider(String name,
- @PackageManager.ResolveInfoFlags long flags, int userId, int callingUid) {
+ @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid) {
return PackageManagerService.this.mComputer
.resolveContentProvider(name, flags, userId,callingUid);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 3b643b5..4082afd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -1079,7 +1079,7 @@
public static boolean hasAnyDomainApproval(
@NonNull DomainVerificationManagerInternal manager,
@NonNull PackageStateInternal pkgSetting, @NonNull Intent intent,
- @PackageManager.ResolveInfoFlags long resolveInfoFlags, @UserIdInt int userId) {
+ @PackageManager.ResolveInfoFlagsBits long resolveInfoFlags, @UserIdInt int userId) {
return manager.approvalLevelForDomain(pkgSetting, intent, resolveInfoFlags, userId)
> DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 0564e85..fdcf1fc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -18,6 +18,7 @@
import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
+import android.Manifest;
import android.accounts.IAccountManager;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -170,6 +171,20 @@
@Override
public int onCommand(String cmd) {
+ switch (Binder.getCallingUid()) {
+ case Process.ROOT_UID:
+ case Process.SHELL_UID:
+ break;
+ default:
+ // This is called from a test and is allowed as non-shell with the right permission
+ if ("install-incremental".equals(cmd)) {
+ mContext.enforceCallingPermission(Manifest.permission.USE_SYSTEM_DATA_LOADERS,
+ "Caller missing USE_SYSTEM_DATA_LOADERS permission to use " + cmd);
+ } else {
+ throw new IllegalArgumentException("Caller must be root or shell");
+ }
+ }
+
if (cmd == null) {
return handleDefaultCommands(cmd);
}
diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
index 8c91b16..802f701 100644
--- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java
+++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
@@ -75,8 +75,8 @@
}
private ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType,
- @PackageManager.ResolveInfoFlags long flags, List<ResolveInfo> query, boolean always,
- boolean removeMatches, boolean debug, int userId) {
+ @PackageManager.ResolveInfoFlagsBits long flags, List<ResolveInfo> query,
+ boolean always, boolean removeMatches, boolean debug, int userId) {
return findPreferredActivityNotLocked(
intent, resolvedType, flags, query, always, removeMatches, debug, userId,
UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID);
@@ -85,7 +85,7 @@
// TODO: handle preferred activities missing while user has amnesia
/** <b>must not hold {@link PackageManagerService.mLock}</b> */
public ResolveInfo findPreferredActivityNotLocked(
- Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
List<ResolveInfo> query, boolean always, boolean removeMatches, boolean debug,
int userId, boolean queryMayBeFiltered) {
if (Thread.holdsLock(mPm.mLock)) {
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index 0ee1f89..2aff90f 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -75,7 +75,7 @@
* since we need to allow the system to start any installed application.
*/
public ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
- @PackageManager.ResolveInfoFlags long flags,
+ @PackageManager.ResolveInfoFlagsBits long flags,
@PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
boolean resolveForStart, int filterCallingUid) {
try {
@@ -115,7 +115,7 @@
}
private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
- @PackageManager.ResolveInfoFlags long flags,
+ @PackageManager.ResolveInfoFlagsBits long flags,
@PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
List<ResolveInfo> query, int userId, boolean queryMayBeFiltered) {
if (query != null) {
@@ -278,7 +278,7 @@
// In this method, we have to know the actual calling UID, but in some cases Binder's
// call identity is removed, so the UID has to be passed in explicitly.
public @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId,
int filterCallingUid) {
if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
mPm.enforceCrossUserPermission(filterCallingUid, userId, false /*requireFullPermission*/,
@@ -374,7 +374,7 @@
public ResolveInfo resolveServiceInternal(Intent intent, String resolvedType,
- @PackageManager.ResolveInfoFlags long flags, int userId, int callingUid) {
+ @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid) {
if (!mPm.mUserManager.exists(userId)) return null;
flags = mPm.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
@@ -391,7 +391,7 @@
}
public @NonNull List<ResolveInfo> queryIntentContentProvidersInternal(
- Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
int userId) {
if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
@@ -533,7 +533,7 @@
public @NonNull List<ResolveInfo> queryIntentActivityOptionsInternal(ComponentName caller,
Intent[] specifics, String[] specificTypes, Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
flags = mPm.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index bc4c8b0..167ad3b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -839,7 +839,7 @@
@Override
public @NonNull List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying,
boolean excludePreCreated) {
- checkManageOrCreateUsersPermission("query users");
+ checkCreateUsersPermission("query users");
return getUsersInternal(excludePartial, excludeDying, excludePreCreated);
}
@@ -865,10 +865,10 @@
public List<UserInfo> getProfiles(@UserIdInt int userId, boolean enabledOnly) {
boolean returnFullInfo;
if (userId != UserHandle.getCallingUserId()) {
- checkManageOrCreateUsersPermission("getting profiles related to user " + userId);
+ checkQueryOrCreateUsersPermission("getting profiles related to user " + userId);
returnFullInfo = true;
} else {
- returnFullInfo = hasManageOrCreateUsersPermission();
+ returnFullInfo = hasCreateUsersPermission();
}
final long ident = Binder.clearCallingIdentity();
try {
@@ -898,7 +898,7 @@
public int[] getProfileIds(@UserIdInt int userId, @Nullable String userType,
boolean enabledOnly) {
if (userId != UserHandle.getCallingUserId()) {
- checkManageOrCreateUsersPermission("getting profiles related to user " + userId);
+ checkQueryOrCreateUsersPermission("getting profiles related to user " + userId);
}
final long ident = Binder.clearCallingIdentity();
try {
@@ -987,7 +987,7 @@
@Override
public boolean isSameProfileGroup(@UserIdInt int userId, int otherUserId) {
if (userId == otherUserId) return true;
- checkManageUsersPermission("check if in the same profile group");
+ checkQueryUsersPermission("check if in the same profile group");
return isSameProfileGroupNoChecks(userId, otherUserId);
}
@@ -1388,7 +1388,7 @@
@Override
public UserInfo getUserInfo(@UserIdInt int userId) {
- checkManageOrCreateUsersPermission("query user");
+ checkQueryOrCreateUsersPermission("query user");
synchronized (mUsersLock) {
return userWithName(getUserInfoLU(userId));
}
@@ -1519,7 +1519,7 @@
@Override
public boolean isProfile(@UserIdInt int userId) {
- checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isProfile");
+ checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isProfile");
synchronized (mUsersLock) {
UserInfo userInfo = getUserInfoLU(userId);
return userInfo != null && userInfo.isProfile();
@@ -1528,7 +1528,7 @@
@Override
public boolean isManagedProfile(@UserIdInt int userId) {
- checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isManagedProfile");
+ checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isManagedProfile");
synchronized (mUsersLock) {
UserInfo userInfo = getUserInfoLU(userId);
return userInfo != null && userInfo.isManagedProfile();
@@ -1592,7 +1592,7 @@
@Override
public String getUserName() {
final int callingUid = Binder.getCallingUid();
- if (!hasManageOrCreateUsersPermission()
+ if (!hasQueryOrCreateUsersPermission()
&& !hasPermissionGranted(
android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, callingUid)) {
throw new SecurityException("You need MANAGE_USERS or CREATE_USERS or "
@@ -1628,18 +1628,59 @@
}
}
+ /**
+ * Enforces that the calling user is in the same profile group as {@code userId} or that only
+ * the system UID or root's UID or apps that have the
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS INTERACT_ACROSS_USERS}
+ * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS}
+ * can make certain calls to the UserManager.
+ *
+ * @param name used as message if SecurityException is thrown
+ * @throws SecurityException if the caller lacks the required permissions.
+ */
private void checkManageOrInteractPermissionIfCallerInOtherProfileGroup(@UserIdInt int userId,
String name) {
final int callingUserId = UserHandle.getCallingUserId();
- if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId) ||
- hasManageUsersPermission()) {
+ if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId)) {
return;
}
- if (!hasPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS,
- Binder.getCallingUid())) {
- throw new SecurityException("You need INTERACT_ACROSS_USERS or MANAGE_USERS permission "
- + "to: check " + name);
+ if (hasManageUsersPermission()) {
+ return;
}
+ if (hasPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS,
+ Binder.getCallingUid())) {
+ return;
+ }
+ throw new SecurityException("You need INTERACT_ACROSS_USERS or MANAGE_USERS permission "
+ + "to: check " + name);
+ }
+
+ /**
+ * Enforces that the calling user is in the same profile group as {@code userId} or that only
+ * the system UID or root's UID or apps that have the
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS INTERACT_ACROSS_USERS}
+ * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or
+ * {@link android.Manifest.permission#QUERY_USERS QUERY_USERS}
+ * can make certain calls to the UserManager.
+ *
+ * @param name used as message if SecurityException is thrown
+ * @throws SecurityException if the caller lacks the required permissions.
+ */
+ private void checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(
+ @UserIdInt int userId, String name) {
+ final int callingUserId = UserHandle.getCallingUserId();
+ if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId)) {
+ return;
+ }
+ if (hasQueryUsersPermission()) {
+ return;
+ }
+ if (hasPermissionGranted(
+ Manifest.permission.INTERACT_ACROSS_USERS, Binder.getCallingUid())) {
+ return;
+ }
+ throw new SecurityException("You need INTERACT_ACROSS_USERS, MANAGE_USERS, or QUERY_USERS "
+ + "permission to: check " + name);
}
@Override
@@ -1667,7 +1708,7 @@
@Override
public boolean isRestricted(@UserIdInt int userId) {
if (userId != UserHandle.getCallingUserId()) {
- checkManageOrCreateUsersPermission("query isRestricted for user " + userId);
+ checkCreateUsersPermission("query isRestricted for user " + userId);
}
synchronized (mUsersLock) {
final UserInfo userInfo = getUserInfoLU(userId);
@@ -2147,7 +2188,7 @@
@Override
public List<EnforcingUser> getUserRestrictionSources(
String restrictionKey, @UserIdInt int userId) {
- checkManageUsersPermission("getUserRestrictionSource");
+ checkQueryUsersPermission("call getUserRestrictionSources.");
// Shortcut for the most common case
if (!hasUserRestriction(restrictionKey, userId)) {
@@ -2186,7 +2227,7 @@
@Override
public boolean hasBaseUserRestriction(String restrictionKey, @UserIdInt int userId) {
- checkManageOrCreateUsersPermission("hasBaseUserRestriction");
+ checkCreateUsersPermission("hasBaseUserRestriction");
if (!UserRestrictionsUtils.isValidRestriction(restrictionKey)) {
return false;
}
@@ -2403,7 +2444,7 @@
*/
@Override
public boolean canAddMoreUsersOfType(String userType) {
- checkManageOrCreateUsersPermission("check if more users can be added.");
+ checkCreateUsersPermission("check if more users can be added.");
final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
return userTypeDetails != null && canAddMoreUsersOfType(userTypeDetails);
}
@@ -2411,7 +2452,7 @@
/** Returns whether the creation of users of the given user type is enabled on this device. */
@Override
public boolean isUserTypeEnabled(String userType) {
- checkManageOrCreateUsersPermission("check if user type is enabled.");
+ checkCreateUsersPermission("check if user type is enabled.");
final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
return userTypeDetails != null && userTypeDetails.isEnabled();
}
@@ -2426,7 +2467,7 @@
@Override
public boolean canAddMoreProfilesToUser(String userType, @UserIdInt int userId,
boolean allowedToRemoveOne) {
- checkManageUsersPermission("check if more profiles can be added.");
+ checkQueryUsersPermission("check if more profiles can be added.");
final UserTypeDetails type = mUserTypes.get(userType);
if (type == null || !type.isEnabled()) {
return false;
@@ -2536,24 +2577,58 @@
*
* @param message used as message if SecurityException is thrown
* @throws SecurityException if the caller is not system or root
- * @see #hasManageOrCreateUsersPermission()
+ * @see #hasCreateUsersPermission()
*/
- private static final void checkManageOrCreateUsersPermission(String message) {
- if (!hasManageOrCreateUsersPermission()) {
+ private static final void checkCreateUsersPermission(String message) {
+ if (!hasCreateUsersPermission()) {
throw new SecurityException(
"You either need MANAGE_USERS or CREATE_USERS permission to: " + message);
}
}
/**
- * Similar to {@link #checkManageOrCreateUsersPermission(String)} but when the caller is tries
+ * Enforces that only the system UID or root's UID or apps that have the
+ * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or
+ * {@link android.Manifest.permission#QUERY_USERS QUERY_USERS}
+ * can make certain calls to the UserManager.
+ *
+ * @param message used as message if SecurityException is thrown
+ * @throws SecurityException if the caller lacks the required permissions.
+ */
+ private static final void checkQueryUsersPermission(String message) {
+ if (!hasQueryUsersPermission()) {
+ throw new SecurityException(
+ "You either need MANAGE_USERS or QUERY_USERS permission to: " + message);
+ }
+ }
+
+ /**
+ * Enforces that only the system UID or root's UID or apps that have the
+ * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or
+ * {@link android.Manifest.permission#CREATE_USERS CREATE_USERS} or
+ * {@link android.Manifest.permission#QUERY_USERS QUERY_USERS}
+ * can make certain calls to the UserManager.
+ *
+ * @param message used as message if SecurityException is thrown
+ * @throws SecurityException if the caller lacks the required permissions.
+ */
+ private static final void checkQueryOrCreateUsersPermission(String message) {
+ if (!hasQueryOrCreateUsersPermission()) {
+ throw new SecurityException(
+ "You either need MANAGE_USERS, CREATE_USERS, or QUERY_USERS permission to: "
+ + message);
+ }
+ }
+
+ /**
+ * Similar to {@link #checkCreateUsersPermission(String)} but when the caller is tries
* to create user/profiles other than what is allowed for
* {@link android.Manifest.permission#CREATE_USERS CREATE_USERS} permission, then it will only
* allow callers with {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} permission.
*/
- private static final void checkManageOrCreateUsersPermission(int creationFlags) {
+ private static final void checkCreateUsersPermission(int creationFlags) {
if ((creationFlags & ~ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION) == 0) {
- if (!hasManageOrCreateUsersPermission()) {
+ if (!hasCreateUsersPermission()) {
throw new SecurityException("You either need MANAGE_USERS or CREATE_USERS "
+ "permission to create an user with flags: " + creationFlags);
}
@@ -2597,11 +2672,31 @@
* {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or
* {@link android.Manifest.permission#CREATE_USERS CREATE_USERS}.
*/
- private static final boolean hasManageOrCreateUsersPermission() {
+ private static final boolean hasCreateUsersPermission() {
return hasManageUsersOrPermission(android.Manifest.permission.CREATE_USERS);
}
/**
+ * @return whether the calling UID is system UID or root's UID or the calling app has the
+ * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or
+ * {@link android.Manifest.permission#QUERY_USERS QUERY_USERS}.
+ */
+ private static final boolean hasQueryUsersPermission() {
+ return hasManageUsersOrPermission(android.Manifest.permission.QUERY_USERS);
+ }
+
+ /**
+ * @return whether the calling UID is system UID or root's UID or the calling app has
+ * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or
+ * {@link android.Manifest.permission#CREATE_USERS CREATE_USERS} or
+ * {@link android.Manifest.permission#QUERY_USERS QUERY_USERS}.
+ */
+ private static final boolean hasQueryOrCreateUsersPermission() {
+ return hasCreateUsersPermission()
+ || hasPermissionGranted(Manifest.permission.QUERY_USERS, Binder.getCallingUid());
+ }
+
+ /**
* Enforces that only the system UID or root's UID (on any user) can make certain calls to the
* UserManager.
*
@@ -3477,7 +3572,7 @@
public UserInfo createProfileForUserWithThrow(@Nullable String name, @NonNull String userType,
@UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages)
throws ServiceSpecificException {
- checkManageOrCreateUsersPermission(flags);
+ checkCreateUsersPermission(flags);
try {
return createUserInternal(name, userType, flags, userId, disallowedPackages);
} catch (UserManager.CheckedUserOperationException e) {
@@ -3493,7 +3588,7 @@
@NonNull String userType,
@UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages)
throws ServiceSpecificException {
- checkManageOrCreateUsersPermission(flags);
+ checkCreateUsersPermission(flags);
try {
return createUserInternalUnchecked(name, userType, flags, userId,
/* preCreate= */ false, disallowedPackages, /* token= */ null);
@@ -3506,7 +3601,7 @@
public UserInfo createUserWithThrow(String name, @NonNull String userType,
@UserInfoFlag int flags)
throws ServiceSpecificException {
- checkManageOrCreateUsersPermission(flags);
+ checkCreateUsersPermission(flags);
try {
return createUserInternal(name, userType, flags, UserHandle.USER_NULL,
/* disallowedPackages= */ null);
@@ -3520,7 +3615,7 @@
final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
final int flags = userTypeDetails != null ? userTypeDetails.getDefaultUserInfoFlags() : 0;
- checkManageOrCreateUsersPermission(flags);
+ checkCreateUsersPermission(flags);
Preconditions.checkArgument(isUserTypeEligibleForPreCreation(userTypeDetails),
"cannot pre-create user of type " + userType);
@@ -3540,7 +3635,7 @@
String userName, String userType, @UserInfoFlag int flags,
Bitmap userIcon,
String accountName, String accountType, PersistableBundle accountOptions) {
- checkManageOrCreateUsersPermission(flags);
+ checkCreateUsersPermission(flags);
if (someUserHasAccountNoChecks(accountName, accountType)) {
throw new ServiceSpecificException(
@@ -3985,7 +4080,7 @@
@Override
public String[] getPreInstallableSystemPackages(@NonNull String userType) {
- checkManageOrCreateUsersPermission("getPreInstallableSystemPackages");
+ checkCreateUsersPermission("getPreInstallableSystemPackages");
final Set<String> installableSystemPackages =
mSystemPackageInstaller.getInstallablePackagesForUserType(userType);
if (installableSystemPackages == null) {
@@ -4110,7 +4205,7 @@
*/
@Override
public UserInfo createRestrictedProfileWithThrow(@Nullable String name, int parentUserId) {
- checkManageOrCreateUsersPermission("setupRestrictedProfile");
+ checkCreateUsersPermission("setupRestrictedProfile");
final UserInfo user = createProfileForUserWithThrow(
name, UserManager.USER_TYPE_FULL_RESTRICTED, 0, parentUserId, null);
if (user == null) {
@@ -4207,7 +4302,7 @@
@Override
public boolean removeUser(@UserIdInt int userId) {
Slog.i(LOG_TAG, "removeUser u" + userId);
- checkManageOrCreateUsersPermission("Only the system can remove users");
+ checkCreateUsersPermission("Only the system can remove users");
final String restriction = getUserRemovalRestriction(userId);
if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
@@ -4219,7 +4314,7 @@
@Override
public boolean removeUserEvenWhenDisallowed(@UserIdInt int userId) {
- checkManageOrCreateUsersPermission("Only the system can remove users");
+ checkCreateUsersPermission("Only the system can remove users");
return removeUserUnchecked(userId);
}
@@ -4334,7 +4429,7 @@
@Override
public @UserManager.RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId,
boolean evenWhenDisallowed) {
- checkManageOrCreateUsersPermission("Only the system can remove users");
+ checkCreateUsersPermission("Only the system can remove users");
if (!evenWhenDisallowed) {
final String restriction = getUserRemovalRestriction(userId);
@@ -5085,7 +5180,7 @@
@Override
public boolean someUserHasAccount(String accountName, String accountType) {
- checkManageOrCreateUsersPermission("check seed account information");
+ checkCreateUsersPermission("check seed account information");
return someUserHasAccountNoChecks(accountName, accountType);
}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index bcb5e72..07cc3d0 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -84,7 +84,7 @@
*/
@Nullable
public static PackageInfo generate(AndroidPackage pkg, int[] gids,
- @PackageManager.PackageInfoFlags long flags, long firstInstallTime,
+ @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
long lastUpdateTime, Set<String> grantedPermissions, PackageUserState state, int userId,
@Nullable PackageStateInternal pkgSetting) {
return generateWithComponents(pkg, gids, flags, firstInstallTime, lastUpdateTime,
@@ -105,7 +105,7 @@
* @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
*/
private static PackageInfo generateWithComponents(AndroidPackage pkg, int[] gids,
- @PackageManager.PackageInfoFlags long flags, long firstInstallTime,
+ @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
long lastUpdateTime, Set<String> grantedPermissions, PackageUserState state, int userId,
@Nullable ApexInfo apexInfo, @Nullable PackageStateInternal pkgSetting) {
ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId,
@@ -209,7 +209,7 @@
*/
@Nullable
public static ApplicationInfo generateApplicationInfo(AndroidPackage pkg,
- @PackageManager.ApplicationInfoFlags long flags, @NonNull PackageUserState state,
+ @PackageManager.ApplicationInfoFlagsBits long flags, @NonNull PackageUserState state,
int userId, @Nullable PackageStateInternal pkgSetting) {
if (pkg == null) {
return null;
@@ -255,7 +255,7 @@
*/
@Nullable
public static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
- @PackageManager.ComponentInfoFlags long flags, PackageUserState state, int userId,
+ @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state, int userId,
@Nullable PackageStateInternal pkgSetting) {
return generateActivityInfo(pkg, a, flags, state, null, userId, pkgSetting);
}
@@ -265,7 +265,7 @@
*/
@Nullable
private static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
- @PackageManager.ComponentInfoFlags long flags, PackageUserState state,
+ @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state,
@Nullable ApplicationInfo applicationInfo, int userId,
@Nullable PackageStateInternal pkgSetting) {
if (a == null) return null;
@@ -291,7 +291,7 @@
*/
@Nullable
public static ServiceInfo generateServiceInfo(AndroidPackage pkg, ParsedService s,
- @PackageManager.ComponentInfoFlags long flags, PackageUserState state, int userId,
+ @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state, int userId,
@Nullable PackageStateInternal pkgSetting) {
return generateServiceInfo(pkg, s, flags, state, null, userId, pkgSetting);
}
@@ -301,7 +301,7 @@
*/
@Nullable
private static ServiceInfo generateServiceInfo(AndroidPackage pkg, ParsedService s,
- @PackageManager.ComponentInfoFlags long flags, PackageUserState state,
+ @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state,
@Nullable ApplicationInfo applicationInfo, int userId,
@Nullable PackageStateInternal pkgSetting) {
if (s == null) return null;
@@ -326,7 +326,7 @@
*/
@Nullable
public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
- @PackageManager.ComponentInfoFlags long flags, PackageUserState state,
+ @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state,
@NonNull ApplicationInfo applicationInfo, int userId,
@Nullable PackageStateInternal pkgSetting) {
if (p == null) return null;
@@ -353,7 +353,7 @@
*/
@Nullable
public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
- AndroidPackage pkg, @PackageManager.ComponentInfoFlags long flags, int userId,
+ AndroidPackage pkg, @PackageManager.ComponentInfoFlagsBits long flags, int userId,
@Nullable PackageStateInternal pkgSetting) {
if (i == null) return null;
@@ -381,7 +381,7 @@
// PackageStateInternal os that checkUseInstalledOrHidden filter can apply
@Nullable
public static PermissionInfo generatePermissionInfo(ParsedPermission p,
- @PackageManager.ComponentInfoFlags long flags) {
+ @PackageManager.ComponentInfoFlagsBits long flags) {
// TODO(b/135203078): Remove null checks and make all usages @NonNull
if (p == null) return null;
@@ -391,7 +391,7 @@
@Nullable
public static PermissionGroupInfo generatePermissionGroupInfo(ParsedPermissionGroup pg,
- @PackageManager.ComponentInfoFlags long flags) {
+ @PackageManager.ComponentInfoFlagsBits long flags) {
if (pg == null) return null;
// For now, permissions don't have state-adjustable fields; return directly
@@ -400,7 +400,7 @@
@Nullable
public static ArrayMap<String, ProcessInfo> generateProcessInfo(
- Map<String, ParsedProcess> procs, @PackageManager.ComponentInfoFlags long flags) {
+ Map<String, ParsedProcess> procs, @PackageManager.ComponentInfoFlagsBits long flags) {
if (procs == null) {
return null;
}
@@ -423,7 +423,7 @@
*/
public static boolean checkUseInstalledOrHidden(AndroidPackage pkg,
PackageStateInternal pkgSetting, PackageUserState state,
- @PackageManager.PackageInfoFlags long flags) {
+ @PackageManager.PackageInfoFlagsBits long flags) {
// Returns false if the package is hidden system app until installed.
if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
&& !state.isInstalled()
@@ -628,8 +628,8 @@
*/
@Nullable
public ApplicationInfo generate(AndroidPackage pkg,
- @PackageManager.ApplicationInfoFlags long flags, PackageUserState state, int userId,
- @Nullable PackageStateInternal pkgSetting) {
+ @PackageManager.ApplicationInfoFlagsBits long flags, PackageUserState state,
+ int userId, @Nullable PackageStateInternal pkgSetting) {
ApplicationInfo appInfo = mCache.get(pkg.getPackageName());
if (appInfo != null) {
return appInfo;
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index e207ff1..8643b5f 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -103,12 +103,12 @@
private static final String TAG = "DefaultPermGrantPolicy"; // must be <= 23 chars
private static final boolean DEBUG = false;
- @PackageManager.ResolveInfoFlags
+ @PackageManager.ResolveInfoFlagsBits
private static final int DEFAULT_INTENT_QUERY_FLAGS =
PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_UNINSTALLED_PACKAGES;
- @PackageManager.PackageInfoFlags
+ @PackageManager.PackageInfoFlagsBits
private static final int DEFAULT_PACKAGE_INFO_QUERY_FLAGS =
PackageManager.MATCH_UNINSTALLED_PACKAGES
| PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
index 471f38a..25147d0 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -391,7 +391,7 @@
*/
@ApprovalLevel
int approvalLevelForDomain(@NonNull PackageStateInternal pkgSetting, @NonNull Intent intent,
- @PackageManager.ResolveInfoFlags long resolveInfoFlags, @UserIdInt int userId);
+ @PackageManager.ResolveInfoFlagsBits long resolveInfoFlags, @UserIdInt int userId);
/**
* @return the domain verification set ID for the given package, or null if the ID is
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 661e67d..a3b0e3e 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -1197,6 +1197,7 @@
@Nullable @UserIdInt Integer userId,
@NonNull Function<String, PackageStateInternal> pkgSettingFunction)
throws NameNotFoundException {
+ mEnforcer.assertApprovedQuerent(mConnection.getCallingUid(), mProxy);
synchronized (mLock) {
mDebug.printState(writer, packageName, userId, pkgSettingFunction, mAttachedPkgStates);
}
@@ -1721,7 +1722,7 @@
@Override
public int approvalLevelForDomain(@NonNull PackageStateInternal pkgSetting,
- @NonNull Intent intent, @PackageManager.ResolveInfoFlags long resolveInfoFlags,
+ @NonNull Intent intent, @PackageManager.ResolveInfoFlagsBits long resolveInfoFlags,
@UserIdInt int userId) {
String packageName = pkgSetting.getPackageName();
if (!DomainVerificationUtils.isDomainVerificationIntent(intent, resolveInfoFlags)) {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
index 12cce0d..058b605 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
@@ -49,7 +49,7 @@
}
public static boolean isDomainVerificationIntent(Intent intent,
- @PackageManager.ResolveInfoFlags long resolveInfoFlags) {
+ @PackageManager.ResolveInfoFlagsBits long resolveInfoFlags) {
if (!intent.isWebIntent()) {
return false;
}
diff --git a/services/core/java/com/android/server/power/PowerGroup.java b/services/core/java/com/android/server/power/PowerGroup.java
index 51bd745..9127484 100644
--- a/services/core/java/com/android/server/power/PowerGroup.java
+++ b/services/core/java/com/android/server/power/PowerGroup.java
@@ -19,6 +19,7 @@
import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.view.Display;
/**
* Used to store power related requests to every display in a
@@ -33,9 +34,10 @@
private final DisplayPowerRequest mDisplayPowerRequest;
private final boolean mSupportsSandman;
+ private final int mGroupId;
- // True if DisplayManagerService has applied all the latest display states that were
- // requested for this group
+ // True if DisplayManagerService has applied all the latest display states that were requested
+ // for this group
private boolean mReady;
// True if this group is in the process of powering on
private boolean mPoweringOn;
@@ -49,8 +51,9 @@
private long mLastUserActivityTime;
private long mLastUserActivityTimeNoChangeLights;
- PowerGroup(DisplayPowerRequest displayPowerRequest, int wakefulness, boolean ready,
+ PowerGroup(int groupId, DisplayPowerRequest displayPowerRequest, int wakefulness, boolean ready,
boolean supportsSandman) {
+ this.mGroupId = groupId;
this.mDisplayPowerRequest = displayPowerRequest;
this.mWakefulness = wakefulness;
this.mReady = ready;
@@ -58,6 +61,7 @@
}
PowerGroup() {
+ this.mGroupId = Display.DEFAULT_DISPLAY_GROUP;
this.mDisplayPowerRequest = new DisplayPowerRequest();
this.mWakefulness = WAKEFULNESS_AWAKE;
this.mReady = false;
@@ -72,6 +76,10 @@
return mWakefulness;
}
+ int getGroupId() {
+ return mGroupId;
+ }
+
/**
* Sets the {@code wakefulness} value for this {@link PowerGroup}.
*
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 29166b3..1455326 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -104,7 +104,6 @@
import com.android.internal.app.IBatteryStats;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.LatencyTracker;
import com.android.internal.util.Preconditions;
@@ -117,7 +116,6 @@
import com.android.server.UserspaceRebootLogger;
import com.android.server.Watchdog;
import com.android.server.am.BatteryStatsService;
-import com.android.server.display.DisplayGroup;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
import com.android.server.policy.WindowManagerPolicy;
@@ -619,10 +617,6 @@
@GuardedBy("mLock")
private final SparseArray<PowerGroup> mPowerGroups = new SparseArray<>();
- // A cached array of DisplayGroup Ids.
- @GuardedBy("mLock")
- private int[] mDisplayGroupIds;
-
// We are currently in the middle of a batch change of uids.
private boolean mUidsChanging;
@@ -665,13 +659,13 @@
// For now, only the default group supports sandman (dream/AOD).
final boolean supportsSandman = groupId == Display.DEFAULT_DISPLAY_GROUP;
final PowerGroup powerGroup = new PowerGroup(
+ groupId,
new DisplayPowerRequest(),
getGlobalWakefulnessLocked(),
/* ready= */ false,
supportsSandman);
mPowerGroups.append(groupId, powerGroup);
- mDisplayGroupIds = ArrayUtils.appendInt(mDisplayGroupIds, groupId);
- onDisplayGroupEventLocked(DISPLAY_GROUP_ADDED, groupId);
+ onPowerGroupEventLocked(DISPLAY_GROUP_ADDED, powerGroup);
}
}
@@ -682,20 +676,22 @@
Slog.wtf(TAG, "Tried to remove default display group: " + groupId);
return;
}
- mDisplayGroupIds = ArrayUtils.removeInt(mDisplayGroupIds, groupId);
if (!mPowerGroups.contains(groupId)) {
Slog.e(TAG, "Tried to remove non-existent group:" + groupId);
return;
}
- mPowerGroups.delete(groupId);
- onDisplayGroupEventLocked(DISPLAY_GROUP_REMOVED, groupId);
+ onPowerGroupEventLocked(DISPLAY_GROUP_REMOVED, mPowerGroups.get(groupId));
}
}
@Override
public void onDisplayGroupChanged(int groupId) {
synchronized (mLock) {
- onDisplayGroupEventLocked(DISPLAY_GROUP_CHANGED, groupId);
+ if (!mPowerGroups.contains(groupId)) {
+ Slog.e(TAG, "Tried to change non-existent group: " + groupId);
+ return;
+ }
+ onPowerGroupEventLocked(DISPLAY_GROUP_CHANGED, mPowerGroups.get(groupId));
}
}
}
@@ -1150,7 +1146,7 @@
updatePowerStateLocked();
if (sQuiescent) {
- sleepDisplayGroupNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP,
+ sleepDisplayGroupNoUpdateLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP),
mClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_QUIESCENT,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
@@ -1169,7 +1165,6 @@
mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);
mAttentionDetector.systemReady(mContext);
mPowerGroups.append(Display.DEFAULT_DISPLAY_GROUP, new PowerGroup());
- mDisplayGroupIds = new int[]{Display.DEFAULT_DISPLAY_GROUP};
mDisplayGroupPowerChangeListener = new DisplayGroupPowerChangeListener();
mDisplayManagerInternal.registerDisplayGroupListener(mDisplayGroupPowerChangeListener);
@@ -1506,10 +1501,10 @@
opPackageName = wakeLock.mPackageName;
opUid = wakeLock.mOwnerUid;
}
- for (int id : mDisplayGroupIds) {
- wakeDisplayGroupNoUpdateLocked(id, mClock.uptimeMillis(),
- PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag,
- opUid, opPackageName, opUid);
+ for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+ wakeDisplayGroupNoUpdateLocked(mPowerGroups.valueAt(idx), mClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag, opUid, opPackageName,
+ opUid);
}
}
}
@@ -1745,7 +1740,8 @@
if (groupId == Display.INVALID_DISPLAY_GROUP) {
return;
}
- if (userActivityNoUpdateLocked(groupId, eventTime, event, flags, uid)) {
+ if (userActivityNoUpdateLocked(mPowerGroups.get(groupId), eventTime, event, flags,
+ uid)) {
updatePowerStateLocked();
}
}
@@ -1753,8 +1749,10 @@
private void onUserAttention() {
synchronized (mLock) {
- if (userActivityNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, mClock.uptimeMillis(),
- PowerManager.USER_ACTIVITY_EVENT_ATTENTION, 0 /* flags */,
+ if (userActivityNoUpdateLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP),
+ mClock.uptimeMillis(),
+ PowerManager.USER_ACTIVITY_EVENT_ATTENTION,
+ 0 /* flags */,
Process.SYSTEM_UID)) {
updatePowerStateLocked();
}
@@ -1764,8 +1762,9 @@
@GuardedBy("mLock")
private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) {
boolean updatePowerState = false;
- for (int id : mDisplayGroupIds) {
- if (userActivityNoUpdateLocked(id, eventTime, event, flags, uid)) {
+ for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+ if (userActivityNoUpdateLocked(mPowerGroups.valueAt(idx), eventTime, event, flags,
+ uid)) {
updatePowerState = true;
}
}
@@ -1774,10 +1773,10 @@
}
@GuardedBy("mLock")
- private boolean userActivityNoUpdateLocked(int groupId, long eventTime, int event, int flags,
- int uid) {
+ private boolean userActivityNoUpdateLocked(final PowerGroup powerGroup, long eventTime,
+ int event, int flags, int uid) {
if (DEBUG_SPEW) {
- Slog.d(TAG, "userActivityNoUpdateLocked: groupId=" + groupId
+ Slog.d(TAG, "userActivityNoUpdateLocked: groupId=" + powerGroup.getGroupId()
+ ", eventTime=" + eventTime + ", event=" + event
+ ", flags=0x" + Integer.toHexString(flags) + ", uid=" + uid);
}
@@ -1801,7 +1800,7 @@
mOverriddenTimeout = -1;
}
- final int wakefulness = mPowerGroups.get(groupId).getWakefulnessLocked();
+ final int wakefulness = powerGroup.getWakefulnessLocked();
if (wakefulness == WAKEFULNESS_ASLEEP
|| wakefulness == WAKEFULNESS_DOZING
|| (flags & PowerManager.USER_ACTIVITY_FLAG_INDIRECT) != 0) {
@@ -1811,11 +1810,9 @@
maybeUpdateForegroundProfileLastActivityLocked(eventTime);
if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) {
- if (eventTime
- > mPowerGroups.get(groupId).getLastUserActivityTimeNoChangeLightsLocked()
- && eventTime > mPowerGroups.get(groupId).getLastUserActivityTimeLocked()) {
- mPowerGroups.get(groupId).setLastUserActivityTimeNoChangeLightsLocked(
- eventTime);
+ if (eventTime > powerGroup.getLastUserActivityTimeNoChangeLightsLocked()
+ && eventTime > powerGroup.getLastUserActivityTimeLocked()) {
+ powerGroup.setLastUserActivityTimeNoChangeLightsLocked(eventTime);
mDirty |= DIRTY_USER_ACTIVITY;
if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {
mDirty |= DIRTY_QUIESCENT;
@@ -1824,8 +1821,8 @@
return true;
}
} else {
- if (eventTime > mPowerGroups.get(groupId).getLastUserActivityTimeLocked()) {
- mPowerGroups.get(groupId).setLastUserActivityTimeLocked(eventTime);
+ if (eventTime > powerGroup.getLastUserActivityTimeLocked()) {
+ powerGroup.setLastUserActivityTimeLocked(eventTime);
mDirty |= DIRTY_USER_ACTIVITY;
if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {
mDirty |= DIRTY_QUIESCENT;
@@ -1850,16 +1847,17 @@
private void wakeDisplayGroup(int groupId, long eventTime, @WakeReason int reason,
String details, int uid, String opPackageName, int opUid) {
synchronized (mLock) {
- if (wakeDisplayGroupNoUpdateLocked(groupId, eventTime, reason, details, uid,
- opPackageName, opUid)) {
+ if (wakeDisplayGroupNoUpdateLocked(mPowerGroups.get(groupId), eventTime, reason,
+ details, uid, opPackageName, opUid)) {
updatePowerStateLocked();
}
}
}
@GuardedBy("mLock")
- private boolean wakeDisplayGroupNoUpdateLocked(int groupId, long eventTime,
+ private boolean wakeDisplayGroupNoUpdateLocked(final PowerGroup powerGroup, long eventTime,
@WakeReason int reason, String details, int uid, String opPackageName, int opUid) {
+ final int groupId = powerGroup.getGroupId();
if (DEBUG_SPEW) {
Slog.d(TAG, "wakeDisplayGroupNoUpdateLocked: eventTime=" + eventTime
+ ", groupId=" + groupId + ", uid=" + uid);
@@ -1869,7 +1867,7 @@
return false;
}
- final int currentState = mPowerGroups.get(groupId).getWakefulnessLocked();
+ final int currentState = powerGroup.getWakefulnessLocked();
if (currentState == WAKEFULNESS_AWAKE) {
if (!mBootCompleted && sQuiescent) {
mDirty |= DIRTY_QUIESCENT;
@@ -1892,9 +1890,8 @@
LatencyTracker.getInstance(mContext)
.onActionStart(ACTION_TURN_ON_SCREEN, String.valueOf(groupId));
- setWakefulnessLocked(groupId, WAKEFULNESS_AWAKE, eventTime, uid, reason, opUid,
+ setWakefulnessLocked(powerGroup, WAKEFULNESS_AWAKE, eventTime, uid, reason, opUid,
opPackageName, details);
- PowerGroup powerGroup = mPowerGroups.get(groupId);
powerGroup.setLastPowerOnTimeLocked(eventTime);
powerGroup.setIsPoweringOnLocked(true);
} finally {
@@ -1907,19 +1904,20 @@
private void sleepDisplayGroup(int groupId, long eventTime, int reason, int flags,
int uid) {
synchronized (mLock) {
- if (sleepDisplayGroupNoUpdateLocked(groupId, eventTime, reason, flags, uid)) {
+ if (sleepDisplayGroupNoUpdateLocked(mPowerGroups.get(groupId), eventTime, reason, flags,
+ uid)) {
updatePowerStateLocked();
}
}
}
@GuardedBy("mLock")
- private boolean sleepDisplayGroupNoUpdateLocked(int groupId, long eventTime, int reason,
- int flags, int uid) {
+ private boolean sleepDisplayGroupNoUpdateLocked(final PowerGroup powerGroup, long eventTime,
+ int reason, int flags, int uid) {
if (DEBUG_SPEW) {
Slog.d(TAG, "sleepDisplayGroupNoUpdateLocked: eventTime=" + eventTime
- + ", groupId=" + groupId + ", reason=" + reason + ", flags=" + flags
- + ", uid=" + uid);
+ + ", groupId=" + powerGroup.getGroupId() + ", reason=" + reason
+ + ", flags=" + flags + ", uid=" + uid);
}
if (eventTime < mLastWakeTime
@@ -1929,7 +1927,7 @@
return false;
}
- final int wakefulness = mPowerGroups.get(groupId).getWakefulnessLocked();
+ final int wakefulness = powerGroup.getWakefulnessLocked();
if (!PowerManagerInternal.isInteractive(wakefulness)) {
return false;
}
@@ -1939,14 +1937,14 @@
reason = Math.min(PowerManager.GO_TO_SLEEP_REASON_MAX,
Math.max(reason, PowerManager.GO_TO_SLEEP_REASON_MIN));
Slog.i(TAG, "Powering off display group due to "
- + PowerManager.sleepReasonToString(reason) + " (groupId= " + groupId
- + ", uid= " + uid + ")...");
+ + PowerManager.sleepReasonToString(reason)
+ + " (groupId= " + powerGroup.getGroupId() + ", uid= " + uid + ")...");
- mPowerGroups.get(groupId).setSandmanSummonedLocked(/* isSandmanSummoned= */ true);
- setWakefulnessLocked(groupId, WAKEFULNESS_DOZING, eventTime, uid, reason,
+ powerGroup.setSandmanSummonedLocked(/* isSandmanSummoned= */ true);
+ setWakefulnessLocked(powerGroup, WAKEFULNESS_DOZING, eventTime, uid, reason,
/* opUid= */ 0, /* opPackageName= */ null, /* details= */ null);
if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {
- reallySleepDisplayGroupNoUpdateLocked(groupId, eventTime, uid);
+ reallySleepDisplayGroupNoUpdateLocked(powerGroup, eventTime, uid);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
@@ -1956,14 +1954,15 @@
private void dreamDisplayGroup(int groupId, long eventTime, int uid) {
synchronized (mLock) {
- if (dreamDisplayGroupNoUpdateLocked(groupId, eventTime, uid)) {
+ if (dreamDisplayGroupNoUpdateLocked(mPowerGroups.get(groupId), eventTime, uid)) {
updatePowerStateLocked();
}
}
}
@GuardedBy("mLock")
- private boolean dreamDisplayGroupNoUpdateLocked(int groupId, long eventTime, int uid) {
+ private boolean dreamDisplayGroupNoUpdateLocked(final PowerGroup powerGroup, long eventTime,
+ int uid) {
if (DEBUG_SPEW) {
Slog.d(TAG, "dreamDisplayGroupNoUpdateLocked: eventTime=" + eventTime
+ ", uid=" + uid);
@@ -1976,11 +1975,12 @@
Trace.traceBegin(Trace.TRACE_TAG_POWER, "napDisplayGroup");
try {
- Slog.i(TAG, "Napping display group (groupId=" + groupId + ", uid=" + uid + ")...");
+ Slog.i(TAG, "Napping display group (groupId=" + powerGroup.getGroupId() + ", uid=" + uid
+ + ")...");
- mPowerGroups.get(groupId).setSandmanSummonedLocked(/* isSandmanSummoned= */ true);
- setWakefulnessLocked(groupId, WAKEFULNESS_DREAMING, eventTime, uid, /* reason= */
- 0, /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null);
+ powerGroup.setSandmanSummonedLocked(/* isSandmanSummoned= */ true);
+ setWakefulnessLocked(powerGroup, WAKEFULNESS_DREAMING, eventTime, uid,
+ /* reason= */0, /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
@@ -1989,7 +1989,8 @@
}
@GuardedBy("mLock")
- private boolean reallySleepDisplayGroupNoUpdateLocked(int groupId, long eventTime, int uid) {
+ private boolean reallySleepDisplayGroupNoUpdateLocked(final PowerGroup powerGroup,
+ long eventTime, int uid) {
if (DEBUG_SPEW) {
Slog.d(TAG, "reallySleepDisplayGroupNoUpdateLocked: eventTime=" + eventTime
+ ", uid=" + uid);
@@ -1997,16 +1998,18 @@
if (eventTime < mLastWakeTime || getWakefulnessLocked() == WAKEFULNESS_ASLEEP
|| !mBootCompleted || !mSystemReady
- || mPowerGroups.get(groupId).getWakefulnessLocked()
+ || powerGroup.getWakefulnessLocked()
== WAKEFULNESS_ASLEEP) {
return false;
}
Trace.traceBegin(Trace.TRACE_TAG_POWER, "reallySleepDisplayGroup");
try {
- Slog.i(TAG, "Sleeping display group (groupId=" + groupId + ", uid=" + uid + ")...");
+ Slog.i(TAG,
+ "Sleeping display group (groupId=" + powerGroup.getGroupId() + ", uid=" + uid
+ + ")...");
- setWakefulnessLocked(groupId, WAKEFULNESS_ASLEEP, eventTime, uid,
+ setWakefulnessLocked(powerGroup, WAKEFULNESS_ASLEEP, eventTime, uid,
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, /* opUid= */ 0,
/* opPackageName= */ null, /* details= */ null);
} finally {
@@ -2019,14 +2022,21 @@
@GuardedBy("mLock")
void setWakefulnessLocked(int groupId, int wakefulness, long eventTime, int uid, int reason,
int opUid, String opPackageName, String details) {
- if (mPowerGroups.get(groupId).setWakefulnessLocked(wakefulness)) {
+ setWakefulnessLocked(mPowerGroups.get(groupId), wakefulness, eventTime, uid, reason, opUid,
+ opPackageName, details);
+ }
+
+ @GuardedBy("mLock")
+ private void setWakefulnessLocked(final PowerGroup powerGroup, int wakefulness, long eventTime,
+ int uid, int reason, int opUid, String opPackageName, String details) {
+ if (powerGroup.setWakefulnessLocked(wakefulness)) {
mDirty |= DIRTY_DISPLAY_GROUP_WAKEFULNESS;
setGlobalWakefulnessLocked(getGlobalWakefulnessLocked(),
eventTime, reason, uid, opUid, opPackageName, details);
if (wakefulness == WAKEFULNESS_AWAKE) {
// Kick user activity to prevent newly awake group from timing out instantly.
- userActivityNoUpdateLocked(
- groupId, eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
+ userActivityNoUpdateLocked(powerGroup, eventTime,
+ PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
}
}
}
@@ -2138,9 +2148,9 @@
}
/**
- * Returns the amalgamated wakefulness of all {@link DisplayGroup DisplayGroups}.
+ * Returns the amalgamated wakefulness of all {@link PowerGroup PowerGroups}.
*
- * <p>This will be the highest wakeful state of all {@link DisplayGroup DisplayGroups}; ordered
+ * <p>This will be the highest wakeful state of all {@link PowerGroup PowerGroups}; ordered
* from highest to lowest:
* <ol>
* <li>{@link PowerManagerInternal#WAKEFULNESS_AWAKE}
@@ -2171,14 +2181,18 @@
}
@GuardedBy("mLock")
- void onDisplayGroupEventLocked(int event, int groupId) {
+ void onPowerGroupEventLocked(int event, PowerGroup powerGroup) {
+ final int groupId = powerGroup.getGroupId();
+ if (event == DisplayGroupPowerChangeListener.DISPLAY_GROUP_REMOVED) {
+ mPowerGroups.remove(groupId);
+ }
final int oldWakefulness = getWakefulnessLocked();
final int newWakefulness = getGlobalWakefulnessLocked();
if (event == DisplayGroupPowerChangeListener.DISPLAY_GROUP_ADDED
&& newWakefulness == WAKEFULNESS_AWAKE) {
// Kick user activity to prevent newly added group from timing out instantly.
- userActivityNoUpdateLocked(groupId, mClock.uptimeMillis(),
+ userActivityNoUpdateLocked(powerGroup, mClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_OTHER, /* flags= */ 0, Process.SYSTEM_UID);
}
@@ -2380,13 +2394,13 @@
final long now = mClock.uptimeMillis();
if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType,
dockedOnWirelessCharger)) {
- wakeDisplayGroupNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, now,
- PowerManager.WAKE_REASON_PLUGGED_IN,
+ wakeDisplayGroupNoUpdateLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP),
+ now, PowerManager.WAKE_REASON_PLUGGED_IN,
"android.server.power:PLUGGED:" + mIsPowered, Process.SYSTEM_UID,
mContext.getOpPackageName(), Process.SYSTEM_UID);
}
- userActivityNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, now,
+ userActivityNoUpdateLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP), now,
PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
// only play charging sounds if boot is completed so charging sounds don't play
@@ -2487,8 +2501,8 @@
mProfilePowerState.valueAt(i).mWakeLockSummary = 0;
}
- for (int groupId : mDisplayGroupIds) {
- mPowerGroups.get(groupId).setWakeLockSummaryLocked(0);
+ for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+ mPowerGroups.valueAt(idx).setWakeLockSummaryLocked(0);
}
int invalidGroupWakeLockSummary = 0;
@@ -2498,17 +2512,18 @@
final Integer groupId = wakeLock.getDisplayGroupId();
// a wakelock with an invalid group ID should affect all groups
if (groupId == null || (groupId != Display.INVALID_DISPLAY_GROUP
- && !ArrayUtils.contains(mDisplayGroupIds, groupId))) {
+ && !mPowerGroups.contains(groupId))) {
continue;
}
+ final PowerGroup powerGroup = mPowerGroups.get(groupId);
final int wakeLockFlags = getWakeLockSummaryFlags(wakeLock);
mWakeLockSummary |= wakeLockFlags;
if (groupId != Display.INVALID_DISPLAY_GROUP) {
- int wakeLockSummary = mPowerGroups.get(groupId).getWakeLockSummaryLocked();
+ int wakeLockSummary = powerGroup.getWakeLockSummaryLocked();
wakeLockSummary |= wakeLockFlags;
- mPowerGroups.get(groupId).setWakeLockSummaryLocked(wakeLockSummary);
+ powerGroup.setWakeLockSummaryLocked(wakeLockSummary);
} else {
invalidGroupWakeLockSummary |= wakeLockFlags;
}
@@ -2521,12 +2536,12 @@
}
}
- for (int groupId : mDisplayGroupIds) {
+ for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+ final PowerGroup powerGroup = mPowerGroups.valueAt(idx);
final int wakeLockSummary = adjustWakeLockSummary(
- mPowerGroups.get(groupId).getWakefulnessLocked(),
- invalidGroupWakeLockSummary
- | mPowerGroups.get(groupId).getWakeLockSummaryLocked());
- mPowerGroups.get(groupId).setWakeLockSummaryLocked(wakeLockSummary);
+ powerGroup.getWakefulnessLocked(),
+ invalidGroupWakeLockSummary | powerGroup.getWakeLockSummaryLocked());
+ powerGroup.setWakeLockSummaryLocked(wakeLockSummary);
}
mWakeLockSummary = adjustWakeLockSummary(getWakefulnessLocked(),
@@ -2684,14 +2699,14 @@
final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager;
long nextTimeout = -1;
boolean hasUserActivitySummary = false;
- for (int groupId : mDisplayGroupIds) {
+ for (int idx = 0; idx < mPowerGroups.size(); idx++) {
int groupUserActivitySummary = 0;
long groupNextTimeout = 0;
- if (mPowerGroups.get(groupId).getWakefulnessLocked() != WAKEFULNESS_ASLEEP) {
- final long lastUserActivityTime =
- mPowerGroups.get(groupId).getLastUserActivityTimeLocked();
+ final PowerGroup powerGroup = mPowerGroups.valueAt(idx);
+ if (powerGroup.getWakefulnessLocked() != WAKEFULNESS_ASLEEP) {
+ final long lastUserActivityTime = powerGroup.getLastUserActivityTimeLocked();
final long lastUserActivityTimeNoChangeLights =
- mPowerGroups.get(groupId).getLastUserActivityTimeNoChangeLightsLocked();
+ powerGroup.getLastUserActivityTimeNoChangeLightsLocked();
if (lastUserActivityTime >= mLastWakeTime) {
groupNextTimeout = lastUserActivityTime + screenOffTimeout - screenDimDuration;
if (now < groupNextTimeout) {
@@ -2708,7 +2723,7 @@
groupNextTimeout = lastUserActivityTimeNoChangeLights + screenOffTimeout;
if (now < groupNextTimeout) {
final DisplayPowerRequest displayPowerRequest =
- mPowerGroups.get(groupId).getDisplayPowerRequestLocked();
+ powerGroup.getDisplayPowerRequestLocked();
if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT
|| displayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) {
groupUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
@@ -2749,7 +2764,7 @@
}
if ((groupUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
- && (mPowerGroups.get(groupId).getWakeLockSummaryLocked()
+ && (powerGroup.getWakeLockSummaryLocked()
& WAKE_LOCK_STAY_AWAKE) == 0) {
groupNextTimeout = mAttentionDetector.updateUserActivity(groupNextTimeout,
screenDimDuration);
@@ -2764,12 +2779,11 @@
}
}
- mPowerGroups.get(groupId).setUserActivitySummaryLocked(groupUserActivitySummary);
+ powerGroup.setUserActivitySummaryLocked(groupUserActivitySummary);
if (DEBUG_SPEW) {
- Slog.d(TAG, "updateUserActivitySummaryLocked: groupId=" + groupId
- + ", mWakefulness=" + wakefulnessToString(
- mPowerGroups.get(groupId).getWakefulnessLocked())
+ Slog.d(TAG, "updateUserActivitySummaryLocked: groupId=" + powerGroup.getGroupId()
+ + ", mWakefulness=" + wakefulnessToString(powerGroup.getWakefulnessLocked())
+ ", mUserActivitySummary=0x" + Integer.toHexString(
groupUserActivitySummary)
+ ", nextTimeout=" + TimeUtils.formatUptime(groupNextTimeout));
@@ -2880,12 +2894,11 @@
}
@GuardedBy("mLock")
- private boolean isAttentiveTimeoutExpired(int groupId, long now) {
+ private boolean isAttentiveTimeoutExpired(final PowerGroup powerGroup, long now) {
long attentiveTimeout = getAttentiveTimeoutLocked();
// Attentive state only applies to the default display group.
- return groupId == Display.DEFAULT_DISPLAY_GROUP && attentiveTimeout >= 0
- && now >= mPowerGroups.get(groupId).getLastUserActivityTimeLocked()
- + attentiveTimeout;
+ return powerGroup.getGroupId() == Display.DEFAULT_DISPLAY_GROUP && attentiveTimeout >= 0
+ && now >= powerGroup.getLastUserActivityTimeLocked() + attentiveTimeout;
}
/**
@@ -2990,28 +3003,32 @@
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED
| DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE
| DIRTY_DOCK_STATE | DIRTY_ATTENTIVE | DIRTY_SETTINGS
- | DIRTY_SCREEN_BRIGHTNESS_BOOST)) != 0) {
- final long time = mClock.uptimeMillis();
- for (int id : mDisplayGroupIds) {
- if (mPowerGroups.get(id).getWakefulnessLocked() == WAKEFULNESS_AWAKE
- && isItBedTimeYetLocked(id)) {
- if (DEBUG_SPEW) {
- Slog.d(TAG, "updateWakefulnessLocked: Bed time for group " + id);
- }
- if (isAttentiveTimeoutExpired(id, time)) {
- if (DEBUG) {
- Slog.i(TAG, "Going to sleep now due to long user inactivity");
- }
- changed = sleepDisplayGroupNoUpdateLocked(id, time,
- PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE,
- PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
- } else if (shouldNapAtBedTimeLocked()) {
- changed = dreamDisplayGroupNoUpdateLocked(id, time, Process.SYSTEM_UID);
- } else {
- changed = sleepDisplayGroupNoUpdateLocked(id, time,
- PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
- }
+ | DIRTY_SCREEN_BRIGHTNESS_BOOST)) == 0) {
+ return changed;
+ }
+ final long time = mClock.uptimeMillis();
+ for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+ final PowerGroup powerGroup = mPowerGroups.valueAt(idx);
+ if (!(powerGroup.getWakefulnessLocked() == WAKEFULNESS_AWAKE
+ && isItBedTimeYetLocked(powerGroup))) {
+ continue;
+ }
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "updateWakefulnessLocked: Bed time for group "
+ + powerGroup.getGroupId());
+ }
+ if (isAttentiveTimeoutExpired(powerGroup, time)) {
+ if (DEBUG) {
+ Slog.i(TAG, "Going to sleep now due to long user inactivity");
}
+ changed = sleepDisplayGroupNoUpdateLocked(powerGroup, time,
+ PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE,
+ PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
+ } else if (shouldNapAtBedTimeLocked()) {
+ changed = dreamDisplayGroupNoUpdateLocked(powerGroup, time, Process.SYSTEM_UID);
+ } else {
+ changed = sleepDisplayGroupNoUpdateLocked(powerGroup, time,
+ PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
}
}
return changed;
@@ -3029,39 +3046,38 @@
}
/**
- * Returns true if the DisplayGroup with the provided {@code groupId} should go to sleep now.
+ * Returns true if the provided {@link PowerGroup} should go to sleep now.
* Also used when exiting a dream to determine whether we should go back to being fully awake or
* else go to sleep for good.
*/
@GuardedBy("mLock")
- private boolean isItBedTimeYetLocked(int groupId) {
+ private boolean isItBedTimeYetLocked(PowerGroup powerGroup) {
if (!mBootCompleted) {
return false;
}
long now = mClock.uptimeMillis();
- if (isAttentiveTimeoutExpired(groupId, now)) {
+ if (isAttentiveTimeoutExpired(powerGroup, now)) {
return !isBeingKeptFromInattentiveSleepLocked();
} else {
- return !isBeingKeptAwakeLocked(groupId);
+ return !isBeingKeptAwakeLocked(powerGroup);
}
}
/**
- * Returns true if the DisplayGroup with the provided {@code groupId} is being kept awake by a
- * wake lock, user activity or the stay on while powered setting. We also keep the phone awake
- * when the proximity sensor returns a positive result so that the device does not lock while in
- * a phone call. This function only controls whether the device will go to sleep or dream which
- * is independent of whether it will be allowed to suspend.
+ * Returns true if the provided {@link PowerGroup} is being kept awake by a wake lock, user
+ * activity or the stay on while powered setting. We also keep the phone awake when the
+ * proximity sensor returns a positive result so that the device does not lock while in a phone
+ * call. This function only controls whether the device will go to sleep or dream which is
+ * independent of whether it will be allowed to suspend.
*/
@GuardedBy("mLock")
- private boolean isBeingKeptAwakeLocked(int groupId) {
+ private boolean isBeingKeptAwakeLocked(final PowerGroup powerGroup) {
return mStayOn
|| mProximityPositive
- || (mPowerGroups.get(groupId).getWakeLockSummaryLocked() & WAKE_LOCK_STAY_AWAKE)
- != 0
- || (mPowerGroups.get(groupId).getUserActivitySummaryLocked() & (
- USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0
+ || (powerGroup.getWakeLockSummaryLocked() & WAKE_LOCK_STAY_AWAKE) != 0
+ || (powerGroup.getUserActivitySummaryLocked() & (
+ USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0
|| mScreenBrightnessBoostInProgress;
}
@@ -3102,10 +3118,11 @@
private void scheduleSandmanLocked() {
if (!mSandmanScheduled) {
mSandmanScheduled = true;
- for (int id : mDisplayGroupIds) {
- if (mPowerGroups.get(id).supportsSandmanLocked()) {
+ for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+ final PowerGroup powerGroup = mPowerGroups.valueAt(idx);
+ if (powerGroup.supportsSandmanLocked()) {
Message msg = mHandler.obtainMessage(MSG_SANDMAN);
- msg.arg1 = id;
+ msg.arg1 = powerGroup.getGroupId();
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
@@ -3126,16 +3143,16 @@
final int wakefulness;
synchronized (mLock) {
mSandmanScheduled = false;
- if (!ArrayUtils.contains(mDisplayGroupIds, groupId)) {
+ if (!mPowerGroups.contains(groupId)) {
// Group has been removed.
return;
}
- wakefulness = mPowerGroups.get(groupId).getWakefulnessLocked();
+ final PowerGroup powerGroup = mPowerGroups.get(groupId);
+ wakefulness = powerGroup.getWakefulnessLocked();
if ((wakefulness == WAKEFULNESS_DREAMING || wakefulness == WAKEFULNESS_DOZING) &&
- mPowerGroups.get(groupId).isSandmanSummonedLocked()
- && mPowerGroups.get(groupId).isReadyLocked()) {
- startDreaming = canDreamLocked(groupId) || canDozeLocked();
- mPowerGroups.get(groupId).setSandmanSummonedLocked(/* isSandmanSummoned= */ false);
+ powerGroup.isSandmanSummonedLocked() && powerGroup.isReadyLocked()) {
+ startDreaming = canDreamLocked(powerGroup) || canDozeLocked();
+ powerGroup.setSandmanSummonedLocked(/* isSandmanSummoned= */ false);
} else {
startDreaming = false;
}
@@ -3162,7 +3179,7 @@
// Update dream state.
synchronized (mLock) {
- if (!ArrayUtils.contains(mDisplayGroupIds, groupId)) {
+ if (!mPowerGroups.contains(groupId)) {
// Group has been removed.
return;
}
@@ -3179,19 +3196,20 @@
// If preconditions changed, wait for the next iteration to determine
// whether the dream should continue (or be restarted).
- if (mPowerGroups.get(groupId).isSandmanSummonedLocked()
- || mPowerGroups.get(groupId).getWakefulnessLocked() != wakefulness) {
+ final PowerGroup powerGroup = mPowerGroups.get(groupId);
+ if (powerGroup.isSandmanSummonedLocked()
+ || powerGroup.getWakefulnessLocked() != wakefulness) {
return; // wait for next cycle
}
// Determine whether the dream should continue.
long now = mClock.uptimeMillis();
if (wakefulness == WAKEFULNESS_DREAMING) {
- if (isDreaming && canDreamLocked(groupId)) {
+ if (isDreaming && canDreamLocked(powerGroup)) {
if (mDreamsBatteryLevelDrainCutoffConfig >= 0
&& mBatteryLevel < mBatteryLevelWhenDreamStarted
- mDreamsBatteryLevelDrainCutoffConfig
- && !isBeingKeptAwakeLocked(groupId)) {
+ && !isBeingKeptAwakeLocked(powerGroup)) {
// If the user activity timeout expired and the battery appears
// to be draining faster than it is charging then stop dreaming
// and go to sleep.
@@ -3206,13 +3224,14 @@
}
// Dream has ended or will be stopped. Update the power state.
- if (isItBedTimeYetLocked(groupId)) {
- final int flags = isAttentiveTimeoutExpired(groupId, now)
+ if (isItBedTimeYetLocked(powerGroup)) {
+ final int flags = isAttentiveTimeoutExpired(powerGroup, now)
? PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE : 0;
- sleepDisplayGroupNoUpdateLocked(groupId, now,
+ sleepDisplayGroupNoUpdateLocked(powerGroup, now,
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, flags, Process.SYSTEM_UID);
} else {
- wakeDisplayGroupNoUpdateLocked(groupId, now, PowerManager.WAKE_REASON_UNKNOWN,
+ wakeDisplayGroupNoUpdateLocked(powerGroup, now,
+ PowerManager.WAKE_REASON_UNKNOWN,
"android.server.power:DREAM_FINISHED", Process.SYSTEM_UID,
mContext.getOpPackageName(), Process.SYSTEM_UID);
}
@@ -3223,7 +3242,7 @@
}
// Doze has ended or will be stopped. Update the power state.
- reallySleepDisplayGroupNoUpdateLocked(groupId, now, Process.SYSTEM_UID);
+ reallySleepDisplayGroupNoUpdateLocked(powerGroup, now, Process.SYSTEM_UID);
updatePowerStateLocked();
}
}
@@ -3238,21 +3257,19 @@
* Returns true if the {@code groupId} is allowed to dream in its current state.
*/
@GuardedBy("mLock")
- private boolean canDreamLocked(int groupId) {
- final DisplayPowerRequest displayPowerRequest =
- mPowerGroups.get(groupId).getDisplayPowerRequestLocked();
+ private boolean canDreamLocked(final PowerGroup powerGroup) {
+ final DisplayPowerRequest displayPowerRequest = powerGroup.getDisplayPowerRequestLocked();
if (!mBootCompleted
|| getWakefulnessLocked() != WAKEFULNESS_DREAMING
|| !mDreamsSupportedConfig
|| !mDreamsEnabledSetting
|| !displayPowerRequest.isBrightOrDim()
|| displayPowerRequest.isVr()
- || (mPowerGroups.get(groupId).getUserActivitySummaryLocked() & (
- USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM
- | USER_ACTIVITY_SCREEN_DREAM)) == 0) {
+ || (powerGroup.getUserActivitySummaryLocked() & (USER_ACTIVITY_SCREEN_BRIGHT
+ | USER_ACTIVITY_SCREEN_DIM | USER_ACTIVITY_SCREEN_DREAM)) == 0) {
return false;
}
- if (!isBeingKeptAwakeLocked(groupId)) {
+ if (!isBeingKeptAwakeLocked(powerGroup)) {
if (!mIsPowered && !mDreamsEnabledOnBatteryConfig) {
return false;
}
@@ -3302,10 +3319,12 @@
}
}
- for (final int groupId : mDisplayGroupIds) {
+ for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+ final PowerGroup powerGroup = mPowerGroups.valueAt(idx);
+ final int groupId = powerGroup.getGroupId();
final DisplayPowerRequest displayPowerRequest =
- mPowerGroups.get(groupId).getDisplayPowerRequestLocked();
- displayPowerRequest.policy = getDesiredScreenPolicyLocked(groupId);
+ powerGroup.getDisplayPowerRequestLocked();
+ displayPowerRequest.policy = getDesiredScreenPolicyLocked(powerGroup);
// Determine appropriate screen brightness and auto-brightness adjustments.
final boolean autoBrightness;
@@ -3334,7 +3353,7 @@
if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
displayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
- if ((mPowerGroups.get(groupId).getWakeLockSummaryLocked() & WAKE_LOCK_DRAW) != 0
+ if ((powerGroup.getWakeLockSummaryLocked() & WAKE_LOCK_DRAW) != 0
&& !mDrawWakeLockOverrideFromSidekick) {
if (displayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) {
displayPowerRequest.dozeScreenState = Display.STATE_DOZE;
@@ -3361,11 +3380,11 @@
+ ", policy=" + policyToString(displayPowerRequest.policy)
+ ", mWakefulness="
+ PowerManagerInternal.wakefulnessToString(
- mPowerGroups.get(groupId).getWakefulnessLocked())
+ powerGroup.getWakefulnessLocked())
+ ", mWakeLockSummary=0x" + Integer.toHexString(
- mPowerGroups.get(groupId).getWakeLockSummaryLocked())
+ powerGroup.getWakeLockSummaryLocked())
+ ", mUserActivitySummary=0x" + Integer.toHexString(
- mPowerGroups.get(groupId).getUserActivitySummaryLocked())
+ powerGroup.getUserActivitySummaryLocked())
+ ", mBootCompleted=" + mBootCompleted
+ ", screenBrightnessOverride="
+ displayPowerRequest.screenBrightnessOverride
@@ -3376,16 +3395,15 @@
+ ", sQuiescent=" + sQuiescent);
}
- final boolean displayReadyStateChanged =
- mPowerGroups.get(groupId).setReadyLocked(ready);
- final boolean poweringOn = mPowerGroups.get(groupId).isPoweringOnLocked();
+ final boolean displayReadyStateChanged = powerGroup.setReadyLocked(ready);
+ final boolean poweringOn = powerGroup.isPoweringOnLocked();
if (ready && displayReadyStateChanged && poweringOn
- && mPowerGroups.get(groupId).getWakefulnessLocked() == WAKEFULNESS_AWAKE) {
- mPowerGroups.get(groupId).setIsPoweringOnLocked(false);
+ && powerGroup.getWakefulnessLocked() == WAKEFULNESS_AWAKE) {
+ powerGroup.setIsPoweringOnLocked(false);
LatencyTracker.getInstance(mContext).onActionEnd(ACTION_TURN_ON_SCREEN);
Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId);
final int latencyMs = (int) (mClock.uptimeMillis()
- - mPowerGroups.get(groupId).getLastPowerOnTimeLocked());
+ - powerGroup.getLastPowerOnTimeLocked());
if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) {
Slog.w(TAG, "Screen on took " + latencyMs + " ms");
}
@@ -3431,8 +3449,12 @@
@VisibleForTesting
@GuardedBy("mLock")
int getDesiredScreenPolicyLocked(int groupId) {
- final int wakefulness = mPowerGroups.get(groupId).getWakefulnessLocked();
- final int wakeLockSummary = mPowerGroups.get(groupId).getWakeLockSummaryLocked();
+ return getDesiredScreenPolicyLocked(mPowerGroups.get(groupId));
+ }
+
+ int getDesiredScreenPolicyLocked(final PowerGroup powerGroup) {
+ final int wakefulness = powerGroup.getWakefulnessLocked();
+ final int wakeLockSummary = powerGroup.getWakeLockSummaryLocked();
if (wakefulness == WAKEFULNESS_ASLEEP || sQuiescent) {
return DisplayPowerRequest.POLICY_OFF;
} else if (wakefulness == WAKEFULNESS_DOZING) {
@@ -3455,8 +3477,7 @@
if ((wakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
|| !mBootCompleted
- || (mPowerGroups.get(groupId).getUserActivitySummaryLocked()
- & USER_ACTIVITY_SCREEN_BRIGHT) != 0
+ || (powerGroup.getUserActivitySummaryLocked() & USER_ACTIVITY_SCREEN_BRIGHT) != 0
|| mScreenBrightnessBoostInProgress) {
return DisplayPowerRequest.POLICY_BRIGHT;
}
@@ -3490,8 +3511,9 @@
mProximityPositive = false;
mInterceptedPowerKeyForProximity = false;
mDirty |= DIRTY_PROXIMITY_POSITIVE;
- userActivityNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, mClock.uptimeMillis(),
- PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
+ userActivityNoUpdateLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP),
+ mClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_OTHER,
+ 0 /* flags */, Process.SYSTEM_UID);
updatePowerStateLocked();
}
}
@@ -3551,8 +3573,8 @@
final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked();
final boolean autoSuspend = !needDisplaySuspendBlocker;
boolean interactive = false;
- for (int id : mDisplayGroupIds) {
- interactive |= mPowerGroups.get(id).getDisplayPowerRequestLocked().isBrightOrDim();
+ for (int idx = 0; idx < mPowerGroups.size() && !interactive; idx++) {
+ interactive = mPowerGroups.valueAt(idx).getDisplayPowerRequestLocked().isBrightOrDim();
}
// Disable auto-suspend if needed.
@@ -3635,9 +3657,9 @@
return true;
}
- for (int id : mDisplayGroupIds) {
+ for (int idx = 0; idx < mPowerGroups.size(); idx++) {
final DisplayPowerRequest displayPowerRequest =
- mPowerGroups.get(id).getDisplayPowerRequestLocked();
+ mPowerGroups.valueAt(idx).getDisplayPowerRequestLocked();
if (displayPowerRequest.isBrightOrDim()) {
// If we asked for the screen to be on but it is off due to the proximity
// sensor then we may suspend but only if the configuration allows it.
@@ -4077,7 +4099,7 @@
mScreenBrightnessBoostInProgress = true;
mDirty |= DIRTY_SCREEN_BRIGHTNESS_BOOST;
- userActivityNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, eventTime,
+ userActivityNoUpdateLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP), eventTime,
PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
updatePowerStateLocked();
}
@@ -4201,9 +4223,9 @@
mForceSuspendActive = true;
// Place the system in an non-interactive state
boolean updatePowerState = false;
- for (int id : mDisplayGroupIds) {
- updatePowerState |= sleepDisplayGroupNoUpdateLocked(id, mClock.uptimeMillis(),
- PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND,
+ for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+ updatePowerState |= sleepDisplayGroupNoUpdateLocked(mPowerGroups.valueAt(idx),
+ mClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid);
}
if (updatePowerState) {
@@ -4504,16 +4526,17 @@
}
pw.println("Display Group User Activity:");
- for (int id : mDisplayGroupIds) {
- pw.println(" displayGroupId=" + id);
+ for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+ final PowerGroup powerGroup = mPowerGroups.valueAt(idx);
+ pw.println(" displayGroupId=" + powerGroup.getGroupId());
pw.println(" userActivitySummary=0x" + Integer.toHexString(
- mPowerGroups.get(id).getUserActivitySummaryLocked()));
+ powerGroup.getUserActivitySummaryLocked()));
pw.println(" lastUserActivityTime=" + TimeUtils.formatUptime(
- mPowerGroups.get(id).getLastUserActivityTimeLocked()));
+ powerGroup.getLastUserActivityTimeLocked()));
pw.println(" lastUserActivityTimeNoChangeLights=" + TimeUtils.formatUptime(
- mPowerGroups.get(id).getLastUserActivityTimeNoChangeLightsLocked()));
+ powerGroup.getLastUserActivityTimeNoChangeLightsLocked()));
pw.println(" mWakeLockSummary=0x" + Integer.toHexString(
- mPowerGroups.get(id).getWakeLockSummaryLocked()));
+ powerGroup.getWakeLockSummaryLocked()));
}
wcd = mWirelessChargerDetector;
@@ -4601,12 +4624,13 @@
proto.write(PowerManagerServiceDumpProto.NOTIFY_LONG_DISPATCHED_MS, mNotifyLongDispatched);
proto.write(PowerManagerServiceDumpProto.NOTIFY_LONG_NEXT_CHECK_MS, mNotifyLongNextCheck);
- for (int id : mDisplayGroupIds) {
+ for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+ final PowerGroup powerGroup = mPowerGroups.valueAt(idx);
final long userActivityToken = proto.start(
PowerManagerServiceDumpProto.USER_ACTIVITY);
- proto.write(PowerManagerServiceDumpProto.UserActivityProto.DISPLAY_GROUP_ID, id);
- final long userActivitySummary =
- mPowerGroups.get(id).getUserActivitySummaryLocked();
+ proto.write(PowerManagerServiceDumpProto.UserActivityProto.DISPLAY_GROUP_ID,
+ powerGroup.getGroupId());
+ final long userActivitySummary = powerGroup.getUserActivitySummaryLocked();
proto.write(PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_BRIGHT,
(userActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0);
proto.write(PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_DIM,
@@ -4615,10 +4639,10 @@
(userActivitySummary & USER_ACTIVITY_SCREEN_DREAM) != 0);
proto.write(
PowerManagerServiceDumpProto.UserActivityProto.LAST_USER_ACTIVITY_TIME_MS,
- mPowerGroups.get(id).getLastUserActivityTimeLocked());
+ powerGroup.getLastUserActivityTimeLocked());
proto.write(
PowerManagerServiceDumpProto.UserActivityProto.LAST_USER_ACTIVITY_TIME_NO_CHANGE_LIGHTS_MS,
- mPowerGroups.get(id).getLastUserActivityTimeNoChangeLightsLocked());
+ powerGroup.getLastUserActivityTimeNoChangeLightsLocked());
proto.end(userActivityToken);
}
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
index 122b3f3..1aa7598 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
@@ -42,6 +42,7 @@
import android.media.tv.interactive.TvIAppService;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteCallbackList;
@@ -652,6 +653,9 @@
userState.mServiceStateMap.put(componentName, serviceState);
} else if (serviceState.mService != null) {
serviceState.mService.prepare(type);
+ } else {
+ serviceState.mPendingPrepare = true;
+ serviceState.mPendingPrepareType = type;
}
}
} catch (RemoteException e) {
@@ -662,6 +666,40 @@
}
@Override
+ public void notifyAppLinkInfo(String tiasId, Bundle appLinkInfo, int userId) {
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, "notifyAppLinkInfo");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+ TvIAppState iAppState = userState.mIAppMap.get(tiasId);
+ if (iAppState == null) {
+ Slogf.e(TAG, "failed to notifyAppLinkInfo - unknown TIAS id "
+ + tiasId);
+ return;
+ }
+ ComponentName componentName = iAppState.mInfo.getComponent();
+ ServiceState serviceState = userState.mServiceStateMap.get(componentName);
+ if (serviceState == null) {
+ serviceState = new ServiceState(
+ componentName, tiasId, resolvedUserId);
+ serviceState.addPendingAppLink(appLinkInfo);
+ userState.mServiceStateMap.put(componentName, serviceState);
+ } else if (serviceState.mService != null) {
+ serviceState.mService.notifyAppLinkInfo(appLinkInfo);
+ } else {
+ serviceState.addPendingAppLink(appLinkInfo);
+ }
+ }
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in notifyAppLinkInfo", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void createSession(final ITvIAppClient client, final String iAppServiceId, int type,
int seq, int userId) {
final int callingUid = Binder.getCallingUid();
@@ -1237,6 +1275,7 @@
private final ServiceConnection mConnection;
private final ComponentName mComponent;
private final String mIAppSeriviceId;
+ private final List<Bundle> mPendingAppLinkInfo = new ArrayList<>();
private boolean mPendingPrepare = false;
private Integer mPendingPrepareType = null;
@@ -1257,6 +1296,10 @@
mConnection = new IAppServiceConnection(component, userId);
mIAppSeriviceId = tias;
}
+
+ private void addPendingAppLink(Bundle info) {
+ mPendingAppLinkInfo.add(info);
+ }
}
private final class IAppServiceConnection implements ServiceConnection {
@@ -1296,6 +1339,23 @@
}
}
+ if (!serviceState.mPendingAppLinkInfo.isEmpty()) {
+ for (Iterator<Bundle> it = serviceState.mPendingAppLinkInfo.iterator();
+ it.hasNext(); ) {
+ Bundle appLinkInfo = it.next();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ serviceState.mService.notifyAppLinkInfo(appLinkInfo);
+ it.remove();
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in notifyAppLinkInfo(" + appLinkInfo
+ + ") when onServiceConnected", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
List<IBinder> tokensToBeRemoved = new ArrayList<>();
// And create sessions, if any.
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 6628802..e508260 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -243,21 +243,24 @@
@Override
public boolean requestFrontend(@NonNull TunerFrontendRequest request,
- @NonNull int[] frontendHandle) throws RemoteException {
+ @NonNull int[] frontendHandle) {
enforceTunerAccessPermission("requestFrontend");
enforceTrmAccessPermission("requestFrontend");
if (frontendHandle == null) {
- throw new RemoteException("frontendHandle can't be null");
+ Slog.e(TAG, "frontendHandle can't be null");
+ return false;
}
synchronized (mLock) {
if (!checkClientExists(request.clientId)) {
- throw new RemoteException("Request frontend from unregistered client: "
+ Slog.e(TAG, "Request frontend from unregistered client: "
+ request.clientId);
+ return false;
}
// If the request client is holding or sharing a frontend, throw an exception.
if (!getClientProfile(request.clientId).getInUseFrontendHandles().isEmpty()) {
- throw new RemoteException("Release frontend before requesting another one. "
- + "Client id: " + request.clientId);
+ Slog.e(TAG, "Release frontend before requesting another one. Client id: "
+ + request.clientId);
+ return false;
}
return requestFrontendInternal(request, frontendHandle);
}
@@ -1153,7 +1156,8 @@
ClientProfile ownerClient = getClientProfile(fe.getOwnerClientId());
if (ownerClient != null) {
for (int shareOwnerId : ownerClient.getShareFeClientIds()) {
- clearFrontendAndClientMapping(getClientProfile(shareOwnerId));
+ reclaimResource(shareOwnerId,
+ TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND);
}
}
}
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index 7b26fe0..4576957 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -226,9 +226,9 @@
.getSystemService(TelephonyManager.class)
.createForSubscriptionId(subId);
- if (!networkPriority.getAllowedPlmnIds().isEmpty()) {
+ if (!networkPriority.getAllowedOperatorPlmnIds().isEmpty()) {
final String plmnId = subIdSpecificTelephonyMgr.getNetworkOperator();
- if (!networkPriority.getAllowedPlmnIds().contains(plmnId)) {
+ if (!networkPriority.getAllowedOperatorPlmnIds().contains(plmnId)) {
return false;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 2b28478..447f4be 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -157,6 +157,7 @@
import static com.android.server.wm.ActivityRecordProto.NUM_INTERESTING_WINDOWS;
import static com.android.server.wm.ActivityRecordProto.PIP_AUTO_ENTER_ENABLED;
import static com.android.server.wm.ActivityRecordProto.PROC_ID;
+import static com.android.server.wm.ActivityRecordProto.PROVIDES_MAX_BOUNDS;
import static com.android.server.wm.ActivityRecordProto.REPORTED_DRAWN;
import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE;
import static com.android.server.wm.ActivityRecordProto.STARTING_DISPLAYED;
@@ -258,6 +259,7 @@
import android.content.LocusId;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ConstrainDisplayApisConfig;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -585,6 +587,8 @@
*/
private CompatDisplayInsets mCompatDisplayInsets;
+ private static ConstrainDisplayApisConfig sConstrainDisplayApisConfig;
+
boolean pendingVoiceInteractionStart; // Waiting for activity-invoked voice session
IVoiceInteractionSession voiceSession; // Voice interaction session for this activity
@@ -1153,8 +1157,10 @@
if (info.configChanges != 0) {
pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges));
}
- pw.println(prefix + "neverSandboxDisplayApis=" + info.neverSandboxDisplayApis());
- pw.println(prefix + "alwaysSandboxDisplayApis=" + info.alwaysSandboxDisplayApis());
+ pw.println(prefix + "neverSandboxDisplayApis=" + info.neverSandboxDisplayApis(
+ sConstrainDisplayApisConfig));
+ pw.println(prefix + "alwaysSandboxDisplayApis=" + info.alwaysSandboxDisplayApis(
+ sConstrainDisplayApisConfig));
}
if (mLastParentBeforePip != null) {
pw.println(prefix + "lastParentTaskIdBeforePip=" + mLastParentBeforePip.mTaskId);
@@ -1731,6 +1737,10 @@
info.windowLayout.windowLayoutAffinity =
uid + ":" + info.windowLayout.windowLayoutAffinity;
}
+ // Initialize once, when we know all system services are available.
+ if (sConstrainDisplayApisConfig == null) {
+ sConstrainDisplayApisConfig = new ConstrainDisplayApisConfig();
+ }
stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0;
nonLocalizedLabel = aInfo.nonLocalizedLabel;
labelRes = aInfo.labelRes;
@@ -7330,8 +7340,8 @@
+ "should create compatDisplayInsets = %s",
getUid(),
mTmpBounds,
- info.neverSandboxDisplayApis(),
- info.alwaysSandboxDisplayApis(),
+ info.neverSandboxDisplayApis(sConstrainDisplayApisConfig),
+ info.alwaysSandboxDisplayApis(sConstrainDisplayApisConfig),
!matchParentBounds(),
mCompatDisplayInsets != null,
shouldCreateCompatDisplayInsets());
@@ -7892,11 +7902,11 @@
return false;
}
// Never apply sandboxing to an app that should be explicitly excluded from the config.
- if (info != null && info.neverSandboxDisplayApis()) {
+ if (info.neverSandboxDisplayApis(sConstrainDisplayApisConfig)) {
return false;
}
// Always apply sandboxing to an app that should be explicitly included from the config.
- if (info != null && info.alwaysSandboxDisplayApis()) {
+ if (info.alwaysSandboxDisplayApis(sConstrainDisplayApisConfig)) {
return true;
}
// Max bounds should be sandboxed when an activity should have compatDisplayInsets, and it
@@ -8899,6 +8909,9 @@
proto.write(PIP_AUTO_ENTER_ENABLED, pictureInPictureArgs.isAutoEnterEnabled());
proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode());
proto.write(MIN_ASPECT_RATIO, getMinAspectRatio());
+ // Only record if max bounds sandboxing is applied, if the caller has the necessary
+ // permission to access the device configs.
+ proto.write(PROVIDES_MAX_BOUNDS, providesMaxBounds());
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 471b4ce..023db13 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -72,6 +72,8 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY;
import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
@@ -1288,7 +1290,7 @@
// This is used to block background activity launch even if the app is still
// visible to user after user clicking home button.
- final boolean appSwitchAllowed = mService.getBalAppSwitchesAllowed();
+ final int appSwitchState = mService.getBalAppSwitchesState();
// don't abort if the callingUid has a visible window or is a persistent system process
final int callingUidProcState = mService.mActiveUids.getUidState(callingUid);
@@ -1301,7 +1303,9 @@
// Normal apps with visible app window will be allowed to start activity if app switching
// is allowed, or apps like live wallpaper with non app visible window will be allowed.
- if (((appSwitchAllowed || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
+ final boolean appSwitchAllowedOrFg =
+ appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY;
+ if (((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
&& callingUidHasAnyVisibleWindow)
|| isCallingUidPersistentSystemProcess) {
if (DEBUG_ACTIVITY_STARTS) {
@@ -1430,7 +1434,7 @@
// don't abort if the callerApp or other processes of that uid are allowed in any way
if (callerApp != null) {
// first check the original calling process
- if (callerApp.areBackgroundActivityStartsAllowed(appSwitchAllowed)) {
+ if (callerApp.areBackgroundActivityStartsAllowed(appSwitchState)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Background activity start allowed: callerApp process (pid = "
+ callerApp.getPid() + ", uid = " + callerAppUid + ") is allowed");
@@ -1444,7 +1448,7 @@
for (int i = uidProcesses.size() - 1; i >= 0; i--) {
final WindowProcessController proc = uidProcesses.valueAt(i);
if (proc != callerApp
- && proc.areBackgroundActivityStartsAllowed(appSwitchAllowed)) {
+ && proc.areBackgroundActivityStartsAllowed(appSwitchState)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG,
"Background activity start allowed: process " + proc.getPid()
@@ -1458,7 +1462,7 @@
// anything that has fallen through would currently be aborted
Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
+ "; callingUid: " + callingUid
- + "; appSwitchAllowed: " + appSwitchAllowed
+ + "; appSwitchState: " + appSwitchState
+ "; isCallingUidForeground: " + isCallingUidForeground
+ "; callingUidHasAnyVisibleWindow: " + callingUidHasAnyVisibleWindow
+ "; callingUidProcState: " + DebugUtils.valueToString(ActivityManager.class,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index bbccac4..be01646 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -505,7 +505,27 @@
* Whether normal application switches are allowed; a call to {@link #stopAppSwitches()
* disables this.
*/
- private volatile boolean mAppSwitchesAllowed = true;
+ private volatile int mAppSwitchesState = APP_SWITCH_ALLOW;
+
+ // The duration of resuming foreground app switch from disallow.
+ private static final long RESUME_FG_APP_SWITCH_MS = 500;
+
+ /** App switch is not allowed. */
+ static final int APP_SWITCH_DISALLOW = 0;
+
+ /** App switch is allowed only if the activity launch was requested by a foreground app. */
+ static final int APP_SWITCH_FG_ONLY = 1;
+
+ /** App switch is allowed. */
+ static final int APP_SWITCH_ALLOW = 2;
+
+ @IntDef({
+ APP_SWITCH_DISALLOW,
+ APP_SWITCH_FG_ONLY,
+ APP_SWITCH_ALLOW,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface AppSwitchState {}
/**
* Last stop app switches time, apps finished before this time cannot start background activity
@@ -1250,7 +1270,7 @@
if (topFocusedRootTask != null && topFocusedRootTask.getTopResumedActivity() != null
&& topFocusedRootTask.getTopResumedActivity().info.applicationInfo.uid
== Binder.getCallingUid()) {
- mAppSwitchesAllowed = true;
+ mAppSwitchesState = APP_SWITCH_ALLOW;
}
}
return pir.sendInner(0, fillInIntent, resolvedType, allowlistToken, null, null,
@@ -2169,8 +2189,8 @@
/**
* Return true if app switching is allowed.
*/
- boolean getBalAppSwitchesAllowed() {
- return mAppSwitchesAllowed;
+ @AppSwitchState int getBalAppSwitchesState() {
+ return mAppSwitchesState;
}
/** Register an {@link AnrController} to control the ANR dialog behavior */
@@ -3691,8 +3711,10 @@
public void stopAppSwitches() {
mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "stopAppSwitches");
synchronized (mGlobalLock) {
- mAppSwitchesAllowed = false;
+ mAppSwitchesState = APP_SWITCH_DISALLOW;
mLastStopAppSwitchesTime = SystemClock.uptimeMillis();
+ mH.removeMessages(H.RESUME_FG_APP_SWITCH_MSG);
+ mH.sendEmptyMessageDelayed(H.RESUME_FG_APP_SWITCH_MSG, RESUME_FG_APP_SWITCH_MS);
}
}
@@ -3700,7 +3722,8 @@
public void resumeAppSwitches() {
mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "resumeAppSwitches");
synchronized (mGlobalLock) {
- mAppSwitchesAllowed = true;
+ mAppSwitchesState = APP_SWITCH_ALLOW;
+ mH.removeMessages(H.RESUME_FG_APP_SWITCH_MSG);
}
}
@@ -5193,6 +5216,7 @@
static final int REPORT_TIME_TRACKER_MSG = 1;
static final int UPDATE_PROCESS_ANIMATING_STATE = 2;
static final int END_POWER_MODE_UNKNOWN_VISIBILITY_MSG = 3;
+ static final int RESUME_FG_APP_SWITCH_MSG = 4;
static final int FIRST_ACTIVITY_TASK_MSG = 100;
static final int FIRST_SUPERVISOR_TASK_MSG = 200;
@@ -5230,6 +5254,14 @@
}
}
break;
+ case RESUME_FG_APP_SWITCH_MSG: {
+ synchronized (mGlobalLock) {
+ if (mAppSwitchesState == APP_SWITCH_DISALLOW) {
+ mAppSwitchesState = APP_SWITCH_FG_ONLY;
+ }
+ }
+ }
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index 71a10df..0afd872 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -20,6 +20,8 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -70,13 +72,13 @@
}
boolean areBackgroundActivityStartsAllowed(int pid, int uid, String packageName,
- boolean appSwitchAllowed, boolean isCheckingForFgsStart,
+ int appSwitchState, boolean isCheckingForFgsStart,
boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges,
long lastStopAppSwitchesTime, long lastActivityLaunchTime,
long lastActivityFinishTime) {
// If app switching is not allowed, we ignore all the start activity grace period
// exception so apps cannot start itself in onPause() after pressing home button.
- if (appSwitchAllowed) {
+ if (appSwitchState == APP_SWITCH_ALLOW) {
// Allow if any activity in the caller has either started or finished very recently, and
// it must be started or finished after last stop app switches time.
final long now = SystemClock.uptimeMillis();
@@ -111,7 +113,8 @@
return true;
}
// Allow if the caller has an activity in any foreground task.
- if (appSwitchAllowed && hasActivityInVisibleTask) {
+ if (hasActivityInVisibleTask
+ && (appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[Process(" + pid
+ ")] Activity start allowed: process has activity in foreground task");
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3864c8f..44d3623 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -167,7 +167,6 @@
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.ColorSpace;
-import android.graphics.GraphicBuffer;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
@@ -4092,8 +4091,7 @@
// Make IME snapshot as trusted overlay
InputMonitor.setTrustedOverlayInputInfo(imeSurface, t, getDisplayId(),
"IME-snapshot-surface");
- GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(buffer);
- t.setBuffer(imeSurface, graphicBuffer);
+ t.setBuffer(imeSurface, buffer);
t.setColorSpace(mSurfaceControl, ColorSpace.get(ColorSpace.Named.SRGB));
t.setRelativeLayer(imeSurface, activity.getSurfaceControl(), 1);
t.setPosition(imeSurface, mInputMethodWindow.getDisplayFrame().left,
@@ -5885,6 +5883,13 @@
rootTask.ensureActivitiesVisible(starting, configChanges, preserveWindows,
notifyClients);
});
+ if (mTransitionController.isCollecting()
+ && mWallpaperController.getWallpaperTarget() != null) {
+ // Also update wallpapers so that their requestedVisibility immediately reflects
+ // the changes to activity visibility.
+ // TODO(b/206005136): Move visibleRequested logic up to WindowToken.
+ mWallpaperController.adjustWallpaperWindows();
+ }
} finally {
mAtmService.mTaskSupervisor.endActivityVisibilityUpdate();
mInEnsureActivitiesVisible = false;
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 94a175c..8a2d116 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -251,15 +251,47 @@
*/
boolean activityBlockedFromFinish(ActivityRecord activity) {
final Task task = activity.getTask();
- if (activity == task.getRootActivity()
- && activity == task.getTopNonFinishingActivity()
- && task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV
- && isRootTask(task)) {
- Slog.i(TAG, "Not finishing task in lock task mode");
- showLockTaskToast();
- return true;
+ if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV || !isRootTask(task)) {
+ return false;
}
- return false;
+
+ final ActivityRecord taskTop = task.getTopNonFinishingActivity();
+ final ActivityRecord taskRoot = task.getRootActivity();
+ // If task has more than one Activity, verify if there's only adjacent TaskFragments that
+ // should be finish together in the Task.
+ if (activity != taskRoot || activity != taskTop) {
+ final TaskFragment taskFragment = activity.getTaskFragment();
+ final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
+ if (taskFragment.asTask() != null
+ || !taskFragment.isDelayLastActivityRemoval()
+ || adjacentTaskFragment == null) {
+ // Don't block activity from finishing if the TaskFragment don't have any adjacent
+ // TaskFragment, or it won't finish together with its adjacent TaskFragment.
+ return false;
+ }
+
+ final boolean hasOtherActivityInTaskFragment =
+ taskFragment.getActivity(a -> !a.finishing && a != activity) != null;
+ if (hasOtherActivityInTaskFragment) {
+ // Don't block activity from finishing if there's other Activity in the same
+ // TaskFragment.
+ return false;
+ }
+
+ final boolean hasOtherActivityInTask = task.getActivity(a -> !a.finishing
+ && a != activity && a.getTaskFragment() != adjacentTaskFragment) != null;
+ if (hasOtherActivityInTask) {
+ // Do not block activity from finishing if there are another running activities
+ // after the current and adjacent TaskFragments are removed. Note that we don't
+ // check activities in adjacent TaskFragment because it will be finished together
+ // with TaskFragment regardless of numbers of activities.
+ return false;
+ }
+ }
+
+ Slog.i(TAG, "Not finishing task in lock task mode");
+ showLockTaskToast();
+ return true;
}
/**
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index b54208d..9ad30da 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -255,25 +255,27 @@
mDestRotatedBounds = null;
mPipTransaction = null;
final Rect areaBounds = taskArea.getBounds();
- if (pipTx != null) {
+ if (pipTx != null && pipTx.mPosition != null) {
// The transaction from recents animation is in old rotation. So the position needs to
// be rotated.
- float dx = pipTx.mPositionX;
- float dy = pipTx.mPositionY;
+ float dx = pipTx.mPosition.x;
+ float dy = pipTx.mPosition.y;
final Matrix matrix = pipTx.getMatrix();
if (pipTx.mRotation == 90) {
- dx = pipTx.mPositionY;
- dy = areaBounds.right - pipTx.mPositionX;
+ dx = pipTx.mPosition.y;
+ dy = areaBounds.right - pipTx.mPosition.x;
matrix.postRotate(-90);
} else if (pipTx.mRotation == -90) {
- dx = areaBounds.bottom - pipTx.mPositionY;
- dy = pipTx.mPositionX;
+ dx = areaBounds.bottom - pipTx.mPosition.y;
+ dy = pipTx.mPosition.x;
matrix.postRotate(90);
}
matrix.postTranslate(dx, dy);
final SurfaceControl leash = pinnedTask.getSurfaceControl();
- t.setMatrix(leash, matrix, new float[9])
- .setCornerRadius(leash, pipTx.mCornerRadius);
+ t.setMatrix(leash, matrix, new float[9]);
+ if (pipTx.hasCornerRadiusSet()) {
+ t.setCornerRadius(leash, pipTx.mCornerRadius);
+ }
Slog.i(TAG, "Seamless rotation PiP tx=" + pipTx + " pos=" + dx + "," + dy);
return;
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 38e3e3a..f97a48b 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -263,13 +263,6 @@
"finish(%b): mCanceled=%b", moveHomeToTop, mCanceled);
final long token = Binder.clearCallingIdentity();
try {
- synchronized (mService.getWindowManagerLock()) {
- // Remove all new task targets.
- for (int i = mPendingNewTaskTargets.size() - 1; i >= 0; i--) {
- removeTaskInternal(mPendingNewTaskTargets.get(i));
- }
- }
-
// Note, the callback will handle its own synchronization, do not lock on WM lock
// prior to calling the callback
mCallbacks.onAnimationFinished(moveHomeToTop
@@ -759,7 +752,7 @@
// the task-id with the leaf id.
final Task leafTask = task.getTopLeafTask();
int taskId = leafTask.mTaskId;
- TaskAnimationAdapter adapter = (TaskAnimationAdapter) addAnimation(task,
+ TaskAnimationAdapter adapter = addAnimation(task,
!recentTaskIds.get(taskId), true /* hidden */, finishedCallback);
mPendingNewTaskTargets.add(taskId);
return adapter.createRemoteAnimationTarget(taskId);
@@ -1012,6 +1005,7 @@
taskAdapter.onCleanup();
}
// Should already be empty, but clean-up pending task-appears in-case they weren't sent.
+ mPendingNewTaskTargets.clear();
mPendingTaskAppears.clear();
for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 038699b..3e55811 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3115,14 +3115,6 @@
}
@Override
- boolean fillsParent() {
- // From the perspective of policy, we still want to report that this task fills parent
- // in fullscreen windowing mode even it doesn't match parent bounds because there will be
- // letterbox around its real content.
- return getWindowingMode() == WINDOWING_MODE_FULLSCREEN || matchParentBounds();
- }
-
- @Override
void forAllLeafTasks(Consumer<Task> callback, boolean traverseTopToBottom) {
final int count = mChildren.size();
boolean isLeafTask = true;
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index fdaa2fc..97cb512 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -240,7 +240,7 @@
/**
* Whether to delay the last activity of TaskFragment being immediately removed while finishing.
* This should only be set on a embedded TaskFragment, where the organizer can have the
- * opportunity to perform other actions or animations.
+ * opportunity to perform animations and finishing the adjacent TaskFragment.
*/
private boolean mDelayLastActivityRemoval;
@@ -2342,6 +2342,14 @@
return true;
}
+ @Override
+ boolean fillsParent() {
+ // From the perspective of policy, we still want to report that this task fills parent
+ // in fullscreen windowing mode even it doesn't match parent bounds because there will be
+ // letterbox around its real content.
+ return getWindowingMode() == WINDOWING_MODE_FULLSCREEN || matchParentBounds();
+ }
+
boolean dump(String prefix, FileDescriptor fd, PrintWriter pw, boolean dumpAll,
boolean dumpClient, String dumpPackage, final boolean needSep, Runnable header) {
boolean printed = false;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 3974747..dae004d 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -365,6 +365,13 @@
t.setPosition(targetLeash, tmpPos.x, tmpPos.y);
t.setCornerRadius(targetLeash, 0);
t.setShadowRadius(targetLeash, 0);
+ // The bounds sent to the transition is always a real bounds. This means we lose
+ // information about "null" bounds (inheriting from parent). Core will fix-up
+ // non-organized window surface bounds; however, since Core can't touch organized
+ // surfaces, add the "inherit from parent" restoration here.
+ if (target.isOrganized() && target.matchParentBounds()) {
+ t.setWindowCrop(targetLeash, -1, -1);
+ }
displays.add(target.getDisplayContent());
}
}
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index cc52713..7956a11 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -173,7 +173,7 @@
@VisibleForTesting
class WindowContextListenerImpl implements WindowContainerListener {
- @NonNull private final IBinder mClientToken;
+ @NonNull private final IWindowToken mClientToken;
private final int mOwnerUid;
@NonNull private WindowContainer<?> mContainer;
/**
@@ -193,7 +193,7 @@
private WindowContextListenerImpl(IBinder clientToken, WindowContainer<?> container,
int ownerUid, @WindowType int type, @Nullable Bundle options) {
- mClientToken = clientToken;
+ mClientToken = IWindowToken.Stub.asInterface(clientToken);
mContainer = Objects.requireNonNull(container);
mOwnerUid = ownerUid;
mType = type;
@@ -205,7 +205,7 @@
mDeathRecipient = deathRecipient;
} catch (RemoteException e) {
ProtoLog.e(WM_ERROR, "Could not register window container listener token=%s, "
- + "container=%s", mClientToken, mContainer);
+ + "container=%s", clientToken, mContainer);
}
}
@@ -228,17 +228,17 @@
}
private void register() {
+ final IBinder token = mClientToken.asBinder();
if (mDeathRecipient == null) {
- throw new IllegalStateException("Invalid client token: " + mClientToken);
+ throw new IllegalStateException("Invalid client token: " + token);
}
- mListeners.putIfAbsent(mClientToken, this);
+ mListeners.putIfAbsent(token, this);
mContainer.registerWindowContainerListener(this);
- reportConfigToWindowTokenClient();
}
private void unregister() {
mContainer.unregisterWindowContainerListener(this);
- mListeners.remove(mClientToken);
+ mListeners.remove(mClientToken.asBinder());
}
private void clear() {
@@ -258,19 +258,24 @@
private void reportConfigToWindowTokenClient() {
if (mDeathRecipient == null) {
- throw new IllegalStateException("Invalid client token: " + mClientToken);
+ throw new IllegalStateException("Invalid client token: " + mClientToken.asBinder());
+ }
+ final DisplayContent dc = mContainer.getDisplayContent();
+ if (!dc.isReady()) {
+ // Do not report configuration when booting. The latest configuration will be sent
+ // when WindowManagerService#displayReady().
+ return;
}
// If the display of window context associated window container is suspended, don't
// report the configuration update. Note that we still dispatch the configuration update
// to WindowProviderService to make it compatible with Service#onConfigurationChanged.
// Service always receives #onConfigurationChanged callback regardless of display state.
- if (!isWindowProviderService(mOptions)
- && isSuspendedState(mContainer.getDisplayContent().getDisplayInfo().state)) {
+ if (!isWindowProviderService(mOptions) && isSuspendedState(dc.getDisplayInfo().state)) {
mHasPendingConfiguration = true;
return;
}
final Configuration config = mContainer.getConfiguration();
- final int displayId = mContainer.getDisplayContent().getDisplayId();
+ final int displayId = dc.getDisplayId();
if (mLastReportedConfig == null) {
mLastReportedConfig = new Configuration();
}
@@ -282,9 +287,8 @@
mLastReportedConfig.setTo(config);
mLastReportedDisplay = displayId;
- IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(mClientToken);
try {
- windowTokenClient.onConfigurationChanged(config, displayId);
+ mClientToken.onConfigurationChanged(config, displayId);
} catch (RemoteException e) {
ProtoLog.w(WM_ERROR, "Could not report config changes to the window token client.");
}
@@ -294,7 +298,7 @@
@Override
public void onRemoved() {
if (mDeathRecipient == null) {
- throw new IllegalStateException("Invalid client token: " + mClientToken);
+ throw new IllegalStateException("Invalid client token: " + mClientToken.asBinder());
}
final WindowToken windowToken = mContainer.asWindowToken();
if (windowToken != null && windowToken.isFromClient()) {
@@ -312,9 +316,8 @@
}
}
mDeathRecipient.unlinkToDeath();
- IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(mClientToken);
try {
- windowTokenClient.onWindowTokenRemoved();
+ mClientToken.onWindowTokenRemoved();
} catch (RemoteException e) {
ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client.");
}
@@ -323,7 +326,7 @@
@Override
public String toString() {
- return "WindowContextListenerImpl{clientToken=" + mClientToken + ", "
+ return "WindowContextListenerImpl{clientToken=" + mClientToken.asBinder() + ", "
+ "container=" + mContainer + "}";
}
@@ -337,11 +340,11 @@
}
void linkToDeath() throws RemoteException {
- mClientToken.linkToDeath(this, 0);
+ mClientToken.asBinder().linkToDeath(this, 0);
}
void unlinkToDeath() {
- mClientToken.unlinkToDeath(this, 0);
+ mClientToken.asBinder().unlinkToDeath(this, 0);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 525d84be..79dcbcb 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -33,6 +33,7 @@
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_SHORTCUT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
@@ -79,6 +80,8 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.LocalServices;
+import com.android.server.pm.LauncherAppsService.LauncherAppsServiceInternal;
import java.util.ArrayList;
import java.util.HashMap;
@@ -785,6 +788,22 @@
null /* requiredPermission */, options);
break;
}
+ case HIERARCHY_OP_TYPE_START_SHORTCUT: {
+ final Bundle launchOpts = hop.getLaunchOptions();
+ final String callingPackage = launchOpts.getString(
+ WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_SHORTCUT_CALLING_PACKAGE);
+ launchOpts.remove(
+ WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_SHORTCUT_CALLING_PACKAGE);
+
+ final LauncherAppsServiceInternal launcherApps = LocalServices.getService(
+ LauncherAppsServiceInternal.class);
+
+ launcherApps.startShortcut(caller.mUid, caller.mPid, callingPackage,
+ hop.getShortcutInfo().getPackage(), null /* default featureId */,
+ hop.getShortcutInfo().getId(), null /* sourceBounds */, launchOpts,
+ hop.getShortcutInfo().getUserId());
+ break;
+ }
case HIERARCHY_OP_TYPE_REPARENT_CHILDREN: {
final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer());
final WindowContainer newParent = hop.getNewParent() != null
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 1cfbe07..3ccb06c 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -512,19 +512,19 @@
*/
@HotPath(caller = HotPath.START_SERVICE)
public boolean areBackgroundFgsStartsAllowed() {
- return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesAllowed(),
+ return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesState(),
true /* isCheckingForFgsStart */);
}
- boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed) {
- return areBackgroundActivityStartsAllowed(appSwitchAllowed,
+ boolean areBackgroundActivityStartsAllowed(int appSwitchState) {
+ return areBackgroundActivityStartsAllowed(appSwitchState,
false /* isCheckingForFgsStart */);
}
- private boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed,
+ private boolean areBackgroundActivityStartsAllowed(int appSwitchState,
boolean isCheckingForFgsStart) {
return mBgLaunchController.areBackgroundActivityStartsAllowed(mPid, mUid, mInfo.packageName,
- appSwitchAllowed, isCheckingForFgsStart, hasActivityInVisibleTask(),
+ appSwitchState, isCheckingForFgsStart, hasActivityInVisibleTask(),
mInstrumentingWithBackgroundActivityStartPrivileges,
mAtm.getLastStopAppSwitchesTime(),
mLastActivityLaunchTime, mLastActivityFinishTime);
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index f72f2cc..f5eb0a7 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -35,6 +35,7 @@
"com_android_server_am_BatteryStatsService.cpp",
"com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp",
"com_android_server_ConsumerIrService.cpp",
+ "com_android_server_companion_virtual_InputController.cpp",
"com_android_server_devicepolicy_CryptoTestHelper.cpp",
"com_android_server_connectivity_Vpn.cpp",
"com_android_server_gpu_GpuService.cpp",
diff --git a/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp b/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
index ccb4f59..1c574fb 100644
--- a/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
+++ b/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
@@ -151,13 +151,8 @@
return -1;
}
- if (!jniRegisterNativeMethods(env, "com/android/server/usb/UsbAlsaJackDetector",
- method_table, NELEM(method_table))) {
- ALOGE("Can't register UsbAlsaJackDetector native methods");
- return -1;
- }
-
- return 0;
+ return jniRegisterNativeMethods(env, "com/android/server/usb/UsbAlsaJackDetector",
+ method_table, NELEM(method_table));
}
}
diff --git a/services/core/jni/com_android_server_companion_virtual_InputController.cpp b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
new file mode 100644
index 0000000..43018a9
--- /dev/null
+++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
@@ -0,0 +1,455 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "InputController"
+
+#include <android-base/unique_fd.h>
+#include <android/input.h>
+#include <android/keycodes.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/uinput.h>
+#include <math.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <utils/Log.h>
+
+#include <map>
+#include <string>
+
+namespace android {
+
+enum class DeviceType {
+ KEYBOARD,
+ MOUSE,
+ TOUCHSCREEN,
+};
+
+enum class UinputAction {
+ RELEASE = 0,
+ PRESS = 1,
+ MOVE = 2,
+ CANCEL = 3,
+};
+
+static std::map<int, UinputAction> BUTTON_ACTION_MAPPING = {
+ {AMOTION_EVENT_ACTION_BUTTON_PRESS, UinputAction::PRESS},
+ {AMOTION_EVENT_ACTION_BUTTON_RELEASE, UinputAction::RELEASE},
+};
+
+static std::map<int, UinputAction> KEY_ACTION_MAPPING = {
+ {AKEY_EVENT_ACTION_DOWN, UinputAction::PRESS},
+ {AKEY_EVENT_ACTION_UP, UinputAction::RELEASE},
+};
+
+static std::map<int, UinputAction> TOUCH_ACTION_MAPPING = {
+ {AMOTION_EVENT_ACTION_DOWN, UinputAction::PRESS},
+ {AMOTION_EVENT_ACTION_UP, UinputAction::RELEASE},
+ {AMOTION_EVENT_ACTION_MOVE, UinputAction::MOVE},
+ {AMOTION_EVENT_ACTION_CANCEL, UinputAction::CANCEL},
+};
+
+// Button code mapping from https://source.android.com/devices/input/touch-devices
+static std::map<int, int> BUTTON_CODE_MAPPING = {
+ {AMOTION_EVENT_BUTTON_PRIMARY, BTN_LEFT}, {AMOTION_EVENT_BUTTON_SECONDARY, BTN_RIGHT},
+ {AMOTION_EVENT_BUTTON_TERTIARY, BTN_MIDDLE}, {AMOTION_EVENT_BUTTON_BACK, BTN_BACK},
+ {AMOTION_EVENT_BUTTON_FORWARD, BTN_FORWARD},
+};
+
+// Tool type mapping from https://source.android.com/devices/input/touch-devices
+static std::map<int, int> TOOL_TYPE_MAPPING = {
+ {AMOTION_EVENT_TOOL_TYPE_FINGER, MT_TOOL_FINGER},
+ {AMOTION_EVENT_TOOL_TYPE_PALM, MT_TOOL_PALM},
+};
+
+// Keycode mapping from https://source.android.com/devices/input/keyboard-devices
+static std::map<int, int> KEY_CODE_MAPPING = {
+ {AKEYCODE_0, KEY_0},
+ {AKEYCODE_1, KEY_1},
+ {AKEYCODE_2, KEY_2},
+ {AKEYCODE_3, KEY_3},
+ {AKEYCODE_4, KEY_4},
+ {AKEYCODE_5, KEY_5},
+ {AKEYCODE_6, KEY_6},
+ {AKEYCODE_7, KEY_7},
+ {AKEYCODE_8, KEY_8},
+ {AKEYCODE_9, KEY_9},
+ {AKEYCODE_A, KEY_A},
+ {AKEYCODE_B, KEY_B},
+ {AKEYCODE_C, KEY_C},
+ {AKEYCODE_D, KEY_D},
+ {AKEYCODE_E, KEY_E},
+ {AKEYCODE_F, KEY_F},
+ {AKEYCODE_G, KEY_G},
+ {AKEYCODE_H, KEY_H},
+ {AKEYCODE_I, KEY_I},
+ {AKEYCODE_J, KEY_J},
+ {AKEYCODE_K, KEY_K},
+ {AKEYCODE_L, KEY_L},
+ {AKEYCODE_M, KEY_M},
+ {AKEYCODE_N, KEY_N},
+ {AKEYCODE_O, KEY_O},
+ {AKEYCODE_P, KEY_P},
+ {AKEYCODE_Q, KEY_Q},
+ {AKEYCODE_R, KEY_R},
+ {AKEYCODE_S, KEY_S},
+ {AKEYCODE_T, KEY_T},
+ {AKEYCODE_U, KEY_U},
+ {AKEYCODE_V, KEY_V},
+ {AKEYCODE_W, KEY_W},
+ {AKEYCODE_X, KEY_X},
+ {AKEYCODE_Y, KEY_Y},
+ {AKEYCODE_Z, KEY_Z},
+ {AKEYCODE_GRAVE, KEY_GRAVE},
+ {AKEYCODE_MINUS, KEY_MINUS},
+ {AKEYCODE_EQUALS, KEY_EQUAL},
+ {AKEYCODE_LEFT_BRACKET, KEY_LEFTBRACE},
+ {AKEYCODE_RIGHT_BRACKET, KEY_RIGHTBRACE},
+ {AKEYCODE_BACKSLASH, KEY_BACKSLASH},
+ {AKEYCODE_SEMICOLON, KEY_SEMICOLON},
+ {AKEYCODE_APOSTROPHE, KEY_APOSTROPHE},
+ {AKEYCODE_COMMA, KEY_COMMA},
+ {AKEYCODE_PERIOD, KEY_DOT},
+ {AKEYCODE_SLASH, KEY_SLASH},
+ {AKEYCODE_ALT_LEFT, KEY_LEFTALT},
+ {AKEYCODE_ALT_RIGHT, KEY_RIGHTALT},
+ {AKEYCODE_CTRL_LEFT, KEY_LEFTCTRL},
+ {AKEYCODE_CTRL_RIGHT, KEY_RIGHTCTRL},
+ {AKEYCODE_SHIFT_LEFT, KEY_LEFTSHIFT},
+ {AKEYCODE_SHIFT_RIGHT, KEY_RIGHTSHIFT},
+ {AKEYCODE_META_LEFT, KEY_LEFTMETA},
+ {AKEYCODE_META_RIGHT, KEY_RIGHTMETA},
+ {AKEYCODE_CAPS_LOCK, KEY_CAPSLOCK},
+ {AKEYCODE_SCROLL_LOCK, KEY_SCROLLLOCK},
+ {AKEYCODE_NUM_LOCK, KEY_NUMLOCK},
+ {AKEYCODE_ENTER, KEY_ENTER},
+ {AKEYCODE_TAB, KEY_TAB},
+ {AKEYCODE_SPACE, KEY_SPACE},
+ {AKEYCODE_DPAD_DOWN, KEY_DOWN},
+ {AKEYCODE_DPAD_UP, KEY_UP},
+ {AKEYCODE_DPAD_LEFT, KEY_LEFT},
+ {AKEYCODE_DPAD_RIGHT, KEY_RIGHT},
+ {AKEYCODE_MOVE_END, KEY_END},
+ {AKEYCODE_MOVE_HOME, KEY_HOME},
+ {AKEYCODE_PAGE_DOWN, KEY_PAGEDOWN},
+ {AKEYCODE_PAGE_UP, KEY_PAGEUP},
+ {AKEYCODE_DEL, KEY_BACKSPACE},
+ {AKEYCODE_FORWARD_DEL, KEY_DELETE},
+ {AKEYCODE_INSERT, KEY_INSERT},
+ {AKEYCODE_ESCAPE, KEY_ESC},
+ {AKEYCODE_BREAK, KEY_PAUSE},
+ {AKEYCODE_F1, KEY_F1},
+ {AKEYCODE_F2, KEY_F2},
+ {AKEYCODE_F3, KEY_F3},
+ {AKEYCODE_F4, KEY_F4},
+ {AKEYCODE_F5, KEY_F5},
+ {AKEYCODE_F6, KEY_F6},
+ {AKEYCODE_F7, KEY_F7},
+ {AKEYCODE_F8, KEY_F8},
+ {AKEYCODE_F9, KEY_F9},
+ {AKEYCODE_F10, KEY_F10},
+ {AKEYCODE_F11, KEY_F11},
+ {AKEYCODE_F12, KEY_F12},
+ {AKEYCODE_BACK, KEY_BACK},
+ {AKEYCODE_FORWARD, KEY_FORWARD},
+ {AKEYCODE_NUMPAD_1, KEY_KP1},
+ {AKEYCODE_NUMPAD_2, KEY_KP2},
+ {AKEYCODE_NUMPAD_3, KEY_KP3},
+ {AKEYCODE_NUMPAD_4, KEY_KP4},
+ {AKEYCODE_NUMPAD_5, KEY_KP5},
+ {AKEYCODE_NUMPAD_6, KEY_KP6},
+ {AKEYCODE_NUMPAD_7, KEY_KP7},
+ {AKEYCODE_NUMPAD_8, KEY_KP8},
+ {AKEYCODE_NUMPAD_9, KEY_KP9},
+ {AKEYCODE_NUMPAD_0, KEY_KP0},
+ {AKEYCODE_NUMPAD_ADD, KEY_KPPLUS},
+ {AKEYCODE_NUMPAD_SUBTRACT, KEY_KPMINUS},
+ {AKEYCODE_NUMPAD_MULTIPLY, KEY_KPASTERISK},
+ {AKEYCODE_NUMPAD_DIVIDE, KEY_KPSLASH},
+ {AKEYCODE_NUMPAD_DOT, KEY_KPDOT},
+ {AKEYCODE_NUMPAD_ENTER, KEY_KPENTER},
+ {AKEYCODE_NUMPAD_EQUALS, KEY_KPEQUAL},
+ {AKEYCODE_NUMPAD_COMMA, KEY_KPCOMMA},
+};
+
+/** Creates a new uinput device and assigns a file descriptor. */
+static int openUinput(const char* readableName, jint vendorId, jint productId,
+ DeviceType deviceType, jint screenHeight, jint screenWidth) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK)));
+ if (fd < 0) {
+ ALOGE("Error creating uinput device: %s", strerror(errno));
+ return -errno;
+ }
+
+ ioctl(fd, UI_SET_EVBIT, EV_KEY);
+ ioctl(fd, UI_SET_EVBIT, EV_SYN);
+ switch (deviceType) {
+ case DeviceType::KEYBOARD:
+ for (const auto& [ignored, keyCode] : KEY_CODE_MAPPING) {
+ ioctl(fd, UI_SET_KEYBIT, keyCode);
+ }
+ break;
+ case DeviceType::MOUSE:
+ ioctl(fd, UI_SET_EVBIT, EV_REL);
+ ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);
+ ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT);
+ ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE);
+ ioctl(fd, UI_SET_KEYBIT, BTN_BACK);
+ ioctl(fd, UI_SET_KEYBIT, BTN_FORWARD);
+ ioctl(fd, UI_SET_RELBIT, REL_X);
+ ioctl(fd, UI_SET_RELBIT, REL_Y);
+ ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
+ ioctl(fd, UI_SET_RELBIT, REL_HWHEEL);
+ break;
+ case DeviceType::TOUCHSCREEN:
+ ioctl(fd, UI_SET_EVBIT, EV_ABS);
+ ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_PRESSURE);
+ ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
+ }
+
+ int version;
+ if (ioctl(fd, UI_GET_VERSION, &version) == 0 && version >= 5) {
+ uinput_setup setup;
+ memset(&setup, 0, sizeof(setup));
+ strlcpy(setup.name, readableName, UINPUT_MAX_NAME_SIZE);
+ setup.id.version = 1;
+ setup.id.bustype = BUS_VIRTUAL;
+ setup.id.vendor = vendorId;
+ setup.id.product = productId;
+ if (deviceType == DeviceType::TOUCHSCREEN) {
+ uinput_abs_setup xAbsSetup;
+ xAbsSetup.code = ABS_MT_POSITION_X;
+ xAbsSetup.absinfo.maximum = screenWidth - 1;
+ xAbsSetup.absinfo.minimum = 0;
+ ioctl(fd, UI_ABS_SETUP, xAbsSetup);
+ uinput_abs_setup yAbsSetup;
+ yAbsSetup.code = ABS_MT_POSITION_Y;
+ yAbsSetup.absinfo.maximum = screenHeight - 1;
+ yAbsSetup.absinfo.minimum = 0;
+ ioctl(fd, UI_ABS_SETUP, yAbsSetup);
+ uinput_abs_setup majorAbsSetup;
+ majorAbsSetup.code = ABS_MT_TOUCH_MAJOR;
+ majorAbsSetup.absinfo.maximum = screenWidth - 1;
+ majorAbsSetup.absinfo.minimum = 0;
+ ioctl(fd, UI_ABS_SETUP, majorAbsSetup);
+ uinput_abs_setup pressureAbsSetup;
+ pressureAbsSetup.code = ABS_MT_PRESSURE;
+ pressureAbsSetup.absinfo.maximum = 255;
+ pressureAbsSetup.absinfo.minimum = 0;
+ ioctl(fd, UI_ABS_SETUP, pressureAbsSetup);
+ }
+ if (ioctl(fd, UI_DEV_SETUP, &setup) != 0) {
+ ALOGE("Error creating uinput device: %s", strerror(errno));
+ return -errno;
+ }
+ } else {
+ // UI_DEV_SETUP was not introduced until version 5. Try setting up manually.
+ uinput_user_dev fallback;
+ memset(&fallback, 0, sizeof(fallback));
+ strlcpy(fallback.name, readableName, UINPUT_MAX_NAME_SIZE);
+ fallback.id.version = 1;
+ fallback.id.bustype = BUS_VIRTUAL;
+ fallback.id.vendor = vendorId;
+ fallback.id.product = productId;
+ if (deviceType == DeviceType::TOUCHSCREEN) {
+ fallback.absmin[ABS_MT_POSITION_X] = 0;
+ fallback.absmax[ABS_MT_POSITION_X] = screenWidth - 1;
+ fallback.absmin[ABS_MT_POSITION_Y] = 0;
+ fallback.absmax[ABS_MT_POSITION_Y] = screenHeight - 1;
+ fallback.absmin[ABS_MT_TOUCH_MAJOR] = 0;
+ fallback.absmax[ABS_MT_TOUCH_MAJOR] = screenWidth - 1;
+ fallback.absmin[ABS_MT_PRESSURE] = 0;
+ fallback.absmax[ABS_MT_PRESSURE] = 255;
+ }
+ if (TEMP_FAILURE_RETRY(write(fd, &fallback, sizeof(fallback))) != sizeof(fallback)) {
+ ALOGE("Error creating uinput device: %s", strerror(errno));
+ return -errno;
+ }
+ }
+
+ if (ioctl(fd, UI_DEV_CREATE) != 0) {
+ ALOGE("Error creating uinput device: %s", strerror(errno));
+ return -errno;
+ }
+
+ return fd.release();
+}
+
+static int openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productId,
+ DeviceType deviceType, int screenHeight, int screenWidth) {
+ ScopedUtfChars readableName(env, name);
+ return openUinput(readableName.c_str(), vendorId, productId, deviceType, screenHeight,
+ screenWidth);
+}
+
+static int nativeOpenUinputKeyboard(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
+ jint productId) {
+ return openUinputJni(env, name, vendorId, productId, DeviceType::KEYBOARD, /* screenHeight */ 0,
+ /* screenWidth */ 0);
+}
+
+static int nativeOpenUinputMouse(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
+ jint productId) {
+ return openUinputJni(env, name, vendorId, productId, DeviceType::MOUSE, /* screenHeight */ 0,
+ /* screenWidth */ 0);
+}
+
+static int nativeOpenUinputTouchscreen(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
+ jint productId, jint height, jint width) {
+ return openUinputJni(env, name, vendorId, productId, DeviceType::TOUCHSCREEN, height, width);
+}
+
+static bool nativeCloseUinput(JNIEnv* env, jobject thiz, jint fd) {
+ ioctl(fd, UI_DEV_DESTROY);
+ return close(fd);
+}
+
+static bool writeInputEvent(int fd, uint16_t type, uint16_t code, int32_t value) {
+ struct input_event ev = {.type = type, .code = code, .value = value};
+ return TEMP_FAILURE_RETRY(write(fd, &ev, sizeof(struct input_event))) == sizeof(ev);
+}
+
+static bool nativeWriteKeyEvent(JNIEnv* env, jobject thiz, jint fd, jint androidKeyCode,
+ jint action) {
+ auto keyCodeIterator = KEY_CODE_MAPPING.find(androidKeyCode);
+ if (keyCodeIterator == KEY_CODE_MAPPING.end()) {
+ ALOGE("No supportive native keycode for androidKeyCode %d", androidKeyCode);
+ return false;
+ }
+ auto actionIterator = KEY_ACTION_MAPPING.find(action);
+ if (actionIterator == KEY_ACTION_MAPPING.end()) {
+ return false;
+ }
+ if (!writeInputEvent(fd, EV_KEY, static_cast<uint16_t>(keyCodeIterator->second),
+ static_cast<int32_t>(actionIterator->second))) {
+ return false;
+ }
+ if (!writeInputEvent(fd, EV_SYN, SYN_REPORT, 0)) {
+ return false;
+ }
+ return true;
+}
+
+static bool nativeWriteButtonEvent(JNIEnv* env, jobject thiz, jint fd, jint buttonCode,
+ jint action) {
+ auto buttonCodeIterator = BUTTON_CODE_MAPPING.find(buttonCode);
+ if (buttonCodeIterator == BUTTON_CODE_MAPPING.end()) {
+ return false;
+ }
+ auto actionIterator = BUTTON_ACTION_MAPPING.find(action);
+ if (actionIterator == BUTTON_ACTION_MAPPING.end()) {
+ return false;
+ }
+ if (!writeInputEvent(fd, EV_KEY, static_cast<uint16_t>(buttonCodeIterator->second),
+ static_cast<int32_t>(actionIterator->second))) {
+ return false;
+ }
+ if (!writeInputEvent(fd, EV_SYN, SYN_REPORT, 0)) {
+ return false;
+ }
+ return true;
+}
+
+static bool nativeWriteTouchEvent(JNIEnv* env, jobject thiz, jint fd, jint pointerId, jint toolType,
+ jint action, jfloat locationX, jfloat locationY, jfloat pressure,
+ jfloat majorAxisSize) {
+ if (!writeInputEvent(fd, EV_ABS, ABS_MT_SLOT, pointerId)) {
+ return false;
+ }
+ auto toolTypeIterator = TOOL_TYPE_MAPPING.find(toolType);
+ if (toolTypeIterator == TOOL_TYPE_MAPPING.end()) {
+ return false;
+ }
+ if (toolType != -1) {
+ if (!writeInputEvent(fd, EV_ABS, ABS_MT_TOOL_TYPE,
+ static_cast<int32_t>(toolTypeIterator->second))) {
+ return false;
+ }
+ }
+ auto actionIterator = TOUCH_ACTION_MAPPING.find(action);
+ if (actionIterator == TOUCH_ACTION_MAPPING.end()) {
+ return false;
+ }
+ UinputAction uinputAction = actionIterator->second;
+ if (uinputAction == UinputAction::PRESS || uinputAction == UinputAction::RELEASE) {
+ if (!writeInputEvent(fd, EV_KEY, BTN_TOUCH, static_cast<int32_t>(uinputAction))) {
+ return false;
+ }
+ if (!writeInputEvent(fd, EV_ABS, ABS_MT_TRACKING_ID,
+ static_cast<int32_t>(uinputAction == UinputAction::PRESS ? pointerId
+ : -1))) {
+ return false;
+ }
+ }
+ if (!writeInputEvent(fd, EV_ABS, ABS_MT_POSITION_X, locationX)) {
+ return false;
+ }
+ if (!writeInputEvent(fd, EV_ABS, ABS_MT_POSITION_Y, locationY)) {
+ return false;
+ }
+ if (!isnan(pressure)) {
+ if (!writeInputEvent(fd, EV_ABS, ABS_MT_PRESSURE, pressure)) {
+ return false;
+ }
+ }
+ if (!isnan(majorAxisSize)) {
+ if (!writeInputEvent(fd, EV_ABS, ABS_MT_TOUCH_MAJOR, majorAxisSize)) {
+ return false;
+ }
+ }
+ return writeInputEvent(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static bool nativeWriteRelativeEvent(JNIEnv* env, jobject thiz, jint fd, jfloat relativeX,
+ jfloat relativeY) {
+ return writeInputEvent(fd, EV_REL, REL_X, relativeX) &&
+ writeInputEvent(fd, EV_REL, REL_Y, relativeY) &&
+ writeInputEvent(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static bool nativeWriteScrollEvent(JNIEnv* env, jobject thiz, jint fd, jfloat xAxisMovement,
+ jfloat yAxisMovement) {
+ return writeInputEvent(fd, EV_REL, REL_HWHEEL, xAxisMovement) &&
+ writeInputEvent(fd, EV_REL, REL_WHEEL, yAxisMovement) &&
+ writeInputEvent(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static JNINativeMethod methods[] = {
+ {"nativeOpenUinputKeyboard", "(Ljava/lang/String;II)I", (void*)nativeOpenUinputKeyboard},
+ {"nativeOpenUinputMouse", "(Ljava/lang/String;II)I", (void*)nativeOpenUinputMouse},
+ {"nativeOpenUinputTouchscreen", "(Ljava/lang/String;IIII)I",
+ (void*)nativeOpenUinputTouchscreen},
+ {"nativeCloseUinput", "(I)Z", (void*)nativeCloseUinput},
+ {"nativeWriteKeyEvent", "(III)Z", (void*)nativeWriteKeyEvent},
+ {"nativeWriteButtonEvent", "(III)Z", (void*)nativeWriteButtonEvent},
+ {"nativeWriteTouchEvent", "(IIIIFFFF)Z", (void*)nativeWriteTouchEvent},
+ {"nativeWriteRelativeEvent", "(IFF)Z", (void*)nativeWriteRelativeEvent},
+ {"nativeWriteScrollEvent", "(IFF)Z", (void*)nativeWriteScrollEvent},
+};
+
+int register_android_server_companion_virtual_InputController(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "com/android/server/companion/virtual/InputController",
+ methods, NELEM(methods));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index ff61abc..d339ef1 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -63,6 +63,7 @@
int register_android_server_GpuService(JNIEnv* env);
int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env);
int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env);
+int register_android_server_companion_virtual_InputController(JNIEnv* env);
};
using namespace android;
@@ -119,5 +120,6 @@
register_android_server_GpuService(env);
register_android_server_stats_pull_StatsPullAtomService(env);
register_android_server_sensor_SensorService(vm, env);
+ register_android_server_companion_virtual_InputController(env);
return JNI_VERSION_1_4;
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index 4a35fdf..d7e3195 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -24,17 +24,7 @@
import android.content.pm.PackageManager
import android.content.pm.SigningDetails
import android.content.pm.parsing.ParsingPackage
-import android.content.pm.parsing.component.ParsedActivityImpl
-import android.content.pm.parsing.component.ParsedAttributionImpl
-import android.content.pm.parsing.component.ParsedComponentImpl
-import android.content.pm.parsing.component.ParsedInstrumentationImpl
-import android.content.pm.parsing.component.ParsedIntentInfoImpl
-import android.content.pm.parsing.component.ParsedPermissionGroupImpl
-import android.content.pm.parsing.component.ParsedPermissionImpl
-import android.content.pm.parsing.component.ParsedProcessImpl
-import android.content.pm.parsing.component.ParsedProviderImpl
-import android.content.pm.parsing.component.ParsedServiceImpl
-import android.content.pm.parsing.component.ParsedUsesPermissionImpl
+import android.content.pm.parsing.component.*
import android.net.Uri
import android.os.Bundle
import android.os.Parcelable
@@ -360,6 +350,13 @@
transformSet = { ParsedActivityImpl().apply { name = it }.withMimeGroups() }
),
getSetByValue(
+ AndroidPackage::getApexSystemServices,
+ PackageImpl::addApexSystemService,
+ "TestApexSystemServiceName",
+ transformGet = { it.singleOrNull()?.name.orEmpty() },
+ transformSet = { ParsedApexSystemServiceImpl().apply { name = it } }
+ ),
+ getSetByValue(
AndroidPackage::getReceivers,
PackageImpl::addReceiver,
"TestReceiverName",
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index d1d7cc6..d9dbf48 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
+import android.content.pm.SigningDetails
import android.content.pm.parsing.component.ParsedActivityImpl
import android.content.pm.parsing.component.ParsedIntentInfoImpl
import android.content.pm.verify.domain.DomainVerificationManager
@@ -26,6 +27,7 @@
import android.os.Build
import android.os.Process
import android.util.ArraySet
+import android.util.IndentingPrintWriter
import android.util.SparseArray
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.pm.parsing.pkg.AndroidPackage
@@ -46,6 +48,7 @@
import org.mockito.Mockito.anyLong
import org.mockito.Mockito.anyString
import org.mockito.Mockito.eq
+import org.mockito.Mockito.mock
import org.mockito.Mockito.verifyNoMoreInteractions
import java.util.UUID
import java.util.concurrent.atomic.AtomicBoolean
@@ -204,6 +207,14 @@
service(Type.QUERENT, "getInfo") {
getDomainVerificationInfo(it.targetPackageName)
},
+ service(Type.QUERENT, "printState") {
+ printState(mock(IndentingPrintWriter::class.java), null, null)
+ },
+ service(Type.QUERENT, "printStateInternal") {
+ printState(mock(IndentingPrintWriter::class.java), null, null) {
+ mockPkgState(it, UUID.randomUUID())
+ }
+ },
service(Type.VERIFIER, "setStatus") {
setDomainVerificationStatus(
it.targetDomainSetId,
@@ -311,6 +322,7 @@
}
)
}
+ whenever(signingDetails) { SigningDetails.UNKNOWN }
}
fun mockPkgState(packageName: String, domainSetId: UUID) =
@@ -327,6 +339,7 @@
}
}
whenever(isSystem) { false }
+ whenever(signingDetails) { SigningDetails.UNKNOWN }
}
}
@@ -794,8 +807,12 @@
}
val valueAsInt = value as? Int
- if (valueAsInt != null && valueAsInt == DomainVerificationManager.STATUS_OK) {
- throw AssertionError("Expected call to return false, was $value")
+ if (valueAsInt != null) {
+ if (valueAsInt == DomainVerificationManager.STATUS_OK) {
+ throw AssertionError("Expected call to return false, was $value")
+ }
+ } else {
+ throw AssertionError("Expected call to fail")
}
} catch (e: SecurityException) {
} catch (e: PackageManager.NameNotFoundException) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
index 17d7c51..d6db1b2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
@@ -123,6 +123,8 @@
doNothing().when(mContextSpy).enforceCallingPermission(
eq(Manifest.permission.WRITE_COMMUNAL_STATE), anyString());
+ doNothing().when(mContextSpy).enforceCallingPermission(
+ eq(Manifest.permission.READ_COMMUNAL_STATE), anyString());
mService = new CommunalManagerService(mContextSpy);
mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
@@ -203,6 +205,18 @@
}
@Test
+ public void testIsCommunalMode_isTrue() throws RemoteException {
+ mBinder.setCommunalViewShowing(true);
+ assertThat(mBinder.isCommunalMode()).isTrue();
+ }
+
+ @Test
+ public void testIsCommunalMode_isFalse() throws RemoteException {
+ mBinder.setCommunalViewShowing(false);
+ assertThat(mBinder.isCommunalMode()).isFalse();
+ }
+
+ @Test
public void testIntercept_unlocked_communalOff_appNotEnabled_showWhenLockedOff() {
when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
mAInfo.flags = 0;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
index 4d6f49e..4eba219 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
@@ -90,6 +90,19 @@
}
@Test
+ public void testThrottle_lowInterval() {
+ ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(0).build();
+
+ mProvider.getController().setRequest(request);
+ mDelegateProvider.reportLocation(createLocationResult("test_provider", mRandom));
+ verify(mListener, times(1)).onReportLocation(any(LocationResult.class));
+
+ mInjector.getDeviceStationaryHelper().setStationary(true);
+ mInjector.getDeviceIdleHelper().setIdle(true);
+ verify(mListener, after(1500).times(2)).onReportLocation(any(LocationResult.class));
+ }
+
+ @Test
public void testThrottle_stationaryExit() {
ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
@@ -104,17 +117,16 @@
mInjector.getDeviceIdleHelper().setIdle(true);
verify(mDelegate).onSetRequest(ProviderRequest.EMPTY_REQUEST);
- verify(mListener, timeout(75).times(2)).onReportLocation(any(LocationResult.class));
- verify(mListener, timeout(75).times(3)).onReportLocation(any(LocationResult.class));
+ verify(mListener, timeout(1100).times(2)).onReportLocation(any(LocationResult.class));
mInjector.getDeviceStationaryHelper().setStationary(false);
verify(mDelegate, times(2)).onSetRequest(request);
- verify(mListener, after(75).times(3)).onReportLocation(any(LocationResult.class));
+ verify(mListener, after(1000).times(2)).onReportLocation(any(LocationResult.class));
}
@Test
public void testThrottle_idleExit() {
- ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
+ ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(1000).build();
mProvider.getController().setRequest(request);
verify(mDelegate).onSetRequest(request);
@@ -127,17 +139,16 @@
mInjector.getDeviceStationaryHelper().setStationary(true);
verify(mDelegate).onSetRequest(ProviderRequest.EMPTY_REQUEST);
- verify(mListener, timeout(75).times(2)).onReportLocation(any(LocationResult.class));
- verify(mListener, timeout(75).times(3)).onReportLocation(any(LocationResult.class));
+ verify(mListener, timeout(1100).times(2)).onReportLocation(any(LocationResult.class));
mInjector.getDeviceIdleHelper().setIdle(false);
verify(mDelegate, times(2)).onSetRequest(request);
- verify(mListener, after(75).times(3)).onReportLocation(any(LocationResult.class));
+ verify(mListener, after(1000).times(2)).onReportLocation(any(LocationResult.class));
}
@Test
public void testThrottle_NoInitialLocation() {
- ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
+ ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(1000).build();
mProvider.getController().setRequest(request);
verify(mDelegate).onSetRequest(request);
@@ -149,11 +160,11 @@
mDelegateProvider.reportLocation(createLocationResult("test_provider", mRandom));
verify(mListener, times(1)).onReportLocation(any(LocationResult.class));
verify(mDelegate, times(1)).onSetRequest(ProviderRequest.EMPTY_REQUEST);
- verify(mListener, timeout(75).times(2)).onReportLocation(any(LocationResult.class));
+ verify(mListener, timeout(1100).times(2)).onReportLocation(any(LocationResult.class));
mInjector.getDeviceStationaryHelper().setStationary(false);
verify(mDelegate, times(2)).onSetRequest(request);
- verify(mListener, after(75).times(2)).onReportLocation(any(LocationResult.class));
+ verify(mListener, after(1000).times(2)).onReportLocation(any(LocationResult.class));
}
@Test
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index e3c60fd..c3a364e 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -32,6 +32,7 @@
"services.appwidget",
"services.autofill",
"services.backup",
+ "services.companion",
"services.core",
"services.devicepolicy",
"services.net",
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 2eb9e34..a0d86c9 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -187,6 +187,30 @@
}
@Test
+ public void testGetEmergencyGesturePowerButtonCooldownPeriodMs_enabled() {
+ withEmergencyGesturePowerButtonCooldownPeriodMsValue(4000);
+ assertEquals(4000,
+ mGestureLauncherService.getEmergencyGesturePowerButtonCooldownPeriodMs(mContext,
+ FAKE_USER_ID));
+ }
+
+ @Test
+ public void testGetEmergencyGesturePowerButtonCooldownPeriodMs_disabled() {
+ withEmergencyGesturePowerButtonCooldownPeriodMsValue(0);
+ assertEquals(0,
+ mGestureLauncherService.getEmergencyGesturePowerButtonCooldownPeriodMs(mContext,
+ FAKE_USER_ID));
+ }
+
+ @Test
+ public void testGetEmergencyGesturePowerButtonCooldownPeriodMs_cappedAtMaximum() {
+ withEmergencyGesturePowerButtonCooldownPeriodMsValue(10000);
+ assertEquals(GestureLauncherService.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX,
+ mGestureLauncherService.getEmergencyGesturePowerButtonCooldownPeriodMs(mContext,
+ FAKE_USER_ID));
+ }
+
+ @Test
public void testHandleCameraLaunchGesture_userSetupComplete() {
withUserSetupCompleteValue(true);
@@ -645,6 +669,211 @@
}
@Test
+ public void testInterceptPowerKeyDown_triggerEmergency_singleTaps_cooldownTriggered() {
+ // Enable power button cooldown
+ withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
+ mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();
+
+ // Trigger emergency by tapping button 5 times
+ long eventTime = triggerEmergencyGesture();
+
+ // Add enough interval to reset consecutive tap count
+ long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
+ eventTime += interval;
+
+ // Subsequent single tap is intercepted, but should not trigger any gesture
+ KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertTrue(intercepted);
+ assertFalse(outLaunched.value);
+
+ // Add enough interval to reset consecutive tap count
+ interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
+ eventTime += interval;
+
+ // Another single tap should be the same (intercepted but should not trigger gesture)
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ interactive = true;
+ outLaunched = new MutableBoolean(true);
+ intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertTrue(intercepted);
+ assertFalse(outLaunched.value);
+ }
+
+ @Test
+ public void
+ testInterceptPowerKeyDown_triggerEmergency_cameraGestureEnabled_doubleTap_cooldownTriggered() {
+ // Enable camera double tap gesture
+ withCameraDoubleTapPowerEnableConfigValue(true);
+ withCameraDoubleTapPowerDisableSettingValue(0);
+ mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+
+ // Enable power button cooldown
+ withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
+ mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();
+
+ // Trigger emergency by tapping button 5 times
+ long eventTime = triggerEmergencyGesture();
+
+ // Add enough interval to reset consecutive tap count
+ long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
+ eventTime += interval;
+
+ // Subsequent double tap is intercepted, but should not trigger any gesture
+ for (int i = 0; i < 2; i++) {
+ KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION,
+ IGNORED_CODE, IGNORED_REPEAT);
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent,
+ interactive, outLaunched);
+ assertTrue(intercepted);
+ assertFalse(outLaunched.value);
+ interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ }
+ }
+
+ @Test
+ public void testInterceptPowerKeyDown_triggerEmergency_fiveTaps_cooldownTriggered() {
+ // Enable power button cooldown
+ withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
+ mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();
+
+ // Trigger emergency by tapping button 5 times
+ long eventTime = triggerEmergencyGesture();
+
+ // Add enough interval to reset consecutive tap count
+ long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
+ eventTime += interval;
+
+ // Subsequent 5 taps are intercepted, but should not trigger any gesture
+ for (int i = 0; i < 5; i++) {
+ KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION,
+ IGNORED_CODE, IGNORED_REPEAT);
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent,
+ interactive, outLaunched);
+ assertTrue(intercepted);
+ assertFalse(outLaunched.value);
+ interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ }
+ }
+
+ @Test
+ public void testInterceptPowerKeyDown_triggerEmergency_longPress_cooldownTriggered() {
+ // Enable power button cooldown
+ withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
+ mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();
+
+ // Trigger emergency by tapping button 5 times
+ long eventTime = triggerEmergencyGesture();
+
+ // Add enough interval to reset consecutive tap count
+ long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
+ eventTime += interval;
+
+ // Subsequent long press is intercepted, but should not trigger any gesture
+ KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE,
+ KeyEvent.FLAG_LONG_PRESS);
+
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertTrue(intercepted);
+ assertFalse(outLaunched.value);
+ }
+
+ @Test
+ public void testInterceptPowerKeyDown_triggerEmergency_cooldownDisabled_cooldownNotTriggered() {
+ // Disable power button cooldown by setting cooldown period to 0
+ withEmergencyGesturePowerButtonCooldownPeriodMsValue(0);
+ mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();
+
+ // Trigger emergency by tapping button 5 times
+ long eventTime = triggerEmergencyGesture();
+
+ // Add enough interval to reset consecutive tap count
+ long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
+ eventTime += interval;
+
+ // Subsequent single tap is NOT intercepted
+ KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertFalse(intercepted);
+ assertFalse(outLaunched.value);
+
+ // Add enough interval to reset consecutive tap count
+ interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
+ eventTime += interval;
+
+ // Long press also NOT intercepted
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE,
+ KeyEvent.FLAG_LONG_PRESS);
+ interactive = true;
+ outLaunched = new MutableBoolean(true);
+ intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertFalse(intercepted);
+ assertFalse(outLaunched.value);
+ }
+
+ @Test
+ public void
+ testInterceptPowerKeyDown_triggerEmergency_outsideCooldownPeriod_cooldownNotTriggered() {
+ // Enable power button cooldown
+ withEmergencyGesturePowerButtonCooldownPeriodMsValue(5000);
+ mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();
+
+ // Trigger emergency by tapping button 5 times
+ long eventTime = triggerEmergencyGesture();
+
+ // Add enough interval to be outside of cooldown period
+ long interval = 5001;
+ eventTime += interval;
+
+ // Subsequent single tap is NOT intercepted
+ KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertFalse(intercepted);
+ assertFalse(outLaunched.value);
+
+ // Add enough interval to reset consecutive tap count
+ interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
+ eventTime += interval;
+
+ // Long press also NOT intercepted
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE,
+ KeyEvent.FLAG_LONG_PRESS);
+ interactive = true;
+ outLaunched = new MutableBoolean(true);
+ intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertFalse(intercepted);
+ assertFalse(outLaunched.value);
+ }
+
+ @Test
public void testInterceptPowerKeyDown_longpress() {
withCameraDoubleTapPowerEnableConfigValue(true);
withCameraDoubleTapPowerDisableSettingValue(0);
@@ -1153,6 +1382,45 @@
assertEquals(1, tapCounts.get(1).intValue());
}
+ /**
+ * Helper method to trigger emergency gesture by pressing button for 5 times.
+ * @return last event time.
+ */
+ private long triggerEmergencyGesture() {
+ // Enable emergency power gesture
+ withEmergencyGestureEnabledConfigValue(true);
+ withEmergencyGestureEnabledSettingValue(true);
+ mGestureLauncherService.updateEmergencyGestureEnabled();
+ withUserSetupCompleteValue(true);
+
+ // 4 button presses
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ boolean interactive = true;
+ KeyEvent keyEvent;
+ MutableBoolean outLaunched = new MutableBoolean(false);
+ for (int i = 0; i < 4; i++) {
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, outLaunched);
+ final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ }
+
+ // 5th button press should trigger the emergency flow
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ outLaunched.value = false;
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertTrue(outLaunched.value);
+ assertTrue(intercepted);
+ verify(mUiEventLogger, times(1))
+ .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER);
+ verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected();
+
+ return eventTime;
+ }
+
private void withCameraDoubleTapPowerEnableConfigValue(boolean enableConfigValue) {
when(mResources.getBoolean(
com.android.internal.R.bool.config_cameraDoubleTapPowerGestureEnabled))
@@ -1181,6 +1449,14 @@
UserHandle.USER_CURRENT);
}
+ private void withEmergencyGesturePowerButtonCooldownPeriodMsValue(int period) {
+ Settings.Secure.putIntForUser(
+ mContentResolver,
+ Settings.Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
+ period,
+ UserHandle.USER_CURRENT);
+ }
+
private void withUserSetupCompleteValue(boolean userSetupComplete) {
int userSetupCompleteValue = userSetupComplete ? 1 : 0;
Settings.Secure.putIntForUser(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index f92b872..28c1c81 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -585,8 +585,8 @@
doAnswer((invocation) -> {
((Region) invocation.getArguments()[1]).set(region);
return null;
- }).when(mMockMagnificationProcessor).getMagnificationRegion(eq(displayId),
- any(), anyBoolean());
+ }).when(mMockMagnificationProcessor).getFullscreenMagnificationRegion(eq(displayId), any(),
+ anyBoolean());
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
final Region result = mServiceConnection.getMagnificationRegion(displayId);
@@ -620,7 +620,8 @@
@Test
public void resetMagnification() {
final int displayId = 1;
- when(mMockMagnificationProcessor.reset(displayId, true)).thenReturn(true);
+ when(mMockMagnificationProcessor.resetFullscreenMagnification(displayId, true)).thenReturn(
+ true);
final boolean result = mServiceConnection.resetMagnification(displayId, true);
assertThat(result, is(true));
@@ -629,7 +630,8 @@
@Test
public void resetMagnification_cantControlMagnification_returnFalse() {
final int displayId = 1;
- when(mMockMagnificationProcessor.reset(displayId, true)).thenReturn(true);
+ when(mMockMagnificationProcessor.resetFullscreenMagnification(displayId, true)).thenReturn(
+ true);
when(mMockSecurityPolicy.canControlMagnification(mServiceConnection)).thenReturn(false);
final boolean result = mServiceConnection.resetMagnification(displayId, true);
@@ -639,7 +641,8 @@
@Test
public void resetMagnification_serviceNotBelongCurrentUser_returnFalse() {
final int displayId = 1;
- when(mMockMagnificationProcessor.reset(displayId, true)).thenReturn(true);
+ when(mMockMagnificationProcessor.resetFullscreenMagnification(displayId, true)).thenReturn(
+ true);
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
final boolean result = mServiceConnection.resetMagnification(displayId, true);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
index 9ebec98..621507e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
@@ -98,7 +98,7 @@
.setScale(TEST_SCALE).build();
setMagnificationActivated(TEST_DISPLAY, config);
- float scale = mMagnificationProcessor.getScale(TEST_DISPLAY);
+ float scale = mMagnificationProcessor.getMagnificationConfig(TEST_DISPLAY).getScale();
assertEquals(scale, TEST_SCALE, 0);
}
@@ -111,20 +111,19 @@
setMagnificationActivated(TEST_DISPLAY, config);
float centerX = mMagnificationProcessor.getCenterX(
- TEST_DISPLAY, /* canControlMagnification= */true);
+ TEST_DISPLAY, /* canControlMagnification= */true);
assertEquals(centerX, TEST_CENTER_X, 0);
}
@Test
- public void getCenterX_canControlWindowMagnification_returnCenterX() {
+ public void getCenterX_controlWindowMagnification_returnCenterX() {
final MagnificationConfig config = new MagnificationConfig.Builder()
.setMode(MAGNIFICATION_MODE_WINDOW)
.setCenterX(TEST_CENTER_X).build();
setMagnificationActivated(TEST_DISPLAY, config);
- float centerX = mMagnificationProcessor.getCenterX(
- TEST_DISPLAY, /* canControlMagnification= */true);
+ float centerX = mMagnificationProcessor.getMagnificationConfig(TEST_DISPLAY).getCenterX();
assertEquals(centerX, TEST_CENTER_X, 0);
}
@@ -143,14 +142,13 @@
}
@Test
- public void getCenterY_canControlWindowMagnification_returnCenterY() {
+ public void getCenterY_controlWindowMagnification_returnCenterY() {
final MagnificationConfig config = new MagnificationConfig.Builder()
.setMode(MAGNIFICATION_MODE_WINDOW)
.setCenterY(TEST_CENTER_Y).build();
setMagnificationActivated(TEST_DISPLAY, config);
- float centerY = mMagnificationProcessor.getCenterY(
- TEST_DISPLAY, /* canControlMagnification= */false);
+ float centerY = mMagnificationProcessor.getMagnificationConfig(TEST_DISPLAY).getCenterY();
assertEquals(centerY, TEST_CENTER_Y, 0);
}
@@ -159,7 +157,7 @@
public void getMagnificationRegion_canControlFullscreenMagnification_returnRegion() {
final Region region = new Region(10, 20, 100, 200);
setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
- mMagnificationProcessor.getMagnificationRegion(TEST_DISPLAY,
+ mMagnificationProcessor.getFullscreenMagnificationRegion(TEST_DISPLAY,
region, /* canControlMagnification= */true);
verify(mMockFullScreenMagnificationController).getMagnificationRegion(eq(TEST_DISPLAY),
@@ -167,17 +165,6 @@
}
@Test
- public void getMagnificationRegion_canControlWindowMagnification_returnRegion() {
- final Region region = new Region(10, 20, 100, 200);
- setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_WINDOW);
- mMagnificationProcessor.getMagnificationRegion(TEST_DISPLAY,
- region, /* canControlMagnification= */true);
-
- verify(mMockWindowMagnificationManager).getMagnificationSourceBounds(eq(TEST_DISPLAY),
- eq(region));
- }
-
- @Test
public void getMagnificationRegion_fullscreenModeNotRegistered_shouldRegisterThenUnregister() {
final Region region = new Region(10, 20, 100, 200);
setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
@@ -188,7 +175,7 @@
any());
final Region result = new Region();
- mMagnificationProcessor.getMagnificationRegion(TEST_DISPLAY,
+ mMagnificationProcessor.getFullscreenMagnificationRegion(TEST_DISPLAY,
result, /* canControlMagnification= */true);
assertEquals(region, result);
verify(mMockFullScreenMagnificationController).register(TEST_DISPLAY);
@@ -237,7 +224,7 @@
public void reset_fullscreenMagnificationActivated() {
setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
- mMagnificationProcessor.reset(TEST_DISPLAY, /* animate= */false);
+ mMagnificationProcessor.resetFullscreenMagnification(TEST_DISPLAY, /* animate= */false);
verify(mMockFullScreenMagnificationController).reset(TEST_DISPLAY, false);
}
@@ -246,7 +233,7 @@
public void reset_windowMagnificationActivated() {
setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_WINDOW);
- mMagnificationProcessor.reset(TEST_DISPLAY, /* animate= */false);
+ mMagnificationProcessor.resetCurrentMagnification(TEST_DISPLAY, /* animate= */false);
verify(mMockWindowMagnificationManager).reset(TEST_DISPLAY);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
index 8b6b7c2..1d6ed03 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
@@ -296,14 +296,6 @@
}
@Test
- public void testToggleSplitScreen_legacy() {
- setupWithRealContext();
- mSystemActionPerformer.performSystemAction(
- AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
- verify(mMockStatusBarManagerInternal).toggleSplitScreen();
- }
-
- @Test
public void testScreenshot_requestsFromScreenshotHelper_legacy() {
setupWithMockContext();
mSystemActionPerformer.performSystemAction(
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/OWNERS b/services/tests/servicestests/src/com/android/server/biometrics/OWNERS
index 8765c9a..6a2192a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/biometrics/OWNERS
@@ -1,7 +1 @@
-set noparent
-
-kchyn@google.com
-jaggies@google.com
-curtislb@google.com
-ilyamaty@google.com
-joshmccloskey@google.com
+include /services/core/java/com/android/server/biometrics/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
new file mode 100644
index 0000000..c7c0756
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -0,0 +1,313 @@
+/*
+ * 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.
+ */
+
+package com.android.server.companion.virtual;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
+import static org.testng.Assert.assertThrows;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.graphics.Point;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.input.VirtualKeyEvent;
+import android.hardware.input.VirtualMouseButtonEvent;
+import android.hardware.input.VirtualMouseRelativeEvent;
+import android.hardware.input.VirtualMouseScrollEvent;
+import android.hardware.input.VirtualTouchEvent;
+import android.os.Binder;
+import android.platform.test.annotations.Presubmit;
+import android.view.KeyEvent;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class VirtualDeviceManagerServiceTest {
+
+ private static final String DEVICE_NAME = "device name";
+ private static final int DISPLAY_ID = 2;
+ private static final int PRODUCT_ID = 10;
+ private static final int VENDOR_ID = 5;
+ private static final int HEIGHT = 1800;
+ private static final int WIDTH = 900;
+ private static final Binder BINDER = new Binder("binder");
+
+ private Context mContext;
+ private VirtualDeviceImpl mDeviceImpl;
+ private InputController mInputController;
+ @Mock
+ private InputController.NativeWrapper mNativeWrapperMock;
+ @Mock
+ private DisplayManagerInternal mDisplayManagerInternalMock;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+ LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+
+ mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+ doNothing().when(mContext).enforceCallingOrSelfPermission(
+ eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
+ mInputController = new InputController(new Object(), mNativeWrapperMock);
+ mDeviceImpl = new VirtualDeviceImpl(mContext,
+ /* association info */ null, new Binder(), /* uid */ 0, mInputController,
+ (int associationId) -> {});
+ }
+
+ @Test
+ public void createVirtualKeyboard_noDisplay_failsSecurityException() {
+ assertThrows(
+ SecurityException.class,
+ () -> mDeviceImpl.createVirtualKeyboard(DISPLAY_ID, DEVICE_NAME, VENDOR_ID,
+ PRODUCT_ID, BINDER));
+ }
+
+ @Test
+ public void createVirtualMouse_noDisplay_failsSecurityException() {
+ assertThrows(
+ SecurityException.class,
+ () -> mDeviceImpl.createVirtualMouse(DISPLAY_ID, DEVICE_NAME, VENDOR_ID,
+ PRODUCT_ID, BINDER));
+ }
+
+ @Test
+ public void createVirtualTouchscreen_noDisplay_failsSecurityException() {
+ assertThrows(
+ SecurityException.class,
+ () -> mDeviceImpl.createVirtualTouchscreen(DISPLAY_ID, DEVICE_NAME,
+ VENDOR_ID, PRODUCT_ID, BINDER, new Point(WIDTH, HEIGHT)));
+ }
+
+ @Test
+ public void createVirtualKeyboard_noPermission_failsSecurityException() {
+ mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+ doCallRealMethod().when(mContext).enforceCallingOrSelfPermission(
+ eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
+ assertThrows(
+ SecurityException.class,
+ () -> mDeviceImpl.createVirtualKeyboard(DISPLAY_ID, DEVICE_NAME, VENDOR_ID,
+ PRODUCT_ID, BINDER));
+ }
+
+ @Test
+ public void createVirtualMouse_noPermission_failsSecurityException() {
+ mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+ doCallRealMethod().when(mContext).enforceCallingOrSelfPermission(
+ eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
+ assertThrows(
+ SecurityException.class,
+ () -> mDeviceImpl.createVirtualMouse(DISPLAY_ID, DEVICE_NAME, VENDOR_ID,
+ PRODUCT_ID, BINDER));
+ }
+
+ @Test
+ public void createVirtualTouchscreen_noPermission_failsSecurityException() {
+ mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+ doCallRealMethod().when(mContext).enforceCallingOrSelfPermission(
+ eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
+ assertThrows(
+ SecurityException.class,
+ () -> mDeviceImpl.createVirtualTouchscreen(DISPLAY_ID, DEVICE_NAME,
+ VENDOR_ID, PRODUCT_ID, BINDER, new Point(WIDTH, HEIGHT)));
+ }
+
+ @Test
+ public void createVirtualKeyboard_hasDisplay_obtainFileDescriptor() {
+ mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+ mDeviceImpl.createVirtualKeyboard(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID,
+ BINDER);
+ assertWithMessage("Virtual keyboard should register fd when the display matches")
+ .that(mInputController.mInputDeviceFds).isNotEmpty();
+ verify(mNativeWrapperMock).openUinputKeyboard(DEVICE_NAME, VENDOR_ID, PRODUCT_ID);
+ }
+
+ @Test
+ public void createVirtualMouse_hasDisplay_obtainFileDescriptor() {
+ mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+ mDeviceImpl.createVirtualMouse(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID,
+ BINDER);
+ assertWithMessage("Virtual keyboard should register fd when the display matches")
+ .that(mInputController.mInputDeviceFds).isNotEmpty();
+ verify(mNativeWrapperMock).openUinputMouse(DEVICE_NAME, VENDOR_ID, PRODUCT_ID);
+ }
+
+ @Test
+ public void createVirtualTouchscreen_hasDisplay_obtainFileDescriptor() {
+ mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+ mDeviceImpl.createVirtualTouchscreen(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID,
+ BINDER, new Point(WIDTH, HEIGHT));
+ assertWithMessage("Virtual keyboard should register fd when the display matches")
+ .that(mInputController.mInputDeviceFds).isNotEmpty();
+ verify(mNativeWrapperMock).openUinputTouchscreen(DEVICE_NAME, VENDOR_ID, PRODUCT_ID, HEIGHT,
+ WIDTH);
+ }
+
+ @Test
+ public void sendKeyEvent_noFd() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder()
+ .setKeyCode(KeyEvent.KEYCODE_A)
+ .setAction(VirtualKeyEvent.ACTION_DOWN).build()));
+ }
+
+ @Test
+ public void sendKeyEvent_hasFd_writesEvent() {
+ final int fd = 1;
+ final int keyCode = KeyEvent.KEYCODE_A;
+ final int action = VirtualKeyEvent.ACTION_UP;
+ mInputController.mInputDeviceFds.put(BINDER, fd);
+ mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder().setKeyCode(keyCode)
+ .setAction(action).build());
+ verify(mNativeWrapperMock).writeKeyEvent(fd, keyCode, action);
+ }
+
+ @Test
+ public void sendButtonEvent_noFd() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ mDeviceImpl.sendButtonEvent(BINDER,
+ new VirtualMouseButtonEvent.Builder()
+ .setButtonCode(VirtualMouseButtonEvent.BUTTON_BACK)
+ .setAction(VirtualMouseButtonEvent.ACTION_BUTTON_PRESS)
+ .build()));
+ }
+
+ @Test
+ public void sendButtonEvent_hasFd_writesEvent() {
+ final int fd = 1;
+ final int buttonCode = VirtualMouseButtonEvent.BUTTON_BACK;
+ final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
+ mInputController.mInputDeviceFds.put(BINDER, fd);
+ mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder()
+ .setButtonCode(buttonCode)
+ .setAction(action).build());
+ verify(mNativeWrapperMock).writeButtonEvent(fd, buttonCode, action);
+ }
+
+ @Test
+ public void sendRelativeEvent_noFd() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ mDeviceImpl.sendRelativeEvent(BINDER,
+ new VirtualMouseRelativeEvent.Builder().setRelativeX(
+ 0.0f).setRelativeY(0.0f).build()));
+ }
+
+ @Test
+ public void sendRelativeEvent_hasFd_writesEvent() {
+ final int fd = 1;
+ final float x = -0.2f;
+ final float y = 0.7f;
+ mInputController.mInputDeviceFds.put(BINDER, fd);
+ mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder()
+ .setRelativeX(x).setRelativeY(y).build());
+ verify(mNativeWrapperMock).writeRelativeEvent(fd, x, y);
+ }
+
+ @Test
+ public void sendScrollEvent_noFd() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ mDeviceImpl.sendScrollEvent(BINDER,
+ new VirtualMouseScrollEvent.Builder()
+ .setXAxisMovement(-1f)
+ .setYAxisMovement(1f).build()));
+ }
+
+ @Test
+ public void sendScrollEvent_hasFd_writesEvent() {
+ final int fd = 1;
+ final float x = 0.5f;
+ final float y = 1f;
+ mInputController.mInputDeviceFds.put(BINDER, fd);
+ mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder()
+ .setXAxisMovement(x)
+ .setYAxisMovement(y).build());
+ verify(mNativeWrapperMock).writeScrollEvent(fd, x, y);
+ }
+
+ @Test
+ public void sendTouchEvent_noFd() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder()
+ .setX(0.0f)
+ .setY(0.0f)
+ .setAction(VirtualTouchEvent.ACTION_UP)
+ .setPointerId(1)
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+ .build()));
+ }
+
+ @Test
+ public void sendTouchEvent_hasFd_writesEvent_withoutPressureOrMajorAxisSize() {
+ final int fd = 1;
+ final int pointerId = 5;
+ final int toolType = VirtualTouchEvent.TOOL_TYPE_FINGER;
+ final float x = 100.5f;
+ final float y = 200.5f;
+ final int action = VirtualTouchEvent.ACTION_UP;
+ mInputController.mInputDeviceFds.put(BINDER, fd);
+ mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x)
+ .setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType).build());
+ verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, Float.NaN,
+ Float.NaN);
+ }
+
+ @Test
+ public void sendTouchEvent_hasFd_writesEvent() {
+ final int fd = 1;
+ final int pointerId = 5;
+ final int toolType = VirtualTouchEvent.TOOL_TYPE_FINGER;
+ final float x = 100.5f;
+ final float y = 200.5f;
+ final int action = VirtualTouchEvent.ACTION_UP;
+ final float pressure = 1.0f;
+ final float majorAxisSize = 10.0f;
+ mInputController.mInputDeviceFds.put(BINDER, fd);
+ mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x)
+ .setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType)
+ .setPressure(pressure).setMajorAxisSize(majorAxisSize).build());
+ verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, pressure,
+ majorAxisSize);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index f664517..abe7d89 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -21,7 +21,9 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyFloat;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -63,6 +65,7 @@
@Mock SensorManager mSensorManager;
@Mock BrightnessMappingStrategy mBrightnessMappingStrategy;
+ @Mock BrightnessMappingStrategy mIdleBrightnessMappingStrategy;
@Mock HysteresisLevels mAmbientBrightnessThresholds;
@Mock HysteresisLevels mScreenBrightnessThresholds;
@Mock Handler mNoOpHandler;
@@ -100,7 +103,7 @@
INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
mAmbientBrightnessThresholds, mScreenBrightnessThresholds,
- mContext, mHbmController
+ mContext, mHbmController, mIdleBrightnessMappingStrategy
);
when(mHbmController.getCurrentBrightnessMax()).thenReturn(BRIGHTNESS_MAX_FLOAT);
@@ -231,4 +234,44 @@
// There should be a user data point added to the mapper.
verify(mBrightnessMappingStrategy).addUserDataPoint(1000f, 0.5f);
}
+
+ @Test
+ public void testSwitchToIdleMappingStrategy() throws Exception {
+ Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
+ mController = setupController(lightSensor);
+
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(lightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // Sensor reads 1000 lux,
+ listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000));
+
+ // User sets brightness to 100
+ mController.configure(true /* enable */, null /* configuration */,
+ 0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */,
+ false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
+
+ // There should be a user data point added to the mapper.
+ verify(mBrightnessMappingStrategy, times(1)).addUserDataPoint(1000f, 0.5f);
+ verify(mBrightnessMappingStrategy, times(2)).setBrightnessConfiguration(any());
+ verify(mBrightnessMappingStrategy, times(3)).getBrightness(anyFloat(), any(), anyInt());
+
+ // Now let's do the same for idle mode
+ mController.switchToIdleMode();
+ // Called once for init, and once when switching
+ verify(mBrightnessMappingStrategy, times(2)).isForIdleMode();
+ // Ensure, after switching, original BMS is not used anymore
+ verifyNoMoreInteractions(mBrightnessMappingStrategy);
+
+ // User sets idle brightness to 0.5
+ mController.configure(true /* enable */, null /* configuration */,
+ 0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */,
+ false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
+
+ // Ensure we use the correct mapping strategy
+ verify(mIdleBrightnessMappingStrategy, times(1)).addUserDataPoint(1000f, 0.5f);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 68e90fb..eaa271a 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -219,7 +219,7 @@
builder.setUniqueId(uniqueId);
builder.setFlags(flags);
int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
- null /* projection */, PACKAGE_NAME);
+ null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -341,7 +341,7 @@
builder.setFlags(flags);
builder.setUniqueId(uniqueId);
int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
- null /* projection */, PACKAGE_NAME);
+ null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -580,7 +580,8 @@
VIRTUAL_DISPLAY_NAME, width, height, dpi);
builder.setUniqueId(uniqueId);
final int firstDisplayId = binderService.createVirtualDisplay(builder.build(),
- mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+ mMockAppToken /* callback */, null /* projection */, null /* virtualDeviceToken */,
+ PACKAGE_NAME);
// The second virtual display requests to mirror the first virtual display.
final String uniqueId2 = "uniqueId --- displayIdToMirrorTest #2";
@@ -590,7 +591,8 @@
builder2.setUniqueId(uniqueId2);
builder2.setDisplayIdToMirror(firstDisplayId);
final int secondDisplayId = binderService.createVirtualDisplay(builder2.build(),
- mMockAppToken2 /* callback */, null /* projection */, PACKAGE_NAME);
+ mMockAppToken2 /* callback */, null /* projection */,
+ null /* virtualDeviceToken */, PACKAGE_NAME);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
// flush the handler
@@ -628,7 +630,8 @@
builder.setSurface(surface);
builder.setUniqueId(uniqueId);
final int displayId = binderService.createVirtualDisplay(builder.build(),
- mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+ mMockAppToken /* callback */, null /* projection */, null /* virtualDeviceToken */,
+ PACKAGE_NAME);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -1108,7 +1111,7 @@
builder.setUniqueId(uniqueId);
int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
- null /* projection */, PACKAGE_NAME);
+ null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId);
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
new file mode 100644
index 0000000..70e78eb
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
@@ -0,0 +1,740 @@
+/*
+ * 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.
+ */
+
+package com.android.server.locales;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.LocaleList;
+import android.os.RemoteException;
+import android.os.SimpleClock;
+import android.util.AtomicFile;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.util.XmlUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.time.Clock;
+import java.time.Duration;
+import java.time.ZoneOffset;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Unit tests for the {@link LocaleManagerInternal}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class LocaleManagerBackupRestoreTest {
+ private static final String DEFAULT_PACKAGE_NAME = "com.android.myapp";
+ private static final String DEFAULT_LOCALE_TAGS = "en-XC,ar-XB";
+ private static final String TEST_LOCALES_XML_TAG = "locales";
+ private static final int DEFAULT_USER_ID = 0;
+ private static final int WORK_PROFILE_USER_ID = 10;
+ private static final int DEFAULT_UID = Binder.getCallingUid() + 100;
+ private static final long DEFAULT_CREATION_TIME_MILLIS = 1000;
+ private static final Duration RETENTION_PERIOD = Duration.ofDays(3);
+ private static final LocaleList DEFAULT_LOCALES =
+ LocaleList.forLanguageTags(DEFAULT_LOCALE_TAGS);
+ private static final Map<String, String> DEFAULT_PACKAGE_LOCALES_MAP = Map.of(
+ DEFAULT_PACKAGE_NAME, DEFAULT_LOCALE_TAGS);
+ private static final File STAGED_LOCALES_DIR = new File(
+ Environment.getExternalStorageDirectory(), "lmsUnitTests");
+
+
+ private LocaleManagerBackupHelper mBackupHelper;
+ private long mCurrentTimeMillis;
+
+ @Mock
+ private Context mMockContext;
+ @Mock
+ private PackageManagerInternal mMockPackageManagerInternal;
+ @Mock
+ private PackageManager mMockPackageManager;
+ @Mock
+ private LocaleManagerService mMockLocaleManagerService;
+ BroadcastReceiver mUserMonitor;
+ PackageMonitor mPackageMonitor;
+
+ private final Clock mClock = new SimpleClock(ZoneOffset.UTC) {
+ @Override
+ public long millis() {
+ return currentTimeMillis();
+ }
+ };
+
+ private long currentTimeMillis() {
+ return mCurrentTimeMillis;
+ }
+
+ private void setCurrentTimeMillis(long currentTimeMillis) {
+ mCurrentTimeMillis = currentTimeMillis;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mMockContext = mock(Context.class);
+ mMockPackageManagerInternal = mock(PackageManagerInternal.class);
+ mMockPackageManager = mock(PackageManager.class);
+ mMockLocaleManagerService = mock(LocaleManagerService.class);
+
+ doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
+
+ mBackupHelper = spy(new ShadowLocaleManagerBackupHelper(mMockContext,
+ mMockLocaleManagerService, mMockPackageManagerInternal,
+ new File(Environment.getExternalStorageDirectory(), "lmsUnitTests"), mClock));
+ doNothing().when(mBackupHelper).notifyBackupManager();
+
+ mUserMonitor = mBackupHelper.getUserMonitor();
+ mPackageMonitor = mBackupHelper.getPackageMonitor();
+ setCurrentTimeMillis(DEFAULT_CREATION_TIME_MILLIS);
+ cleanStagedFiles();
+ }
+
+ @Test
+ public void testBackupPayload_noAppsInstalled_returnsNull() throws Exception {
+ doReturn(List.of()).when(mMockPackageManagerInternal)
+ .getInstalledApplications(anyLong(), anyInt(), anyInt());
+
+ assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
+ }
+
+ @Test
+ public void testBackupPayload_noAppLocalesSet_returnsNull() throws Exception {
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.getEmptyLocaleList());
+ setUpDummyAppForPackageManager(DEFAULT_PACKAGE_NAME);
+
+ assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
+ }
+
+ @Test
+ public void testBackupPayload_appLocalesSet_returnsNonNullBlob() throws Exception {
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, DEFAULT_LOCALES);
+ setUpDummyAppForPackageManager(DEFAULT_PACKAGE_NAME);
+
+ byte[] payload = mBackupHelper.getBackupPayload(DEFAULT_USER_ID);
+ verifyPayloadForAppLocales(DEFAULT_PACKAGE_LOCALES_MAP, payload);
+ }
+
+ @Test
+ public void testBackupPayload_exceptionInGetLocalesAllPackages_returnsNull() throws Exception {
+ setUpDummyAppForPackageManager(DEFAULT_PACKAGE_NAME);
+ doThrow(new RemoteException("mock")).when(mMockLocaleManagerService).getApplicationLocales(
+ anyString(), anyInt());
+
+ assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
+ }
+
+ @Test
+ public void testBackupPayload_exceptionInGetLocalesSomePackages_appsWithExceptionNotBackedUp()
+ throws Exception {
+ // Set up two apps.
+ ApplicationInfo defaultAppInfo = new ApplicationInfo();
+ ApplicationInfo anotherAppInfo = new ApplicationInfo();
+ defaultAppInfo.packageName = DEFAULT_PACKAGE_NAME;
+ anotherAppInfo.packageName = "com.android.anotherapp";
+ doReturn(List.of(defaultAppInfo, anotherAppInfo)).when(mMockPackageManagerInternal)
+ .getInstalledApplications(anyLong(), anyInt(), anyInt());
+
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, DEFAULT_LOCALES);
+ // Exception when getting locales for anotherApp.
+ doThrow(new RemoteException("mock")).when(mMockLocaleManagerService).getApplicationLocales(
+ eq(anotherAppInfo.packageName), anyInt());
+
+ byte[] payload = mBackupHelper.getBackupPayload(DEFAULT_USER_ID);
+ verifyPayloadForAppLocales(DEFAULT_PACKAGE_LOCALES_MAP, payload);
+ }
+
+ @Test
+ public void testRestore_nullPayload_nothingRestoredAndNoStageFile() throws Exception {
+ mBackupHelper.stageAndApplyRestoredPayload(/* payload= */ null, DEFAULT_USER_ID);
+
+ verifyNothingRestored();
+ checkStageFileDoesNotExist(DEFAULT_USER_ID);
+ }
+
+ @Test
+ public void testRestore_zeroLengthPayload_nothingRestoredAndNoStageFile() throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ mBackupHelper.stageAndApplyRestoredPayload(/* payload= */ out.toByteArray(),
+ DEFAULT_USER_ID);
+
+ verifyNothingRestored();
+ checkStageFileDoesNotExist(DEFAULT_USER_ID);
+ }
+
+ @Test
+ public void testRestore_allAppsInstalled_noStageFileCreated() throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
+
+ setUpPackageInstalled(DEFAULT_PACKAGE_NAME);
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.getEmptyLocaleList());
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ // Locales were restored
+ verify(mMockLocaleManagerService, times(1)).setApplicationLocales(DEFAULT_PACKAGE_NAME,
+ DEFAULT_USER_ID, DEFAULT_LOCALES);
+
+ // Stage file wasn't created.
+ checkStageFileDoesNotExist(DEFAULT_USER_ID);
+ }
+
+ @Test
+ public void testRestore_noAppsInstalled_everythingStaged() throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
+
+ setUpPackageNotInstalled(DEFAULT_PACKAGE_NAME);
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ verifyNothingRestored();
+ verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP,
+ getStageFileIfExists(DEFAULT_USER_ID), DEFAULT_CREATION_TIME_MILLIS);
+ }
+
+ @Test
+ public void testRestore_someAppsInstalled_partiallyStaged() throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ HashMap<String, String> pkgLocalesMap = new HashMap<>();
+
+ String pkgNameA = "com.android.myAppA", pkgNameB = "com.android.myAppB";
+ String langTagsA = "ru", langTagsB = "hi,fr";
+ pkgLocalesMap.put(pkgNameA, langTagsA);
+ pkgLocalesMap.put(pkgNameB, langTagsB);
+ writeTestPayload(out, pkgLocalesMap);
+
+ setUpPackageInstalled(pkgNameA);
+ setUpPackageNotInstalled(pkgNameB);
+ setUpLocalesForPackage(pkgNameA, LocaleList.getEmptyLocaleList());
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameA, DEFAULT_USER_ID,
+ LocaleList.forLanguageTags(langTagsA));
+
+ pkgLocalesMap.remove(pkgNameA);
+ verifyStageFileContent(pkgLocalesMap, getStageFileIfExists(DEFAULT_USER_ID),
+ DEFAULT_CREATION_TIME_MILLIS);
+ }
+
+ @Test
+ public void testRestore_appLocalesAlreadySet_nothingRestoredAndNoStageFile() throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
+
+ setUpPackageInstalled(DEFAULT_PACKAGE_NAME);
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.forLanguageTags("hi,mr"));
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ // Since locales are already set, we should not restore anything for it.
+ verifyNothingRestored();
+ // Stage file wasn't created
+ checkStageFileDoesNotExist(DEFAULT_USER_ID);
+ }
+
+ @Test
+ public void testRestore_appLocalesSetForSomeApps_restoresOnlyForAppsHavingNoLocalesSet()
+ throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ HashMap<String, String> pkgLocalesMap = new HashMap<>();
+
+ String pkgNameA = "com.android.myAppA", pkgNameB = "com.android.myAppB", pkgNameC =
+ "com.android.myAppC";
+ String langTagsA = "ru", langTagsB = "hi,fr", langTagsC = "zh,es";
+ pkgLocalesMap.put(pkgNameA, langTagsA);
+ pkgLocalesMap.put(pkgNameB, langTagsB);
+ pkgLocalesMap.put(pkgNameC, langTagsC);
+ writeTestPayload(out, pkgLocalesMap);
+
+ // Both app A & B are installed on the device but A has locales already set.
+ setUpPackageInstalled(pkgNameA);
+ setUpPackageInstalled(pkgNameB);
+ setUpPackageNotInstalled(pkgNameC);
+ setUpLocalesForPackage(pkgNameA, LocaleList.forLanguageTags("mr,fr"));
+ setUpLocalesForPackage(pkgNameB, LocaleList.getEmptyLocaleList());
+ setUpLocalesForPackage(pkgNameC, LocaleList.getEmptyLocaleList());
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ // Restore locales only for myAppB.
+ verify(mMockLocaleManagerService, times(0)).setApplicationLocales(eq(pkgNameA), anyInt(),
+ any());
+ verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameB, DEFAULT_USER_ID,
+ LocaleList.forLanguageTags(langTagsB));
+ verify(mMockLocaleManagerService, times(0)).setApplicationLocales(eq(pkgNameC), anyInt(),
+ any());
+
+ // App C is staged.
+ pkgLocalesMap.remove(pkgNameA);
+ pkgLocalesMap.remove(pkgNameB);
+ verifyStageFileContent(pkgLocalesMap, getStageFileIfExists(DEFAULT_USER_ID),
+ DEFAULT_CREATION_TIME_MILLIS);
+ }
+
+ @Test
+ public void testRestore_restoreInvokedAgain_creationTimeChanged() throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
+
+ setUpPackageNotInstalled(DEFAULT_PACKAGE_NAME);
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP, getStageFileIfExists(DEFAULT_USER_ID),
+ DEFAULT_CREATION_TIME_MILLIS);
+
+ final long newCreationTime = DEFAULT_CREATION_TIME_MILLIS + 100;
+ setCurrentTimeMillis(newCreationTime);
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP, getStageFileIfExists(DEFAULT_USER_ID),
+ newCreationTime);
+ }
+
+ @Test
+ public void testRestore_appInstalledAfterSUW_restoresFromStage() throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ HashMap<String, String> pkgLocalesMap = new HashMap<>();
+
+ String pkgNameA = "com.android.myAppA", pkgNameB = "com.android.myAppB";
+ String langTagsA = "ru", langTagsB = "hi,fr";
+ pkgLocalesMap.put(pkgNameA, langTagsA);
+ pkgLocalesMap.put(pkgNameB, langTagsB);
+ writeTestPayload(out, pkgLocalesMap);
+
+ setUpPackageNotInstalled(pkgNameA);
+ setUpPackageNotInstalled(pkgNameB);
+ setUpLocalesForPackage(pkgNameA, LocaleList.getEmptyLocaleList());
+ setUpLocalesForPackage(pkgNameB, LocaleList.getEmptyLocaleList());
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ verifyNothingRestored();
+
+ setUpPackageInstalled(pkgNameA);
+
+ mPackageMonitor.onPackageAdded(pkgNameA, DEFAULT_UID);
+
+ verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameA, DEFAULT_USER_ID,
+ LocaleList.forLanguageTags(langTagsA));
+
+ pkgLocalesMap.remove(pkgNameA);
+ verifyStageFileContent(pkgLocalesMap, getStageFileIfExists(DEFAULT_USER_ID),
+ DEFAULT_CREATION_TIME_MILLIS);
+
+ setUpPackageInstalled(pkgNameB);
+
+ mPackageMonitor.onPackageAdded(pkgNameB, DEFAULT_UID);
+
+ verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameB, DEFAULT_USER_ID,
+ LocaleList.forLanguageTags(langTagsB));
+ checkStageFileDoesNotExist(DEFAULT_USER_ID);
+ }
+
+ @Test
+ public void testRestore_appInstalledAfterSUWAndLocalesAlreadySet_restoresNothing()
+ throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
+
+ // Package is not present on the device when the SUW restore is going on.
+ setUpPackageNotInstalled(DEFAULT_PACKAGE_NAME);
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ verifyNothingRestored();
+ verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP, getStageFileIfExists(DEFAULT_USER_ID),
+ DEFAULT_CREATION_TIME_MILLIS);
+
+ // App is installed later (post SUW).
+ setUpPackageInstalled(DEFAULT_PACKAGE_NAME);
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.forLanguageTags("hi,mr"));
+
+ mPackageMonitor.onPackageAdded(DEFAULT_PACKAGE_NAME, DEFAULT_UID);
+
+ // Since locales are already set, we should not restore anything for it.
+ verifyNothingRestored();
+ checkStageFileDoesNotExist(DEFAULT_USER_ID);
+ }
+
+ @Test
+ public void testStageFileDeletion_backupPassRunAfterRetentionPeriod_stageFileDeleted()
+ throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP);
+
+ setUpPackageNotInstalled(DEFAULT_PACKAGE_NAME);
+
+ mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
+
+ verifyNothingRestored();
+ verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP, getStageFileIfExists(DEFAULT_USER_ID),
+ DEFAULT_CREATION_TIME_MILLIS);
+
+ // Retention period has not elapsed.
+ setCurrentTimeMillis(
+ DEFAULT_CREATION_TIME_MILLIS + RETENTION_PERIOD.minusHours(1).toMillis());
+ doReturn(List.of()).when(mMockPackageManagerInternal)
+ .getInstalledApplications(anyLong(), anyInt(), anyInt());
+ assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
+
+ // Stage file should NOT be deleted.
+ checkStageFileExists(DEFAULT_USER_ID);
+
+ // Exactly RETENTION_PERIOD amount of time has passed so stage file should still not be
+ // removed.
+ setCurrentTimeMillis(DEFAULT_CREATION_TIME_MILLIS + RETENTION_PERIOD.toMillis());
+ doReturn(List.of()).when(mMockPackageManagerInternal)
+ .getInstalledApplications(anyLong(), anyInt(), anyInt());
+ assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
+
+ // Stage file should NOT be deleted.
+ checkStageFileExists(DEFAULT_USER_ID);
+
+ // Retention period has now expired, stage file should be deleted.
+ setCurrentTimeMillis(
+ DEFAULT_CREATION_TIME_MILLIS + RETENTION_PERIOD.plusSeconds(1).toMillis());
+ doReturn(List.of()).when(mMockPackageManagerInternal)
+ .getInstalledApplications(anyLong(), anyInt(), anyInt());
+ assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
+
+ // Stage file should be deleted.
+ checkStageFileDoesNotExist(DEFAULT_USER_ID);
+ }
+
+ @Test
+ public void testUserRemoval_userRemoved_stageFileDeleted() throws Exception {
+ final ByteArrayOutputStream outDefault = new ByteArrayOutputStream();
+ writeTestPayload(outDefault, DEFAULT_PACKAGE_LOCALES_MAP);
+
+ final ByteArrayOutputStream outWorkProfile = new ByteArrayOutputStream();
+ String anotherPackage = "com.android.anotherapp";
+ String anotherLangTags = "mr,zh";
+ HashMap<String, String> pkgLocalesMapWorkProfile = new HashMap<>();
+ pkgLocalesMapWorkProfile.put(anotherPackage, anotherLangTags);
+ writeTestPayload(outWorkProfile, pkgLocalesMapWorkProfile);
+
+ // DEFAULT_PACKAGE_NAME is NOT installed on the device.
+ setUpPackageNotInstalled(DEFAULT_PACKAGE_NAME);
+ setUpPackageNotInstalled(anotherPackage);
+
+ mBackupHelper.stageAndApplyRestoredPayload(outDefault.toByteArray(), DEFAULT_USER_ID);
+ mBackupHelper.stageAndApplyRestoredPayload(outWorkProfile.toByteArray(),
+ WORK_PROFILE_USER_ID);
+
+ verifyNothingRestored();
+
+ // Verify stage file contents.
+ AtomicFile stageFileDefaultUser = getStageFileIfExists(DEFAULT_USER_ID);
+ verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP, stageFileDefaultUser,
+ DEFAULT_CREATION_TIME_MILLIS);
+
+ AtomicFile stageFileWorkProfile = getStageFileIfExists(WORK_PROFILE_USER_ID);
+ verifyStageFileContent(pkgLocalesMapWorkProfile, stageFileWorkProfile,
+ DEFAULT_CREATION_TIME_MILLIS);
+
+ Intent intent = new Intent();
+ intent.setAction(Intent.ACTION_USER_REMOVED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, DEFAULT_USER_ID);
+ mUserMonitor.onReceive(mMockContext, intent);
+
+ // Stage file should be removed only for DEFAULT_USER_ID.
+ checkStageFileDoesNotExist(DEFAULT_USER_ID);
+ verifyStageFileContent(pkgLocalesMapWorkProfile, stageFileWorkProfile,
+ DEFAULT_CREATION_TIME_MILLIS);
+ }
+
+ @Test
+ public void testLoadStageFiles_invalidNameFormat_stageFileDeleted() throws Exception {
+ // Stage file name should be : staged_locales_<user_id_int>.xml
+ File stageFile = new File(STAGED_LOCALES_DIR, "xyz.xml");
+ assertTrue(stageFile.createNewFile());
+ assertTrue(stageFile.isFile());
+
+ // Putting valid xml data in file.
+ FileOutputStream out = new FileOutputStream(stageFile);
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP, /* forStage= */
+ true, /* creationTimeMillis= */ 0);
+ out.flush();
+ out.close();
+
+ verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP,
+ new AtomicFile(stageFile), /* creationTimeMillis= */ 0);
+
+ mBackupHelper = new LocaleManagerBackupHelper(mMockContext, mMockLocaleManagerService,
+ mMockPackageManagerInternal, STAGED_LOCALES_DIR, mClock);
+ assertFalse(stageFile.isFile());
+ }
+
+ @Test
+ public void testLoadStageFiles_userIdNotParseable_stageFileDeleted() throws Exception {
+ // Stage file name should be : staged_locales_<user_id_int>.xml
+ File stageFile = new File(STAGED_LOCALES_DIR, "staged_locales_abc.xml");
+ assertTrue(stageFile.createNewFile());
+ assertTrue(stageFile.isFile());
+
+ // Putting valid xml data in file.
+ FileOutputStream out = new FileOutputStream(stageFile);
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP, /* forStage= */
+ true, /* creationTimeMillis= */ 0);
+ out.flush();
+ out.close();
+
+ verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP,
+ new AtomicFile(stageFile), /* creationTimeMillis= */ 0);
+
+ mBackupHelper = new LocaleManagerBackupHelper(mMockContext, mMockLocaleManagerService,
+ mMockPackageManagerInternal, STAGED_LOCALES_DIR, mClock);
+ assertFalse(stageFile.isFile());
+ }
+
+ @Test
+ public void testLoadStageFiles_invalidContent_stageFileDeleted() throws Exception {
+ File stageFile = new File(STAGED_LOCALES_DIR, "staged_locales_0.xml");
+ assertTrue(stageFile.createNewFile());
+ assertTrue(stageFile.isFile());
+
+ FileOutputStream out = new FileOutputStream(stageFile);
+ out.write("some_non_xml_string".getBytes());
+ out.close();
+
+ mBackupHelper = new LocaleManagerBackupHelper(mMockContext, mMockLocaleManagerService,
+ mMockPackageManagerInternal, STAGED_LOCALES_DIR, mClock);
+ assertFalse(stageFile.isFile());
+ }
+
+ @Test
+ public void testLoadStageFiles_validContent_doesLazyRestore() throws Exception {
+ File stageFile = new File(STAGED_LOCALES_DIR, "staged_locales_0.xml");
+ assertTrue(stageFile.createNewFile());
+ assertTrue(stageFile.isFile());
+
+ // Putting valid xml data in file.
+ FileOutputStream out = new FileOutputStream(stageFile);
+ writeTestPayload(out, DEFAULT_PACKAGE_LOCALES_MAP, /* forStage= */
+ true, DEFAULT_CREATION_TIME_MILLIS);
+ out.flush();
+ out.close();
+
+ verifyStageFileContent(DEFAULT_PACKAGE_LOCALES_MAP,
+ new AtomicFile(stageFile), DEFAULT_CREATION_TIME_MILLIS);
+
+ mBackupHelper = new LocaleManagerBackupHelper(mMockContext, mMockLocaleManagerService,
+ mMockPackageManagerInternal, STAGED_LOCALES_DIR, mClock);
+ mPackageMonitor = mBackupHelper.getPackageMonitor();
+
+ // Stage file still exists.
+ assertTrue(stageFile.isFile());
+
+ // App is installed later.
+ setUpPackageInstalled(DEFAULT_PACKAGE_NAME);
+ setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.getEmptyLocaleList());
+
+ mPackageMonitor.onPackageAdded(DEFAULT_PACKAGE_NAME, DEFAULT_UID);
+
+ verify(mMockLocaleManagerService, times(1)).setApplicationLocales(DEFAULT_PACKAGE_NAME,
+ DEFAULT_USER_ID, DEFAULT_LOCALES);
+
+ // Stage file gets deleted here because all staged locales have been applied.
+ assertFalse(stageFile.isFile());
+ }
+
+ private void setUpPackageInstalled(String packageName) throws Exception {
+ doReturn(new PackageInfo()).when(mMockPackageManager).getPackageInfoAsUser(
+ eq(packageName), anyInt(), anyInt());
+ }
+
+ private void setUpPackageNotInstalled(String packageName) throws Exception {
+ doReturn(null).when(mMockPackageManager).getPackageInfoAsUser(eq(packageName),
+ anyInt(), anyInt());
+ }
+
+ private void setUpLocalesForPackage(String packageName, LocaleList locales) throws Exception {
+ doReturn(locales).when(mMockLocaleManagerService).getApplicationLocales(
+ eq(packageName), anyInt());
+ }
+
+ private void setUpDummyAppForPackageManager(String packageName) {
+ ApplicationInfo dummyApp = new ApplicationInfo();
+ dummyApp.packageName = packageName;
+ doReturn(List.of(dummyApp)).when(mMockPackageManagerInternal)
+ .getInstalledApplications(anyLong(), anyInt(), anyInt());
+ }
+
+ /**
+ * Verifies that nothing was restored for any package.
+ *
+ * <p>If {@link LocaleManagerService#setApplicationLocales} is not invoked, we can conclude
+ * that nothing was restored.
+ */
+ private void verifyNothingRestored() throws Exception {
+ verify(mMockLocaleManagerService, times(0)).setApplicationLocales(anyString(), anyInt(),
+ any());
+ }
+
+
+ private static void verifyPayloadForAppLocales(Map<String, String> expectedPkgLocalesMap,
+ byte[] payload)
+ throws IOException, XmlPullParserException {
+ verifyPayloadForAppLocales(expectedPkgLocalesMap, payload, /* forStage= */ false, -1);
+ }
+
+ private static void verifyPayloadForAppLocales(Map<String, String> expectedPkgLocalesMap,
+ byte[] payload, boolean forStage, long expectedCreationTime)
+ throws IOException, XmlPullParserException {
+ final ByteArrayInputStream stream = new ByteArrayInputStream(payload);
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(stream, StandardCharsets.UTF_8.name());
+
+ Map<String, String> backupDataMap = new HashMap<>();
+ XmlUtils.beginDocument(parser, TEST_LOCALES_XML_TAG);
+ if (forStage) {
+ long actualCreationTime = parser.getAttributeLong(/* namespace= */ null,
+ "creationTimeMillis");
+ assertEquals(expectedCreationTime, actualCreationTime);
+ }
+ int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (parser.getName().equals("package")) {
+ String packageName = parser.getAttributeValue(null, "name");
+ String languageTags = parser.getAttributeValue(null, "locales");
+ backupDataMap.put(packageName, languageTags);
+ }
+ }
+
+ assertEquals(expectedPkgLocalesMap, backupDataMap);
+ }
+
+ private static void writeTestPayload(OutputStream stream, Map<String, String> pkgLocalesMap)
+ throws IOException {
+ writeTestPayload(stream, pkgLocalesMap, /* forStage= */ false, /* creationTimeMillis= */
+ -1);
+ }
+
+ private static void writeTestPayload(OutputStream stream, Map<String, String> pkgLocalesMap,
+ boolean forStage, long creationTimeMillis)
+ throws IOException {
+ if (pkgLocalesMap.isEmpty()) {
+ return;
+ }
+
+ TypedXmlSerializer out = Xml.newFastSerializer();
+ out.setOutput(stream, StandardCharsets.UTF_8.name());
+ out.startDocument(/* encoding= */ null, /* standalone= */ true);
+ out.startTag(/* namespace= */ null, TEST_LOCALES_XML_TAG);
+
+ if (forStage) {
+ out.attribute(/* namespace= */ null, "creationTimeMillis",
+ Long.toString(creationTimeMillis));
+ }
+
+ for (String pkg : pkgLocalesMap.keySet()) {
+ out.startTag(/* namespace= */ null, "package");
+ out.attribute(/* namespace= */ null, "name", pkg);
+ out.attribute(/* namespace= */ null, "locales", pkgLocalesMap.get(pkg));
+ out.endTag(/*namespace= */ null, "package");
+ }
+
+ out.endTag(/* namespace= */ null, TEST_LOCALES_XML_TAG);
+ out.endDocument();
+ }
+
+ private static void verifyStageFileContent(Map<String, String> expectedPkgLocalesMap,
+ AtomicFile stageFile,
+ long creationTimeMillis)
+ throws Exception {
+ assertNotNull(stageFile);
+ try (InputStream stagedDataInputStream = stageFile.openRead()) {
+ verifyPayloadForAppLocales(expectedPkgLocalesMap, stagedDataInputStream.readAllBytes(),
+ /* forStage= */ true, creationTimeMillis);
+ } catch (IOException | XmlPullParserException e) {
+ throw e;
+ }
+ }
+
+ private static void checkStageFileDoesNotExist(int userId) {
+ assertNull(getStageFileIfExists(userId));
+ }
+
+ private static void checkStageFileExists(int userId) {
+ assertNotNull(getStageFileIfExists(userId));
+ }
+
+ private static AtomicFile getStageFileIfExists(int userId) {
+ File file = new File(STAGED_LOCALES_DIR, String.format("staged_locales_%d.xml", userId));
+ if (file.isFile()) {
+ return new AtomicFile(file);
+ }
+ return null;
+ }
+
+ private static void cleanStagedFiles() {
+ File[] files = STAGED_LOCALES_DIR.listFiles();
+ if (files != null) {
+ for (File f : files) {
+ f.delete();
+ }
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
index ddc58b2..658f8d5 100644
--- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
@@ -30,6 +30,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.Manifest;
@@ -60,7 +61,7 @@
private static final int DEFAULT_USER_ID = 0;
private static final int DEFAULT_UID = Binder.getCallingUid() + 100;
private static final int INVALID_UID = -1;
- private static final String DEFAULT_LOCALE_TAGS = "en-XC, ar-XB";
+ private static final String DEFAULT_LOCALE_TAGS = "en-XC,ar-XB";
private static final LocaleList DEFAULT_LOCALES =
LocaleList.forLanguageTags(DEFAULT_LOCALE_TAGS);
private static final InstallSourceInfo DEFAULT_INSTALL_SOURCE_INFO = new InstallSourceInfo(
@@ -68,6 +69,7 @@
/* originatingPackageName = */ null, /* installingPackageName = */ null);
private LocaleManagerService mLocaleManagerService;
+ private LocaleManagerBackupHelper mMockBackupHelper;
@Mock
private Context mMockContext;
@@ -104,8 +106,9 @@
.handleIncomingUser(anyInt(), anyInt(), eq(DEFAULT_USER_ID), anyBoolean(), anyInt(),
anyString(), anyString());
+ mMockBackupHelper = mock(ShadowLocaleManagerBackupHelper.class);
mLocaleManagerService = new LocaleManagerService(mMockContext, mMockActivityTaskManager,
- mMockActivityManager, mMockPackageManagerInternal);
+ mMockActivityManager, mMockPackageManagerInternal, mMockBackupHelper);
}
@Test(expected = SecurityException.class)
@@ -122,6 +125,7 @@
verify(mMockContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.CHANGE_CONFIGURATION),
anyString());
+ verify(mMockBackupHelper, times(0)).notifyBackupManager();
assertNoLocalesStored(mFakePackageConfigurationUpdater.getStoredLocales());
}
}
@@ -133,6 +137,7 @@
DEFAULT_USER_ID, LocaleList.getEmptyLocaleList());
fail("Expected NullPointerException");
} finally {
+ verify(mMockBackupHelper, times(0)).notifyBackupManager();
assertNoLocalesStored(mFakePackageConfigurationUpdater.getStoredLocales());
}
}
@@ -146,6 +151,7 @@
/* locales = */ null);
fail("Expected NullPointerException");
} finally {
+ verify(mMockBackupHelper, times(0)).notifyBackupManager();
assertNoLocalesStored(mFakePackageConfigurationUpdater.getStoredLocales());
}
}
@@ -163,6 +169,7 @@
DEFAULT_LOCALES);
assertEquals(DEFAULT_LOCALES, mFakePackageConfigurationUpdater.getStoredLocales());
+ verify(mMockBackupHelper, times(1)).notifyBackupManager();
}
@@ -175,6 +182,7 @@
DEFAULT_LOCALES);
assertEquals(DEFAULT_LOCALES, mFakePackageConfigurationUpdater.getStoredLocales());
+ verify(mMockBackupHelper, times(1)).notifyBackupManager();
}
@Test(expected = IllegalArgumentException.class)
@@ -187,6 +195,7 @@
fail("Expected IllegalArgumentException");
} finally {
assertNoLocalesStored(mFakePackageConfigurationUpdater.getStoredLocales());
+ verify(mMockBackupHelper, times(0)).notifyBackupManager();
}
}
@@ -217,7 +226,7 @@
.when(mMockActivityTaskManager).getApplicationConfig(anyString(), anyInt());
LocaleList locales = mLocaleManagerService.getApplicationLocales(
- DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
assertEquals(LocaleList.getEmptyLocaleList(), locales);
}
diff --git a/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
new file mode 100644
index 0000000..93972c3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package com.android.server.locales;
+
+import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+
+import java.io.File;
+import java.time.Clock;
+
+/**
+ * Shadow for {@link LocaleManagerBackupHelper} to enable mocking it for tests.
+ *
+ * <p>{@link LocaleManagerBackupHelper} is a package private class and hence not mockable directly.
+ */
+public class ShadowLocaleManagerBackupHelper extends LocaleManagerBackupHelper {
+ ShadowLocaleManagerBackupHelper(Context context,
+ LocaleManagerService localeManagerService,
+ PackageManagerInternal pmInternal, File stagedLocalesDir, Clock clock) {
+ super(context, localeManagerService, pmInternal, stagedLocalesDir, clock);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 3722ba4..58c9db7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -579,7 +579,7 @@
}
@Override
- public void verifyCallingPackage(String callingPackage) {
+ public void verifyCallingPackage(String callingPackage, int callerUid) {
// SKIP
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 11bac45..c888524 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -46,6 +46,7 @@
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedActivityImpl;
+import android.content.pm.parsing.component.ParsedApexSystemService;
import android.content.pm.parsing.component.ParsedComponent;
import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedInstrumentationImpl;
@@ -526,6 +527,23 @@
}
@Test
+ public void testParseApexSystemService() throws Exception {
+ final File testFile = extractFile(TEST_APP4_APK);
+ try {
+ final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false);
+ final List<ParsedApexSystemService> systemServices = pkg.getApexSystemServices();
+ for (ParsedApexSystemService systemService: systemServices) {
+ assertEquals(PACKAGE_NAME + ".SystemService", systemService.getName());
+ assertEquals("service-test.jar", systemService.getJarPath());
+ assertEquals("30", systemService.getMinSdkVersion());
+ assertEquals("31", systemService.getMaxSdkVersion());
+ }
+ } finally {
+ testFile.delete();
+ }
+ }
+
+ @Test
public void testParseModernPackageHasNoCompatPermissions() throws Exception {
final File testFile = extractFile(TEST_APP1_APK);
try {
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
index 299b9a0..70fd28d 100644
--- a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
+++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
@@ -57,6 +57,11 @@
<property android:name="android.cts.PROPERTY_SERVICE" android:value="@integer/integer_property" />
<property android:name="android.cts.PROPERTY_COMPONENT" android:resource="@integer/integer_property" />
</service>
+ <apex-system-service
+ android:name="com.android.servicestests.apps.packageparserapp.SystemService"
+ android:path="service-test.jar"
+ android:minSdkVersion = "30"
+ android:maxSdkVersion = "31" />
</application>
<instrumentation
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 c85e876..e8a27990 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -31,6 +31,7 @@
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.media.AudioAttributes.CONTENT_TYPE_SONIFICATION;
import static android.media.AudioAttributes.USAGE_NOTIFICATION;
+import static android.os.UserHandle.USER_SYSTEM;
import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
@@ -545,14 +546,14 @@
mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_NONE);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true,
- UserHandle.USER_SYSTEM, channel1.getId(), channel2.getId(), channel3.getId(),
+ USER_SYSTEM, channel1.getId(), channel2.getId(), channel3.getId(),
NotificationChannel.DEFAULT_CHANNEL_ID);
mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_N_MR1, PKG_O},
new int[]{UID_N_MR1, UID_O});
mHelper.setShowBadge(PKG_O, UID_O, true);
- loadStreamXml(baos, true, UserHandle.USER_SYSTEM);
+ loadStreamXml(baos, true, USER_SYSTEM);
assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG_O, UID_O));
assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
@@ -597,17 +598,22 @@
mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
String xml = "<ranking version=\"2\">\n"
- + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
+ + "<package name=\"" + PKG_N_MR1 + "\" uid=\"" + UID_N_MR1
+ + "\" show_badge=\"true\">\n"
+ "<channel id=\"idn\" name=\"name\" importance=\"2\" />\n"
+ "<channel id=\"miscellaneous\" name=\"Uncategorized\" />\n"
+ "</package>\n"
- + "<package name=\"" + PKG_O + "\" importance=\"0\">\n"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" importance=\"0\">\n"
+ "<channel id=\"ido\" name=\"name2\" importance=\"2\" show_badge=\"true\"/>\n"
+ "</package>\n"
- + "<package name=\"" + PKG_P + "\" importance=\"2\">\n"
+ + "<package name=\"" + PKG_P + "\" uid=\"" + UID_P + "\" importance=\"2\">\n"
+ "<channel id=\"idp\" name=\"name3\" importance=\"4\" locked=\"2\" />\n"
+ "</package>\n"
+ "</ranking>\n";
+
+ loadByteArrayXml(xml.getBytes(), false, USER_SYSTEM);
+
+ // expected values
NotificationChannel idn = new NotificationChannel("idn", "name", IMPORTANCE_LOW);
idn.setSound(null, new AudioAttributes.Builder()
.setUsage(USAGE_NOTIFICATION)
@@ -637,8 +643,7 @@
// Notifications enabled, user set b/c channel modified
PackagePermission pExpected = new PackagePermission(PKG_P, 0, true, true);
- loadByteArrayXml(xml.getBytes(), true, UserHandle.USER_SYSTEM);
-
+ // verify data
assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
assertEquals(idn, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, idn.getId(), false));
@@ -651,6 +656,85 @@
}
@Test
+ public void testReadXml_oldXml_backup_migratesWhenPkgInstalled() throws Exception {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+
+ when(mPm.getPackageUidAsUser("pkg1", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
+ when(mPm.getPackageUidAsUser("pkg2", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
+ when(mPm.getPackageUidAsUser("pkg3", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
+ when(mPm.getApplicationInfoAsUser(eq("pkg1"), anyInt(), anyInt())).thenThrow(
+ new PackageManager.NameNotFoundException());
+ when(mPm.getApplicationInfoAsUser(eq("pkg2"), anyInt(), anyInt())).thenThrow(
+ new PackageManager.NameNotFoundException());
+ when(mPm.getApplicationInfoAsUser(eq("pkg3"), anyInt(), anyInt())).thenThrow(
+ new PackageManager.NameNotFoundException());
+
+ String xml = "<ranking version=\"2\">\n"
+ + "<package name=\"pkg1\" show_badge=\"true\">\n"
+ + "<channel id=\"idn\" name=\"name\" importance=\"2\" />\n"
+ + "<channel id=\"miscellaneous\" name=\"Uncategorized\" />\n"
+ + "</package>\n"
+ + "<package name=\"pkg2\" importance=\"0\">\n"
+ + "<channel id=\"ido\" name=\"name2\" importance=\"2\" show_badge=\"true\"/>\n"
+ + "</package>\n"
+ + "<package name=\"pkg3\" importance=\"2\">\n"
+ + "<channel id=\"idp\" name=\"name3\" importance=\"4\" locked=\"2\" />\n"
+ + "</package>\n"
+ + "</ranking>\n";
+ NotificationChannel idn = new NotificationChannel("idn", "name", IMPORTANCE_LOW);
+ idn.setSound(null, new AudioAttributes.Builder()
+ .setUsage(USAGE_NOTIFICATION)
+ .setContentType(CONTENT_TYPE_SONIFICATION)
+ .setFlags(0)
+ .build());
+ idn.setShowBadge(false);
+ NotificationChannel ido = new NotificationChannel("ido", "name2", IMPORTANCE_LOW);
+ ido.setShowBadge(true);
+ ido.setSound(null, new AudioAttributes.Builder()
+ .setUsage(USAGE_NOTIFICATION)
+ .setContentType(CONTENT_TYPE_SONIFICATION)
+ .setFlags(0)
+ .build());
+ NotificationChannel idp = new NotificationChannel("idp", "name3", IMPORTANCE_HIGH);
+ idp.lockFields(2);
+ idp.setSound(null, new AudioAttributes.Builder()
+ .setUsage(USAGE_NOTIFICATION)
+ .setContentType(CONTENT_TYPE_SONIFICATION)
+ .setFlags(0)
+ .build());
+
+ // Notifications enabled, not user set
+ PackagePermission pkg1Expected = new PackagePermission("pkg1", 0, true, false);
+ // Notifications not enabled, so user set
+ PackagePermission pkg2Expected = new PackagePermission("pkg2", 0, false, true);
+ // Notifications enabled, user set b/c channel modified
+ PackagePermission pkg3Expected = new PackagePermission("pkg3", 0, true, true);
+
+ loadByteArrayXml(xml.getBytes(), true, USER_SYSTEM);
+
+ verify(mPermissionHelper, never()).setNotificationPermission(any());
+
+ when(mPm.getPackageUidAsUser("pkg1", USER_SYSTEM)).thenReturn(11);
+ when(mPm.getPackageUidAsUser("pkg2", USER_SYSTEM)).thenReturn(12);
+ when(mPm.getPackageUidAsUser("pkg3", USER_SYSTEM)).thenReturn(13);
+
+ mHelper.onPackagesChanged(
+ false, 0, new String[]{"pkg1", "pkg2", "pkg3"}, new int[] {11, 12, 13});
+
+ assertTrue(mHelper.canShowBadge("pkg1", 11));
+
+ assertEquals(idn, mHelper.getNotificationChannel("pkg1", 11, idn.getId(), false));
+ compareChannels(ido, mHelper.getNotificationChannel("pkg2", 12, ido.getId(), false));
+ compareChannels(idp, mHelper.getNotificationChannel("pkg3", 13, idp.getId(), false));
+
+ verify(mPermissionHelper).setNotificationPermission(pkg1Expected);
+ verify(mPermissionHelper).setNotificationPermission(pkg2Expected);
+ verify(mPermissionHelper).setNotificationPermission(pkg3Expected);
+ }
+
+ @Test
public void testReadXml_newXml_noMigration() throws Exception {
when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
@@ -690,7 +774,7 @@
.setFlags(0)
.build());
- loadByteArrayXml(xml.getBytes(), true, UserHandle.USER_SYSTEM);
+ loadByteArrayXml(xml.getBytes(), true, USER_SYSTEM);
assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
@@ -714,7 +798,7 @@
appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false));
appPermissions.put(new Pair(UID_N_MR1, PKG_N_MR1), new Pair(true, false));
- when(mPermissionHelper.getNotificationPermissionValues(UserHandle.USER_SYSTEM))
+ when(mPermissionHelper.getNotificationPermissionValues(USER_SYSTEM))
.thenReturn(appPermissions);
NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
@@ -748,7 +832,7 @@
mHelper.setInvalidMsgAppDemoted(PKG_P, UID_P, true);
ByteArrayOutputStream baos = writeXmlAndPurge(
- PKG_N_MR1, UID_N_MR1, false, UserHandle.USER_SYSTEM);
+ PKG_N_MR1, UID_N_MR1, false, USER_SYSTEM);
String expected = "<ranking version=\"3\">\n"
+ "<package name=\"com.example.o\" show_badge=\"true\" "
+ "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
@@ -795,7 +879,7 @@
appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false));
appPermissions.put(new Pair(UID_N_MR1, PKG_N_MR1), new Pair(true, false));
- when(mPermissionHelper.getNotificationPermissionValues(UserHandle.USER_SYSTEM))
+ when(mPermissionHelper.getNotificationPermissionValues(USER_SYSTEM))
.thenReturn(appPermissions);
NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
@@ -829,7 +913,7 @@
mHelper.setInvalidMsgAppDemoted(PKG_P, UID_P, true);
ByteArrayOutputStream baos = writeXmlAndPurge(
- PKG_N_MR1, UID_N_MR1, true, UserHandle.USER_SYSTEM);
+ PKG_N_MR1, UID_N_MR1, true, USER_SYSTEM);
String expected = "<ranking version=\"3\">\n"
// Importance 0 because off in permissionhelper
+ "<package name=\"com.example.o\" importance=\"0\" show_badge=\"true\" "
@@ -878,7 +962,7 @@
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair(UID_P, PKG_P), new Pair(true, false));
appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false));
- when(mPermissionHelper.getNotificationPermissionValues(UserHandle.USER_SYSTEM))
+ when(mPermissionHelper.getNotificationPermissionValues(USER_SYSTEM))
.thenReturn(appPermissions);
NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
@@ -912,7 +996,7 @@
mHelper.setInvalidMsgAppDemoted(PKG_P, UID_P, true);
ByteArrayOutputStream baos = writeXmlAndPurge(
- PKG_N_MR1, UID_N_MR1, true, UserHandle.USER_SYSTEM);
+ PKG_N_MR1, UID_N_MR1, true, USER_SYSTEM);
String expected = "<ranking version=\"3\">\n"
// Importance 0 because off in permissionhelper
+ "<package name=\"com.example.o\" importance=\"0\" show_badge=\"true\" "
@@ -962,11 +1046,11 @@
appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false));
appPermissions.put(new Pair(UID_N_MR1, PKG_N_MR1), new Pair(true, false));
- when(mPermissionHelper.getNotificationPermissionValues(UserHandle.USER_SYSTEM))
+ when(mPermissionHelper.getNotificationPermissionValues(USER_SYSTEM))
.thenReturn(appPermissions);
ByteArrayOutputStream baos = writeXmlAndPurge(
- PKG_N_MR1, UID_N_MR1, true, UserHandle.USER_SYSTEM);
+ PKG_N_MR1, UID_N_MR1, true, USER_SYSTEM);
String expected = "<ranking version=\"3\">\n"
// Packages that exist solely in permissionhelper
+ "<package name=\"" + PKG_P + "\" importance=\"3\" />\n"
@@ -986,10 +1070,10 @@
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true,
- UserHandle.USER_SYSTEM, channel.getId());
+ USER_SYSTEM, channel.getId());
// Testing that in restore we are given the canonical version
- loadStreamXml(baos, true, UserHandle.USER_SYSTEM);
+ loadStreamXml(baos, true, USER_SYSTEM);
verify(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
}
@@ -1012,9 +1096,9 @@
channel.setSound(SOUND_URI, mAudioAttributes);
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true,
- UserHandle.USER_SYSTEM, channel.getId());
+ USER_SYSTEM, channel.getId());
- loadStreamXml(baos, true, UserHandle.USER_SYSTEM);
+ loadStreamXml(baos, true, USER_SYSTEM);
NotificationChannel actualChannel = mHelper.getNotificationChannel(
PKG_N_MR1, UID_N_MR1, channel.getId(), false);
@@ -1034,9 +1118,9 @@
channel.setSound(SOUND_URI, mAudioAttributes);
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true,
- UserHandle.USER_SYSTEM, channel.getId());
+ USER_SYSTEM, channel.getId());
- loadStreamXml(baos, true, UserHandle.USER_SYSTEM);
+ loadStreamXml(baos, true, USER_SYSTEM);
NotificationChannel actualChannel = mHelper.getNotificationChannel(
PKG_N_MR1, UID_N_MR1, channel.getId(), false);
@@ -1065,7 +1149,7 @@
+ "</ranking>\n";
loadByteArrayXml(
- backupWithUncanonicalizedSoundUri.getBytes(), true, UserHandle.USER_SYSTEM);
+ backupWithUncanonicalizedSoundUri.getBytes(), true, USER_SYSTEM);
NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, id, false);
assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
@@ -1078,9 +1162,9 @@
channel.setSound(null, mAudioAttributes);
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true,
- UserHandle.USER_SYSTEM, channel.getId());
+ USER_SYSTEM, channel.getId());
- loadStreamXml(baos, true, UserHandle.USER_SYSTEM);
+ loadStreamXml(baos, true, USER_SYSTEM);
NotificationChannel actualChannel = mHelper.getNotificationChannel(
PKG_N_MR1, UID_N_MR1, channel.getId(), false);
@@ -1111,7 +1195,7 @@
mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true,
- UserHandle.USER_SYSTEM, channel1.getId(), channel2.getId(), channel3.getId(),
+ USER_SYSTEM, channel1.getId(), channel2.getId(), channel3.getId(),
NotificationChannel.DEFAULT_CHANNEL_ID);
mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_N_MR1}, new int[]{
UID_N_MR1});
@@ -1120,7 +1204,7 @@
parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
null);
parser.nextTag();
- mHelper.readXml(parser, true, UserHandle.USER_SYSTEM);
+ mHelper.readXml(parser, true, USER_SYSTEM);
assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId(), false));
@@ -2218,7 +2302,7 @@
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
- assertTrue(mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG_N_MR1},
+ assertTrue(mHelper.onPackagesChanged(true, USER_SYSTEM, new String[]{PKG_N_MR1},
new int[]{UID_N_MR1}));
assertEquals(0, mHelper.getNotificationChannels(
@@ -2227,7 +2311,7 @@
// Not deleted
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
- assertFalse(mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM,
+ assertFalse(mHelper.onPackagesChanged(false, USER_SYSTEM,
new String[]{PKG_N_MR1}, new int[]{UID_N_MR1}));
assertEquals(2, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size());
}
@@ -2236,7 +2320,7 @@
public void testOnPackageChanged_packageRemoval_importance() throws Exception {
mHelper.setImportance(PKG_N_MR1, UID_N_MR1, NotificationManager.IMPORTANCE_HIGH);
- mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{
+ mHelper.onPackagesChanged(true, USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{
UID_N_MR1});
assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1,
@@ -2250,7 +2334,7 @@
NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
- mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{
+ mHelper.onPackagesChanged(true, USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{
UID_N_MR1});
assertEquals(0, mHelper.getNotificationChannelGroups(
@@ -2267,7 +2351,7 @@
legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
when(mPm.getApplicationInfoAsUser(eq(PKG_O), anyInt(), anyInt())).thenReturn(legacy);
mHelper.onPackagesChanged(
- false, UserHandle.USER_SYSTEM, new String[]{PKG_O}, new int[]{UID_O});
+ false, USER_SYSTEM, new String[]{PKG_O}, new int[]{UID_O});
// make sure the default channel was readded
//assertEquals(2, mHelper.getNotificationChannels(PKG_O, UID_O, false).getList().size());
@@ -3224,7 +3308,7 @@
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
mAppOpsManager, mStatsEventBuilderFactory);
- loadByteArrayXml(preQXml.getBytes(), true, UserHandle.USER_SYSTEM);
+ loadByteArrayXml(preQXml.getBytes(), true, USER_SYSTEM);
assertEquals(PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS,
mHelper.shouldHideSilentStatusIcons());
@@ -4058,7 +4142,7 @@
// clear data
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, true,
- UserHandle.USER_SYSTEM, channel1.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
+ USER_SYSTEM, channel1.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_O}, new int[]{
UID_O});
@@ -4070,7 +4154,7 @@
parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
null);
parser.nextTag();
- mHelper.readXml(parser, true, UserHandle.USER_SYSTEM);
+ mHelper.readXml(parser, true, USER_SYSTEM);
assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, channel1.getId(), false)
.isImportanceLockedByCriticalDeviceFunction());
@@ -4357,7 +4441,7 @@
assertTrue(nc1.isDeleted());
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_P, UID_P, false,
- UserHandle.USER_SYSTEM, "id", NotificationChannel.DEFAULT_CHANNEL_ID);
+ USER_SYSTEM, "id", NotificationChannel.DEFAULT_CHANNEL_ID);
TypedXmlPullParser parser = Xml.newFastPullParser();
parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
@@ -4366,7 +4450,7 @@
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
mAppOpsManager, mStatsEventBuilderFactory);
- mHelper.readXml(parser, true, UserHandle.USER_SYSTEM);
+ mHelper.readXml(parser, true, USER_SYSTEM);
NotificationChannel nc = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true);
assertTrue(DateUtils.isToday(nc.getDeletedTimeMs()));
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 9fec96b..467084a 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -837,6 +837,17 @@
"android.telecom.extra.AUDIO_CODEC_BANDWIDTH_KHZ";
/**
+ * Last known cell identity key to be used to fill geo location header in case of an emergency
+ * call. This entry will not be filled if call is not identified as an emergency call.
+ * {@link Connection}. Only provided to the {@link ConnectionService} for the purpose
+ * of placing an emergency call; will not be present in the {@link InCallService} layer.
+ * The {@link ConnectionService}'s implementation will be logged for fine location access
+ * when an outgoing call is placed in this case.
+ */
+ public static final String EXTRA_LAST_KNOWN_CELL_IDENTITY =
+ "android.telecom.extra.LAST_KNOWN_CELL_IDENTITY";
+
+ /**
* Boolean connection extra key used to indicate whether device to device communication is
* available for the current call.
* @hide
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 2b52af3..1ba997f 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -576,6 +576,17 @@
}
/**
+ * Check if the caller (or self, if not processing an IPC) has ACCESS_LAST_KNOWN_CELL_ID
+ * permission
+ *
+ * @return true if caller has ACCESS_LAST_KNOWN_CELL_ID permission else false.
+ */
+ public static boolean checkLastKnownCellIdAccessPermission(Context context) {
+ return context.checkCallingOrSelfPermission("android.permission.ACCESS_LAST_KNOWN_CELL_ID")
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
* Ensure the caller (or self, if not processing an IPC) has
* {@link android.Manifest.permission#READ_PHONE_STATE} or carrier privileges.
*
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index 1d7a476..4469ffc 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -17,6 +17,7 @@
package android.telephony;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.hardware.radio.V1_5.AccessNetwork;
@@ -28,6 +29,8 @@
*/
public final class AccessNetworkConstants {
+ private static final String TAG = AccessNetworkConstants.class.getSimpleName();
+
/**
* Wireless transportation type
*
@@ -108,6 +111,21 @@
default: return Integer.toString(type);
}
}
+
+ /** @hide */
+ public static @RadioAccessNetworkType int fromString(@NonNull String str) {
+ switch (str.toUpperCase()) {
+ case "GERAN" : return GERAN;
+ case "UTRAN" : return UTRAN;
+ case "EUTRAN" : return EUTRAN;
+ case "CDMA2000" : return CDMA2000;
+ case "IWLAN" : return IWLAN;
+ case "NGRAN" : return NGRAN;
+ default:
+ Rlog.e(TAG, "Invalid access network type " + str);
+ return UNKNOWN;
+ }
+ }
}
/**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6f92c31..c80d35b 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1063,6 +1063,12 @@
"always_show_emergency_alert_onoff_bool";
/**
+ * Default mobile network MTU value, in bytes.
+ * @hide
+ */
+ public static final String KEY_DEFAULT_MTU_INT = "default_mtu_int";
+
+ /**
* The data call retry configuration for different types of APN.
* @hide
*/
@@ -2914,19 +2920,37 @@
"signal_strength_nr_nsa_use_lte_as_primary_bool";
/**
+ * String array of TCP buffer sizes per network type.
+ * The entries should be of the following form, with values in bytes:
+ * "network_name:read_min,read_default,read_max,write_min,write_default,write_max".
+ * For NR (5G), the following network names should be used:
+ * - NR_NSA: NR NSA, sub-6 frequencies
+ * - NR_NSA_MMWAVE: NR NSA, mmwave frequencies
+ * - NR_SA: NR SA, sub-6 frequencies
+ * - NR_SA_MMWAVE: NR SA, mmwave frequencies
+ * @hide
+ */
+ public static final String KEY_TCP_BUFFERS_STRING_ARRAY = "tcp_buffers_string_array";
+
+ /**
* String array of default bandwidth values per network type.
- * The entries should be of form "network_name:downstream,upstream", with values in Kbps.
+ * The entries should be of form: "network_name:downlink,uplink", with values in Kbps.
+ * For NR (5G), the following network names should be used:
+ * - NR_NSA: NR NSA, sub-6 frequencies
+ * - NR_NSA_MMWAVE: NR NSA, mmwave frequencies
+ * - NR_SA: NR SA, sub-6 frequencies
+ * - NR_SA_MMWAVE: NR SA, mmwave frequencies
* @hide
*/
public static final String KEY_BANDWIDTH_STRING_ARRAY = "bandwidth_string_array";
/**
* For NR (non-standalone), whether to use the LTE value instead of NR value as the default for
- * upstream bandwidth. Downstream bandwidth will still use the NR value as the default.
+ * uplink bandwidth. Downlink bandwidth will still use the NR value as the default.
* @hide
*/
- public static final String KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPSTREAM_BOOL =
- "bandwidth_nr_nsa_use_lte_value_for_upstream_bool";
+ public static final String KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPLINK_BOOL =
+ "bandwidth_nr_nsa_use_lte_value_for_uplink_bool";
/**
* Key identifying if voice call barring notification is required to be shown to the user.
@@ -3628,6 +3652,18 @@
public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = "5g_watchdog_time_ms_long";
/**
+ * Which NR types are unmetered. A string array containing the following keys:
+ * NR_NSA - NR NSA is unmetered for sub-6 frequencies
+ * NR_NSA_MMWAVE - NR NSA is unmetered for mmwave frequencies
+ * NR_SA - NR SA is unmetered for sub-6 frequencies
+ * NR_SA_MMWAVE - NR SA is unmetered for mmwave frequencies
+ * TODO: remove other unmetered keys and replace with this
+ * @hide
+ */
+ public static final String KEY_UNMETERED_NETWORK_TYPES_STRING_ARRAY =
+ "unmetered_network_types_string_array";
+
+ /**
* Whether NR (non-standalone) should be unmetered for all frequencies.
* If either {@link #KEY_UNMETERED_NR_NSA_MMWAVE_BOOL} or
* {@link #KEY_UNMETERED_NR_NSA_SUB6_BOOL} are true, then this value will be ignored.
@@ -5345,6 +5381,34 @@
public static final String KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL =
"unthrottle_data_retry_when_tac_changes_bool";
+ /**
+ * IWLAN handover rules that determine whether handover is allowed or disallowed between
+ * cellular and IWLAN.
+ *
+ * The handover rules will be matched in the order. Here are some sample rules.
+ * <string-array name="iwlan_handover_rules" num="5">
+ * <!-- Handover from IWLAN to 2G/3G is not allowed -->
+ * <item value="source=IWLAN, target=GERAN|UTRAN, type=disallowed"/>
+ * <!-- Handover from 2G/3G to IWLAN is not allowed -->
+ * <item value="source=GERAN|UTRAN, target:IWLAN, type=disallowed"/>
+ * <!-- Handover from IWLAN to 3G/4G/5G is not allowed if the device is roaming. -->
+ * <item value="source=IWLAN, target=UTRAN|EUTRAN|NGRAN, roaming=true, type=disallowed"/>
+ * <!-- Handover from 4G to IWLAN is not allowed -->
+ * <item value="source=EUTRAN, target=IWLAN, type=disallowed"/>
+ * <!-- Handover is always allowed in any condition. -->
+ * <item value="source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN,
+ * target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"/>
+ * </string-array>
+ *
+ * When handover is not allowed, frameworks will tear down the data network on source transport,
+ * and then setup a new one on the target transport when Qualified Network Service changes the
+ * preferred access networks for particular APN types.
+ *
+ * @hide
+ */
+ public static final String KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY =
+ "iwlan_handover_policy_string_array";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -5483,6 +5547,7 @@
sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
+ sDefaults.putInt(KEY_DEFAULT_MTU_INT, 1500);
sDefaults.putStringArray(KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS, new String[]{
"default:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
+ "320000:5000,640000:5000,1280000:5000,1800000:5000",
@@ -5801,12 +5866,35 @@
CellSignalStrengthNr.USE_SSRSRP);
sDefaults.putBoolean(KEY_SIGNAL_STRENGTH_NR_NSA_USE_LTE_AS_PRIMARY_BOOL, true);
sDefaults.putStringArray(KEY_BANDWIDTH_STRING_ARRAY, new String[]{
- "GPRS:24,24", "EDGE:70,18", "UMTS:115,115", "CDMA-IS95A:14,14", "CDMA-IS95B:14,14",
- "1xRTT:30,30", "EvDo-rev.0:750,48", "EvDo-rev.A:950,550", "HSDPA:4300,620",
- "HSUPA:4300,1800", "HSPA:4300,1800", "EvDo-rev.B:1500,550", "eHRPD:750,48",
- "HSPAP:13000,3400", "TD-SCDMA:115,115", "LTE:30000,15000", "NR_NSA:47000,18000",
- "NR_NSA_MMWAVE:145000,60000", "NR_SA:145000,60000"});
- sDefaults.putBoolean(KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPSTREAM_BOOL, false);
+ "GPRS:24,24", "EDGE:70,18", "UMTS:115,115", "CDMA:14,14",
+ "1xRTT:30,30", "EvDo_0:750,48", "EvDo_A:950,550", "HSDPA:4300,620",
+ "HSUPA:4300,1800", "HSPA:4300,1800", "EvDo_B:1500,550", "eHRPD:750,48",
+ "iDEN:14,14", "LTE:30000,15000", "HSPA+:13000,3400", "GSM:24,24",
+ "TD_SCDMA:115,115", "LTE_CA:30000,15000", "NR_NSA:47000,18000",
+ "NR_NSA_MMWAVE:145000,60000", "NR_SA:145000,60000", "NR_SA_MMWAVE:145000,60000"});
+ sDefaults.putStringArray(KEY_TCP_BUFFERS_STRING_ARRAY, new String[]{
+ "GPRS:4092,8760,48000,4096,8760,48000", "EDGE:4093,26280,70800,4096,16384,70800",
+ "UMTS:58254,349525,1048576,58254,349525,1048576",
+ "CDMA:4094,87380,262144,4096,16384,262144",
+ "1xRTT:16384,32768,131072,4096,16384,102400",
+ "EvDo_0:4094,87380,262144,4096,16384,262144",
+ "EvDo_A:4094,87380,262144,4096,16384,262144",
+ "HSDPA:61167,367002,1101005,8738,52429,262114",
+ "HSUPA:40778,244668,734003,16777,100663,301990",
+ "HSPA:40778,244668,734003,16777,100663,301990",
+ "EvDo_B:4094,87380,262144,4096,16384,262144",
+ "eHRPD:131072,262144,1048576,4096,16384,524288",
+ "iDEN:4094,87380,262144,4096,16384,262144",
+ "LTE:524288,1048576,2097152,262144,524288,1048576",
+ "HSPA+:122334,734003,2202010,32040,192239,576717",
+ "GSM:4092,8760,48000,4096,8760,48000",
+ "TD_SCDMA:58254,349525,1048576,58254,349525,1048576",
+ "LTE_CA:4096,6291456,12582912,4096,1048576,2097152",
+ "NR_NSA:2097152,6291456,16777216,512000,2097152,8388608",
+ "NR_NSA_MMWAVE:2097152,6291456,16777216,512000,2097152,8388608",
+ "NR_SA:2097152,6291456,16777216,512000,2097152,8388608",
+ "NR_SA_MMWAVE:2097152,6291456,16777216,512000,2097152,8388608"});
+ sDefaults.putBoolean(KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPLINK_BOOL, false);
sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "rssi");
sDefaults.putBoolean(KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL, false);
@@ -5832,6 +5920,7 @@
sDefaults.putInt(KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0);
sDefaults.putBoolean(KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL, true);
sDefaults.putBoolean(KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL, false);
+ sDefaults.putStringArray(KEY_UNMETERED_NETWORK_TYPES_STRING_ARRAY, new String[0]);
sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_BOOL, false);
sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, false);
sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_SUB6_BOOL, false);
@@ -5892,6 +5981,7 @@
sDefaults.putInt(
KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG,
120000);
+ sDefaults.putAll(ImsServiceEntitlement.getDefaults());
sDefaults.putAll(Gps.getDefaults());
sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
new int[] {
@@ -5982,6 +6072,9 @@
sDefaults.putBoolean(KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false);
sDefaults.putBoolean(KEY_VONR_SETTING_VISIBILITY_BOOL, true);
sDefaults.putBoolean(KEY_VONR_ENABLED_BOOL, false);
+ sDefaults.putStringArray(KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY, new String[]{
+ "source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, "
+ + "target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"});
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 0267b68..cca5e2f 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -8551,12 +8551,58 @@
public NetworkScan requestNetworkScan(
NetworkScanRequest request, Executor executor,
TelephonyScanManager.NetworkScanCallback callback) {
- synchronized (this) {
+ return requestNetworkScan(false, request, executor, callback);
+ }
+
+ /**
+ * Request a network scan.
+ *
+ * This method is asynchronous, so the network scan results will be returned by callback.
+ * The returned NetworkScan will contain a callback method which can be used to stop the scan.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+ * app has carrier privileges (see {@link #hasCarrierPrivileges})
+ * and {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
+ *
+ * If the system-wide location switch is off, apps may still call this API, with the
+ * following constraints:
+ * <ol>
+ * <li>The app must hold the {@code android.permission.NETWORK_SCAN} permission.</li>
+ * <li>The app must not supply any specific bands or channels to scan.</li>
+ * <li>The app must only specify MCC/MNC pairs that are
+ * associated to a SIM in the device.</li>
+ * <li>Returned results will have no meaningful info other than signal strength
+ * and MCC/MNC info.</li>
+ * </ol>
+ *
+ * @param renounceFineLocationAccess Set this to true if the caller would not like to receive
+ * location related information which will be sent if the caller already possess
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and do not renounce the permission
+ * @param request Contains all the RAT with bands/channels that need to be scanned.
+ * @param executor The executor through which the callback should be invoked. Since the scan
+ * request may trigger multiple callbacks and they must be invoked in the same order as
+ * they are received by the platform, the user should provide an executor which executes
+ * tasks one at a time in serial order. For example AsyncTask.SERIAL_EXECUTOR.
+ * @param callback Returns network scan results or errors.
+ * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
+ */
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ })
+ public @Nullable NetworkScan requestNetworkScan(
+ boolean renounceFineLocationAccess, @NonNull NetworkScanRequest request,
+ @NonNull Executor executor,
+ @NonNull TelephonyScanManager.NetworkScanCallback callback) {
+ synchronized (sCacheLock) {
if (mTelephonyScanManager == null) {
mTelephonyScanManager = new TelephonyScanManager();
}
}
- return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback,
+ return mTelephonyScanManager.requestNetworkScan(getSubId(), renounceFineLocationAccess,
+ request, executor, callback,
getOpPackageName(), getAttributionTag());
}
@@ -11613,7 +11659,6 @@
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges})
* and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
- *
* May return {@code null} when the subscription is inactive or when there was an error
* communicating with the phone process.
*/
@@ -11623,7 +11668,72 @@
Manifest.permission.ACCESS_COARSE_LOCATION
})
public @Nullable ServiceState getServiceState() {
- return getServiceStateForSubscriber(getSubId());
+ return getServiceState(false, false);
+ }
+
+ /**
+ * Returns the current {@link ServiceState} information.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+ *
+ * If you want continuous updates of service state info, register a {@link PhoneStateListener}
+ * via {@link #listen} with the {@link PhoneStateListener#LISTEN_SERVICE_STATE} event.
+ *
+ * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges})
+ * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+ * @param renounceFineLocationAccess Set this to true if the caller would not like to receive
+ * location related information which will be sent if the caller already possess
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and do not renounce the permission
+ * @param renounceCoarseLocationAccess Set this to true if the caller would not like to
+ * receive location related information which will be sent if the caller already possess
+ * {@link Manifest.permission#ACCESS_COARSE_LOCATION} and do not renounce the permissions.
+ * May return {@code null} when the subscription is inactive or when there was an error
+ * communicating with the phone process.
+ */
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(allOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ })
+ public @Nullable ServiceState getServiceState(boolean renounceFineLocationAccess,
+ boolean renounceCoarseLocationAccess) {
+ return getServiceStateForSubscriber(getSubId(), renounceFineLocationAccess,
+ renounceCoarseLocationAccess);
+ }
+
+ /**
+ * Returns the service state information on specified subscription. Callers require
+ * either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information.
+ *
+ * May return {@code null} when the subscription is inactive or when there was an error
+ * communicating with the phone process.
+ * @param renounceFineLocationAccess Set this to true if the caller would not like to receive
+ * location related information which will be sent if the caller already possess
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and do not renounce the permission
+ * @param renounceCoarseLocationAccess Set this to true if the caller would not like to
+ * receive location related information which will be sent if the caller already possess
+ * {@link Manifest.permission#ACCESS_COARSE_LOCATION} and do not renounce the permissions.
+ */
+ private ServiceState getServiceStateForSubscriber(int subId,
+ boolean renounceFineLocationAccess,
+ boolean renounceCoarseLocationAccess) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getServiceStateForSubscriber(subId, renounceFineLocationAccess,
+ renounceCoarseLocationAccess,
+ getOpPackageName(), getAttributionTag());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getServiceStateForSubscriber", e);
+ } catch (NullPointerException e) {
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString("a3ab0b9d-f2aa-4baf-911d-7096c0d4645a"),
+ "getServiceStateForSubscriber " + subId + " NPE");
+ }
+ return null;
}
/**
@@ -11636,20 +11746,7 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public ServiceState getServiceStateForSubscriber(int subId) {
- try {
- ITelephony service = getITelephony();
- if (service != null) {
- return service.getServiceStateForSubscriber(subId, getOpPackageName(),
- getAttributionTag());
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#getServiceStateForSubscriber", e);
- } catch (NullPointerException e) {
- AnomalyReporter.reportAnomaly(
- UUID.fromString("a3ab0b9d-f2aa-4baf-911d-7096c0d4645a"),
- "getServiceStateForSubscriber " + subId + " NPE");
- }
- return null;
+ return getServiceStateForSubscriber(getSubId(), false, false);
}
/**
@@ -15500,7 +15597,49 @@
*/
public void registerTelephonyCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull TelephonyCallback callback) {
+ registerTelephonyCallback(false, false, executor, callback);
+ }
+ /**
+ * Registers a callback object to receive notification of changes in specified telephony states.
+ * <p>
+ * To register a callback, pass a {@link TelephonyCallback} which implements
+ * interfaces of events. For example,
+ * FakeServiceStateCallback extends {@link TelephonyCallback} implements
+ * {@link TelephonyCallback.ServiceStateListener}.
+ *
+ * At registration, and when a specified telephony state changes, the telephony manager invokes
+ * the appropriate callback method on the callback object and passes the current (updated)
+ * values.
+ * <p>
+ *
+ * If this TelephonyManager object has been created with {@link #createForSubscriptionId},
+ * applies to the given subId. Otherwise, applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}. To register events for multiple
+ * subIds, pass a separate callback object to each TelephonyManager object created with
+ * {@link #createForSubscriptionId}.
+ *
+ * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
+ * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
+ * {@link SecurityException} will be thrown otherwise.
+ *
+ * This API should be used sparingly -- large numbers of callbacks will cause system
+ * instability. If a process has registered too many callbacks without unregistering them, it
+ * may encounter an {@link IllegalStateException} when trying to register more callbacks.
+ *
+ * @param renounceFineLocationAccess Set this to true if the caller would not like to receive
+ * location related information which will be sent if the caller already possess
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and do not renounce the permissions.
+ * @param renounceCoarseLocationAccess Set this to true if the caller would not like to
+ * receive location related information which will be sent if the caller already possess
+ * {@link Manifest.permission#ACCESS_COARSE_LOCATION} and do not renounce the permissions.
+ * @param executor The executor of where the callback will execute.
+ * @param callback The {@link TelephonyCallback} object to register.
+ */
+ public void registerTelephonyCallback(boolean renounceFineLocationAccess,
+ boolean renounceCoarseLocationAccess,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull TelephonyCallback callback) {
if (mContext == null) {
throw new IllegalStateException("telephony service is null.");
}
@@ -15511,7 +15650,8 @@
mTelephonyRegistryMgr = (TelephonyRegistryManager)
mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
if (mTelephonyRegistryMgr != null) {
- mTelephonyRegistryMgr.registerTelephonyCallback(executor, mSubId, getOpPackageName(),
+ mTelephonyRegistryMgr.registerTelephonyCallback(renounceFineLocationAccess,
+ renounceCoarseLocationAccess, executor, mSubId, getOpPackageName(),
getAttributionTag(), callback, getITelephony() != null);
} else {
throw new IllegalStateException("telephony service is null.");
@@ -16035,4 +16175,31 @@
ex.rethrowAsRuntimeException();
}
}
+
+ /**
+ * Get last known cell identity.
+ * Require {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and
+ * com.android.phone.permission.ACCESS_LAST_KNOWN_CELL_ID, otherwise throws SecurityException.
+ * If there is current registered network this value will be same as the registered cell
+ * identity. If the device goes out of service the previous cell identity is cached and
+ * will be returned. If the cache age of the Cell identity is more than 24 hours
+ * it will be cleared and null will be returned.
+ * @return last known cell identity {@CellIdentity}.
+ * @hide
+ */
+ @RequiresPermission(allOf = {Manifest.permission.ACCESS_FINE_LOCATION,
+ "com.android.phone.permission.ACCESS_LAST_KNOWN_CELL_ID"})
+ public @Nullable CellIdentity getLastKnownCellIdentity() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ return telephony.getLastKnownCellIdentity(getSubId(), getOpPackageName(),
+ getAttributionTag());
+ } catch (RemoteException ex) {
+ ex.rethrowAsRuntimeException();
+ }
+ return null;
+ }
}
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 9572154..122662d 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -228,7 +228,9 @@
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
* Or the calling app has carrier privileges. @see #hasCarrierPrivileges
- *
+ * @param renounceFineLocationAccess Set this to true if the caller would not like to receive
+ * location related information which will be sent if the caller already possess
+ * {@link android.Manifest.permission.ACCESS_FINE_LOCATION} and do not renounce the permission
* @param request Contains all the RAT with bands/channels that need to be scanned.
* @param callback Returns network scan results or errors.
* @param callingPackage The package name of the caller
@@ -237,6 +239,7 @@
* @hide
*/
public NetworkScan requestNetworkScan(int subId,
+ boolean renounceFineLocationAccess,
NetworkScanRequest request, Executor executor, NetworkScanCallback callback,
String callingPackage, @Nullable String callingFeatureId) {
try {
@@ -252,7 +255,8 @@
// the record to the ScanInfo cache.
synchronized (mScanInfo) {
int scanId = telephony.requestNetworkScan(
- subId, request, mMessenger, new Binder(), callingPackage,
+ subId, renounceFineLocationAccess, request, mMessenger,
+ new Binder(), callingPackage,
callingFeatureId);
if (scanId == INVALID_SCAN_ID) {
Rlog.e(TAG, "Failed to initiate network scan");
diff --git a/telephony/java/android/telephony/ims/RcsClientConfiguration.java b/telephony/java/android/telephony/ims/RcsClientConfiguration.java
index 793c377..c25ace0 100644
--- a/telephony/java/android/telephony/ims/RcsClientConfiguration.java
+++ b/telephony/java/android/telephony/ims/RcsClientConfiguration.java
@@ -50,6 +50,7 @@
private String mRcsProfile;
private String mClientVendor;
private String mClientVersion;
+ private boolean mRcsEnabledByUser;
/**
* Create a RcsClientConfiguration object.
@@ -63,14 +64,41 @@
* @param clientVersion Identifies the RCS client version. Refer to GSMA
* RCC.07 "client_version" parameter.
* Example:client_version=RCSAndrd-1.0
+ * @deprecated Use {@link #RcsClientConfiguration(String, String, String, String, boolean)}
+ * instead. Deprecated prototype assumes that the user setting controlling RCS is enabled.
*/
+ @Deprecated
public RcsClientConfiguration(@NonNull String rcsVersion,
@NonNull @StringRcsProfile String rcsProfile,
@NonNull String clientVendor, @NonNull String clientVersion) {
+ this(rcsVersion, rcsProfile, clientVendor, clientVersion, true);
+ }
+
+ /**
+ * Create a RcsClientConfiguration object.
+ * Default messaging application must pass a valid configuration object
+ * @param rcsVersion The parameter identifies the RCS version supported
+ * by the client. Refer to GSMA RCC.07 "rcs_version" parameter.
+ * @param rcsProfile Identifies a fixed set of RCS services that are
+ * supported by the client. See {@link #RCS_PROFILE_1_0 } or
+ * {@link #RCS_PROFILE_2_3 }
+ * @param clientVendor Identifies the vendor providing the RCS client.
+ * @param clientVersion Identifies the RCS client version. Refer to GSMA
+ * RCC.07 "client_version" parameter.
+ * Example:client_version=RCSAndrd-1.0
+ * @param isRcsEnabledByUser The current user setting for whether or not the user has
+ * enabled or disabled RCS. Please refer to GSMA RCC.07 "rcs_state" parameter for how this
+ * can affect provisioning.
+ */
+ public RcsClientConfiguration(@NonNull String rcsVersion,
+ @NonNull @StringRcsProfile String rcsProfile,
+ @NonNull String clientVendor, @NonNull String clientVersion,
+ boolean isRcsEnabledByUser) {
mRcsVersion = rcsVersion;
mRcsProfile = rcsProfile;
mClientVendor = clientVendor;
mClientVersion = clientVersion;
+ mRcsEnabledByUser = isRcsEnabledByUser;
}
/**
@@ -102,6 +130,18 @@
}
/**
+ * The current user setting provided by the RCS messaging application that determines
+ * whether or not the user has enabled RCS.
+ * <p>
+ * See GSMA RCC.07 "rcs_state" parameter for more information about how this setting
+ * affects provisioning.
+ * @return true if RCS is enabled by the user, false if RCS is disabled by the user.
+ */
+ public boolean isRcsEnabledByUser() {
+ return mRcsEnabledByUser;
+ }
+
+ /**
* {@link Parcelable#writeToParcel}
*/
@Override
@@ -110,6 +150,7 @@
out.writeString(mRcsProfile);
out.writeString(mClientVendor);
out.writeString(mClientVersion);
+ out.writeBoolean(mRcsEnabledByUser);
}
/**
@@ -124,8 +165,9 @@
String rcsProfile = in.readString();
String clientVendor = in.readString();
String clientVersion = in.readString();
+ Boolean rcsEnabledByUser = in.readBoolean();
return new RcsClientConfiguration(rcsVersion, rcsProfile,
- clientVendor, clientVersion);
+ clientVendor, clientVersion, rcsEnabledByUser);
}
@Override
@@ -152,11 +194,13 @@
return mRcsVersion.equals(other.mRcsVersion) && mRcsProfile.equals(other.mRcsProfile)
&& mClientVendor.equals(other.mClientVendor)
- && mClientVersion.equals(other.mClientVersion);
+ && mClientVersion.equals(other.mClientVersion)
+ && (mRcsEnabledByUser == other.mRcsEnabledByUser);
}
@Override
public int hashCode() {
- return Objects.hash(mRcsVersion, mRcsProfile, mClientVendor, mClientVersion);
+ return Objects.hash(mRcsVersion, mRcsProfile, mClientVendor, mClientVersion,
+ mRcsEnabledByUser);
}
}
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 7a1c092..61de3ac 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -28,13 +28,10 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IImsRcsController;
import android.telephony.ims.aidl.IRcsUceControllerCallback;
import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
-import android.telephony.ims.feature.RcsFeature;
import android.util.Log;
import java.lang.annotation.Retention;
@@ -337,6 +334,14 @@
@SystemApi
public static final int PUBLISH_STATE_OTHER_ERROR = 6;
+ /**
+ * The device is currently trying to publish its capabilities to the network.
+ * @hide
+ */
+ @SystemApi
+ public static final int PUBLISH_STATE_PUBLISHING = 7;
+
+
/**@hide*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "PUBLISH_STATE_", value = {
@@ -345,7 +350,8 @@
PUBLISH_STATE_VOICE_PROVISION_ERROR,
PUBLISH_STATE_RCS_PROVISION_ERROR,
PUBLISH_STATE_REQUEST_TIMEOUT,
- PUBLISH_STATE_OTHER_ERROR
+ PUBLISH_STATE_OTHER_ERROR,
+ PUBLISH_STATE_PUBLISHING
})
public @interface PublishState {}
@@ -480,9 +486,12 @@
* <p>
* Be sure to check the availability of this feature using
* {@link ImsRcsManager#isAvailable(int, int)} and ensuring
- * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
- * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else
- * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
+ * {@link
+ * android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
+ * {@link
+ * android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is
+ * enabled or else this operation will fail with {@link #ERROR_NOT_AVAILABLE} or
+ * {@link #ERROR_NOT_ENABLED}.
*
* @param contactNumbers A list of numbers that the capabilities are being requested for.
* @param executor The executor that will be used when the request is completed and the
@@ -573,8 +582,10 @@
* <p>
* Be sure to check the availability of this feature using
* {@link ImsRcsManager#isAvailable(int, int)} and ensuring
- * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
- * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is
+ * {@link
+ * android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
+ * {@link
+ * android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is
* enabled or else this operation will fail with
* {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
*
@@ -690,7 +701,8 @@
* Registers a {@link OnPublishStateChangedListener} with the system, which will provide publish
* state updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}.
* <p>
- * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to subscription
+ * Use {@link android.telephony.SubscriptionManager.OnSubscriptionsChangedListener} to listen
+ * to subscription
* changed events and call
* {@link #removeOnPublishStateChangedListener(OnPublishStateChangedListener)} to clean up.
* <p>
@@ -792,7 +804,8 @@
* cache associated with those contacts as the local cache becomes stale.
* <p>
* This setting will only enable this feature if
- * {@link CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is also enabled.
+ * {@link android.telephony.CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is
+ * also enabled.
* <p>
* Note: This setting does not affect whether or not the device publishes its service
* capabilities if the subscription supports presence publication.
@@ -843,7 +856,8 @@
* session.
* <p>
* This setting will only enable this feature if
- * {@link CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is also enabled.
+ * {@link android.telephony.CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is
+ * also enabled.
* <p>
* Note: This setting does not affect whether or not the device publishes its service
* capabilities if the subscription supports presence publication.
diff --git a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
index c3d7325..c27fa4f 100644
--- a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
@@ -81,6 +81,26 @@
}
/**
+ * Receives the status of changes in the publishing connection from ims service
+ * and deliver this callback to the framework.
+ */
+ public void onPublishUpdated(int reasonCode, @NonNull String reasonPhrase,
+ int reasonHeaderCause, @NonNull String reasonHeaderText) throws ImsException {
+ ICapabilityExchangeEventListener listener = mListenerBinder;
+ if (listener == null) {
+ return;
+ }
+ try {
+ listener.onPublishUpdated(reasonCode, reasonPhrase,
+ reasonHeaderCause, reasonHeaderText);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "onPublishUpdated exception: " + e);
+ throw new ImsException("Remote is not available",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
* Receives the callback of the remote capability request from the network and deliver this
* request to the framework.
*/
diff --git a/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl b/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl
index 078ac91..c675bc3 100644
--- a/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl
@@ -31,6 +31,8 @@
oneway interface ICapabilityExchangeEventListener {
void onRequestPublishCapabilities(int publishTriggerType);
void onUnpublish();
+ void onPublishUpdated(int reasonCode, String reasonPhrase, int reasonHeaderCause,
+ String reasonHeaderText);
void onRemoteCapabilityRequest(in Uri contactUri,
in List<String> remoteCapabilities, IOptionsRequestCallback cb);
}
diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
index a3be8da..9293a40 100644
--- a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
+++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
@@ -90,6 +90,30 @@
void onUnpublish() throws ImsException;
/**
+ * Notify the framework that the ImsService has refreshed the PUBLISH
+ * internally, which has resulted in a new PUBLISH result.
+ * <p>
+ * This method must return both SUCCESS (200 OK) and FAILURE (300+) codes in order to
+ * keep the AOSP stack up to date.
+ * @param reasonCode The SIP response code sent from the network.
+ * @param reasonPhrase The optional reason response from the network. If the
+ * network provided no reason with the sip code, the string should be empty.
+ * @param reasonHeaderCause The “cause” parameter of the “reason” header
+ * included in the SIP message.
+ * @param reasonHeaderText The “text” parameter of the “reason” header
+ * included in the SIP message.
+ * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not
+ * currently connected to the framework. This can happen if the {@link RcsFeature} is not
+ * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+ * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare
+ * cases when the Telephony stack has crashed.
+ *
+ */
+ default void onPublishUpdated(int reasonCode, @NonNull String reasonPhrase,
+ int reasonHeaderCause, @NonNull String reasonHeaderText) throws ImsException {
+ }
+
+ /**
* Inform the framework of an OPTIONS query from a remote device for this device's UCE
* capabilities.
* <p>
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index d5c9ec4..f317c92 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -871,6 +871,8 @@
* Perform a radio network scan and return the id of this scan.
*
* @param subId the id of the subscription.
+ * @param renounceFineLocationAccess Set this to true if the caller would not like to
+ * receive fine location related information
* @param request Defines all the configs for network scan.
* @param messenger Callback messages will be sent using this messenger.
* @param binder the binder object instantiated in TelephonyManager.
@@ -878,8 +880,9 @@
* @param callingFeatureId The feature in the package
* @return An id for this scan.
*/
- int requestNetworkScan(int subId, in NetworkScanRequest request, in Messenger messenger,
- in IBinder binder, in String callingPackage, String callingFeatureId);
+ int requestNetworkScan(int subId, in boolean renounceFineLocationAccess,
+ in NetworkScanRequest request, in Messenger messenger, in IBinder binder,
+ in String callingPackage, String callingFeatureId);
/**
* Stop an existing radio network scan.
@@ -1353,12 +1356,17 @@
/**
* Get the service state on specified subscription
* @param subId Subscription id
+ * @param renounceFineLocationAccess Set this to true if the caller would not like to
+ * receive fine location related information
+ * @param renounceCoarseLocationAccess Set this to true if the caller would not like to
+ * receive coarse location related information
* @param callingPackage The package making the call
* @param callingFeatureId The feature in the package
* @return Service state on specified subscription.
*/
- ServiceState getServiceStateForSubscriber(int subId, String callingPackage,
- String callingFeatureId);
+ ServiceState getServiceStateForSubscriber(int subId, boolean renounceFineLocationAccess,
+ boolean renounceCoarseLocationAccess,
+ String callingPackage, String callingFeatureId);
/**
* Returns the URI for the per-account voicemail ringtone set in Phone settings.
@@ -2493,4 +2501,16 @@
* Unregister an IMS connection state callback
*/
void unregisterImsStateCallback(in IImsStateCallback cb);
+
+ /**
+ * return last known cell identity
+ * @param subId user preferred subId.
+ * @param callingPackage the name of the package making the call.
+ * @param callingFeatureId The feature in the package.
+ */
+ CellIdentity getLastKnownCellIdentity(int subId, String callingPackage,
+ String callingFeatureId);
+
+ /** Check if telephony new data stack is enabled. */
+ boolean isUsingNewDataStack();
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 866fd2c..ba95841 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -530,6 +530,8 @@
int RIL_REQUEST_GET_SLICING_CONFIG = 224;
int RIL_REQUEST_ENABLE_VONR = 225;
int RIL_REQUEST_IS_VONR_ENABLED = 226;
+ int RIL_REQUEST_SET_USAGE_SETTING = 227;
+ int RIL_REQUEST_GET_USAGE_SETTING = 228;
/* Responses begin */
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index d1a68d4..c5169e5 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -235,6 +235,7 @@
method @Deprecated public android.content.Intent getLaunchIntentForPackage(String);
method @Deprecated public android.content.Intent getLeanbackLaunchIntentForPackage(String);
method @Deprecated public String getNameForUid(int);
+ method @Deprecated public android.content.pm.PackageInfo getPackageArchiveInfo(String, int);
method @Deprecated public int[] getPackageGids(String) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated public int[] getPackageGids(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated public android.content.pm.PackageInfo getPackageInfo(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index aec80ac..75f1337 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -141,6 +141,10 @@
*
* @param originalLayer Layer that should be visible at the start
* @param newLayer Layer that should be visible at the end
+ * @param ignoreEntriesWithRotationLayer If entries with a visible rotation layer should be ignored
+ * when checking the transition. If true we will not fail the assertion if a rotation layer is
+ * visible to fill the gap between the [originalLayer] being visible and the [newLayer] being
+ * visible.
* @param ignoreSnapshot If the snapshot layer should be ignored during the transition
* (useful mostly for app launch)
* @param ignoreSplashscreen If the splashscreen layer should be ignored during the transition.
@@ -150,20 +154,23 @@
fun FlickerTestParameter.replacesLayer(
originalLayer: FlickerComponentName,
newLayer: FlickerComponentName,
+ ignoreEntriesWithRotationLayer: Boolean = false,
ignoreSnapshot: Boolean = false,
ignoreSplashscreen: Boolean = true
) {
assertLayers {
val assertion = this.isVisible(originalLayer)
- if (ignoreSnapshot || ignoreSplashscreen) {
- assertion.then()
+
+ if (ignoreEntriesWithRotationLayer) {
+ assertion.then().isVisible(FlickerComponentName.ROTATION, isOptional = true)
}
if (ignoreSnapshot) {
- assertion.isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
+ assertion.then().isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
}
if (ignoreSplashscreen) {
- assertion.isSplashScreenVisibleFor(newLayer, isOptional = true)
+ assertion.then().isSplashScreenVisibleFor(newLayer, isOptional = true)
}
+
assertion.then().isVisible(newLayer)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index 5d0d718..3914ef6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -231,7 +231,7 @@
@Test
fun screenLockedStart() {
testSpec.assertLayersStart {
- isVisible(colorFadComponent)
+ isEmpty()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index 62e3fa61..b5c81bb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -166,7 +166,8 @@
* is replaced by [testApp], which remains visible until the end
*/
open fun appLayerReplacesLauncher() {
- testSpec.replacesLayer(LAUNCHER_COMPONENT, testApp.component, ignoreSnapshot = true)
+ testSpec.replacesLayer(LAUNCHER_COMPONENT, testApp.component,
+ ignoreEntriesWithRotationLayer = true, ignoreSnapshot = true)
}
/**
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 1ab59a8..05e8bde 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -106,26 +106,26 @@
* @param files the paths of files which might contain wildcards
*/
private void deleteFiles(String... files) throws Exception {
- boolean found = false;
- for (String file : files) {
- CommandResult result = getDevice().executeShellV2Command("ls " + file);
- if (result.getStatus() == CommandStatus.SUCCESS) {
- found = true;
- break;
+ try {
+ getDevice().enableAdbRoot();
+ boolean found = false;
+ for (String file : files) {
+ CommandResult result = getDevice().executeShellV2Command("ls " + file);
+ if (result.getStatus() == CommandStatus.SUCCESS) {
+ found = true;
+ break;
+ }
}
- }
- if (found) {
- try {
- getDevice().enableAdbRoot();
+ if (found) {
getDevice().remountSystemWritable();
for (String file : files) {
getDevice().executeShellCommand("rm -rf " + file);
}
- } finally {
- getDevice().disableAdbRoot();
+ getDevice().reboot();
}
- getDevice().reboot();
+ } finally {
+ getDevice().disableAdbRoot();
}
}
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index 926bf1b..f06fa81e 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -112,6 +112,10 @@
* @param files the paths of files which might contain wildcards
*/
private void deleteFiles(String... files) throws Exception {
+ if (!getDevice().isAdbRoot()) {
+ getDevice().enableAdbRoot();
+ }
+
boolean found = false;
for (String file : files) {
CommandResult result = getDevice().executeShellV2Command("ls " + file);
@@ -122,9 +126,6 @@
}
if (found) {
- if (!getDevice().isAdbRoot()) {
- getDevice().enableAdbRoot();
- }
getDevice().remountSystemWritable();
for (String file : files) {
getDevice().executeShellCommand("rm -rf " + file);
diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java
index f7d36970..476be44 100644
--- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java
@@ -36,7 +36,7 @@
return new VcnCellUnderlyingNetworkPriority.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(true /* allowMetered */)
- .setAllowedPlmnIds(ALLOWED_PLMN_IDS)
+ .setAllowedOperatorPlmnIds(ALLOWED_PLMN_IDS)
.setAllowedSpecificCarrierIds(ALLOWED_CARRIER_IDS)
.setAllowRoaming(true /* allowRoaming */)
.setRequireOpportunistic(true /* requireOpportunistic */)
@@ -48,7 +48,7 @@
final VcnCellUnderlyingNetworkPriority networkPriority = getTestNetworkPriority();
assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
assertTrue(networkPriority.allowMetered());
- assertEquals(ALLOWED_PLMN_IDS, networkPriority.getAllowedPlmnIds());
+ assertEquals(ALLOWED_PLMN_IDS, networkPriority.getAllowedOperatorPlmnIds());
assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getAllowedSpecificCarrierIds());
assertTrue(networkPriority.allowRoaming());
assertTrue(networkPriority.requireOpportunistic());
@@ -60,7 +60,7 @@
new VcnCellUnderlyingNetworkPriority.Builder().build();
assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
assertFalse(networkPriority.allowMetered());
- assertEquals(new HashSet<String>(), networkPriority.getAllowedPlmnIds());
+ assertEquals(new HashSet<String>(), networkPriority.getAllowedOperatorPlmnIds());
assertEquals(new HashSet<Integer>(), networkPriority.getAllowedSpecificCarrierIds());
assertFalse(networkPriority.allowRoaming());
assertFalse(networkPriority.requireOpportunistic());
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 724c33f..377f526 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -17,6 +17,8 @@
package android.net.vcn;
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
+import static android.net.vcn.VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_PRIORITIES;
+import static android.net.vcn.VcnGatewayConnectionConfig.UNDERLYING_NETWORK_PRIORITIES_KEY;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -30,6 +32,7 @@
import android.net.ipsec.ike.IkeTunnelConnectionParams;
import android.net.vcn.persistablebundleutils.IkeSessionParamsUtilsTest;
import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtilsTest;
+import android.os.PersistableBundle;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -230,6 +233,16 @@
assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle()));
}
+ @Test
+ public void testParsePersistableBundleWithoutVcnUnderlyingNetworkPriorities() {
+ PersistableBundle configBundle = buildTestConfig().toPersistableBundle();
+ configBundle.putPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY, null);
+
+ final VcnGatewayConnectionConfig config = new VcnGatewayConnectionConfig(configBundle);
+ assertEquals(
+ DEFAULT_UNDERLYING_NETWORK_PRIORITIES, config.getVcnUnderlyingNetworkPriorities());
+ }
+
private static IkeTunnelConnectionParams buildTunnelConnectionParams(String ikePsk) {
final IkeSessionParams ikeParams =
IkeSessionParamsUtilsTest.createBuilderMinimum()
@@ -271,4 +284,40 @@
assertNotEquals(tunnelParams, anotherTunnelParams);
assertNotEquals(config, anotherConfig);
}
+
+ private static VcnGatewayConnectionConfig buildTestConfigWithVcnUnderlyingNetworkPriorities(
+ LinkedHashSet<VcnUnderlyingNetworkPriority> networkPriorities) {
+ return buildTestConfigWithExposedCaps(
+ new VcnGatewayConnectionConfig.Builder(
+ "buildTestConfigWithVcnUnderlyingNetworkPriorities",
+ TUNNEL_CONNECTION_PARAMS)
+ .setVcnUnderlyingNetworkPriorities(networkPriorities),
+ EXPOSED_CAPS);
+ }
+
+ @Test
+ public void testVcnUnderlyingNetworkPrioritiesEquality() throws Exception {
+ final VcnGatewayConnectionConfig config =
+ buildTestConfigWithVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_PRIORITIES);
+
+ final LinkedHashSet<VcnUnderlyingNetworkPriority> networkPrioritiesEqual =
+ new LinkedHashSet();
+ networkPrioritiesEqual.add(VcnCellUnderlyingNetworkPriorityTest.getTestNetworkPriority());
+ networkPrioritiesEqual.add(VcnWifiUnderlyingNetworkPriorityTest.getTestNetworkPriority());
+ final VcnGatewayConnectionConfig configEqual =
+ buildTestConfigWithVcnUnderlyingNetworkPriorities(networkPrioritiesEqual);
+
+ final LinkedHashSet<VcnUnderlyingNetworkPriority> networkPrioritiesNotEqual =
+ new LinkedHashSet();
+ networkPrioritiesNotEqual.add(
+ VcnWifiUnderlyingNetworkPriorityTest.getTestNetworkPriority());
+ final VcnGatewayConnectionConfig configNotEqual =
+ buildTestConfigWithVcnUnderlyingNetworkPriorities(networkPrioritiesNotEqual);
+
+ assertEquals(UNDERLYING_NETWORK_PRIORITIES, networkPrioritiesEqual);
+ assertEquals(config, configEqual);
+
+ assertNotEquals(UNDERLYING_NETWORK_PRIORITIES, networkPrioritiesNotEqual);
+ assertNotEquals(config, configNotEqual);
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index 2e1aab6..46a614f 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -279,7 +279,7 @@
final String networkPriorityPlmnId = useMatchedPlmnId ? PLMN_ID : PLMN_ID_OTHER;
final VcnCellUnderlyingNetworkPriority networkPriority =
getCellNetworkPriorityBuilder()
- .setAllowedPlmnIds(Set.of(networkPriorityPlmnId))
+ .setAllowedOperatorPlmnIds(Set.of(networkPriorityPlmnId))
.build();
assertEquals(
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index b46a125..d204190 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -544,6 +544,7 @@
application_action["activity-alias"] = component_action;
application_action["service"] = component_action;
application_action["receiver"] = component_action;
+ application_action["apex-system-service"] = component_action;
// Provider actions.
application_action["provider"] = component_action;
diff --git a/tools/locked_region_code_injection/Android.bp b/tools/locked_region_code_injection/Android.bp
index 98c0e69..3e12971 100644
--- a/tools/locked_region_code_injection/Android.bp
+++ b/tools/locked_region_code_injection/Android.bp
@@ -12,10 +12,10 @@
manifest: "manifest.txt",
srcs: ["src/**/*.java"],
static_libs: [
- "asm-6.0",
- "asm-commons-6.0",
- "asm-tree-6.0",
- "asm-analysis-6.0",
+ "asm-7.0",
+ "asm-commons-7.0",
+ "asm-tree-7.0",
+ "asm-analysis-7.0",
"guava-21.0",
],
}
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetStateAnalysis.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetStateAnalysis.java
index 1002c88..335b3f8 100644
--- a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetStateAnalysis.java
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetStateAnalysis.java
@@ -13,8 +13,6 @@
*/
package lockedregioncodeinjection;
-import java.util.ArrayList;
-import java.util.List;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
@@ -22,6 +20,9 @@
import org.objectweb.asm.tree.analysis.BasicInterpreter;
import org.objectweb.asm.tree.analysis.BasicValue;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* A simple dataflow analysis to determine if the operands on the stack must be one of target lock
* class type.
@@ -31,6 +32,7 @@
private final List<LockTarget> targetLocks;
public LockTargetStateAnalysis(List<LockTarget> targetLocks) {
+ super(Utils.ASM_VERSION);
this.targetLocks = targetLocks;
}
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java
index 219c2b3..f1e84b1 100644
--- a/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java
@@ -13,13 +13,14 @@
*/
package lockedregioncodeinjection;
+import org.objectweb.asm.Opcodes;
+
import java.util.ArrayList;
import java.util.List;
-import org.objectweb.asm.Opcodes;
public class Utils {
- public static final int ASM_VERSION = Opcodes.ASM6;
+ public static final int ASM_VERSION = Opcodes.ASM7;
/**
* Reads a comma separated configuration similar to the Jack definition.
diff --git a/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java b/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java
index c408b9e..31fa0bf 100644
--- a/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java
+++ b/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java
@@ -30,7 +30,7 @@
* rm -fr out/*
*
* # Make booster
- * javac -cp lib/asm-6.0_BETA.jar:lib/asm-commons-6.0_BETA.jar:lib/asm-tree-6.0_BETA.jar:lib/asm-analysis-6.0_BETA.jar:lib/guava-21.0.jar src/*/*.java -d out/
+ * javac -cp lib/asm-7.0_BETA.jar:lib/asm-commons-7.0_BETA.jar:lib/asm-tree-7.0_BETA.jar:lib/asm-analysis-7.0_BETA.jar:lib/guava-21.0.jar src/*/*.java -d out/
* pushd out
* jar cfe lockedregioncodeinjection.jar lockedregioncodeinjection.Main */*.class
* popd
@@ -43,7 +43,7 @@
* popd
*
* # Run tool on unit tests.
- * java -ea -cp lib/asm-6.0_BETA.jar:lib/asm-commons-6.0_BETA.jar:lib/asm-tree-6.0_BETA.jar:lib/asm-analysis-6.0_BETA.jar:lib/guava-21.0.jar:out/lockedregioncodeinjection.jar \
+ * java -ea -cp lib/asm-7.0_BETA.jar:lib/asm-commons-7.0_BETA.jar:lib/asm-tree-7.0_BETA.jar:lib/asm-analysis-7.0_BETA.jar:lib/guava-21.0.jar:out/lockedregioncodeinjection.jar \
* lockedregioncodeinjection.Main \
* -i out/test_input.jar -o out/test_output.jar \
* --targets 'Llockedregioncodeinjection/TestTarget;' \