diff options
452 files changed, 14705 insertions, 7802 deletions
diff --git a/Android.bp b/Android.bp index 11f5c41c73bc..4a1f96ece71d 100644 --- a/Android.bp +++ b/Android.bp @@ -496,7 +496,6 @@ java_library { "//frameworks/base/apex/appsearch/framework", "//frameworks/base/apex/blobstore/framework", "//frameworks/base/apex/jobscheduler/framework", - "//frameworks/base/apex/statsd/service", "//frameworks/base/packages/Tethering/tests/unit", ], } diff --git a/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java b/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java index d7428cf0ab8a..761e9300398a 100644 --- a/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java +++ b/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java @@ -31,6 +31,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; import androidx.test.platform.app.InstrumentationRegistry; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -58,6 +59,12 @@ public class PackageManagerPerfTest { final Context context = InstrumentationRegistry.getInstrumentation().getContext(); } + @Before + public void setup() { + PackageManager.disableApplicationInfoCache(); + PackageManager.disablePackageInfoCache(); + } + @Test @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY) public void testCheckPermissionExists() { diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index a4ab31d7b49a..7a1b4f275ab0 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -1844,7 +1844,7 @@ public class AppStandbyController implements AppStandbyInternal { break; case MSG_REPORT_SYNC_SCHEDULED: - final boolean exempted = msg.arg1 > 0 ? true : false; + final boolean exempted = msg.arg2 > 0 ? true : false; if (exempted) { reportExemptedSyncScheduled((String) msg.obj, msg.arg1); } else { diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java index 11d3a6867ac3..566f4cdca6da 100644 --- a/apex/media/framework/java/android/media/MediaParser.java +++ b/apex/media/framework/java/android/media/MediaParser.java @@ -1114,20 +1114,22 @@ public final class MediaParser { static { // Using a LinkedHashMap to keep the insertion order when iterating over the keys. LinkedHashMap<String, ExtractorFactory> extractorFactoriesByName = new LinkedHashMap<>(); - extractorFactoriesByName.put("exo.Ac3Parser", Ac3Extractor::new); - extractorFactoriesByName.put("exo.Ac4Parser", Ac4Extractor::new); - extractorFactoriesByName.put("exo.AdtsParser", AdtsExtractor::new); - extractorFactoriesByName.put("exo.AmrParser", AmrExtractor::new); - extractorFactoriesByName.put("exo.FlacParser", FlacExtractor::new); - extractorFactoriesByName.put("exo.FlvParser", FlvExtractor::new); - extractorFactoriesByName.put("exo.FragmentedMp4Parser", FragmentedMp4Extractor::new); + // Parsers are ordered to match ExoPlayer's DefaultExtractorsFactory extractor ordering, + // which in turn aims to minimize the chances of incorrect extractor selections. extractorFactoriesByName.put("exo.MatroskaParser", MatroskaExtractor::new); - extractorFactoriesByName.put("exo.Mp3Parser", Mp3Extractor::new); + extractorFactoriesByName.put("exo.FragmentedMp4Parser", FragmentedMp4Extractor::new); extractorFactoriesByName.put("exo.Mp4Parser", Mp4Extractor::new); + extractorFactoriesByName.put("exo.Mp3Parser", Mp3Extractor::new); + extractorFactoriesByName.put("exo.AdtsParser", AdtsExtractor::new); + extractorFactoriesByName.put("exo.Ac3Parser", Ac3Extractor::new); + extractorFactoriesByName.put("exo.TsParser", TsExtractor::new); + extractorFactoriesByName.put("exo.FlvParser", FlvExtractor::new); extractorFactoriesByName.put("exo.OggParser", OggExtractor::new); extractorFactoriesByName.put("exo.PsParser", PsExtractor::new); - extractorFactoriesByName.put("exo.TsParser", TsExtractor::new); extractorFactoriesByName.put("exo.WavParser", WavExtractor::new); + extractorFactoriesByName.put("exo.AmrParser", AmrExtractor::new); + extractorFactoriesByName.put("exo.Ac4Parser", Ac4Extractor::new); + extractorFactoriesByName.put("exo.FlacParser", FlacExtractor::new); EXTRACTOR_FACTORIES_BY_NAME = Collections.unmodifiableMap(extractorFactoriesByName); HashMap<String, Class> expectedTypeByParameterName = new HashMap<>(); diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java index 205ffc297945..30a8b458cce5 100644 --- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java +++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java @@ -246,7 +246,7 @@ public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPers permissionState.isGranted() && (permissionState.getFlags() & PackageManager.FLAG_PERMISSION_ONE_TIME) == 0)); serializer.attribute(null, ATTRIBUTE_FLAGS, Integer.toHexString( - permissionState.getFlags() & ~PackageManager.FLAG_PERMISSION_ONE_TIME)); + permissionState.getFlags())); serializer.endTag(null, TAG_PERMISSION); } } diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java index cb167c30e30f..dc61f2aedf99 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java @@ -21,11 +21,13 @@ import android.app.AlarmManager; import android.app.AlarmManager.OnAlarmListener; import android.app.StatsManager; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -85,12 +87,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { public static final int DEATH_THRESHOLD = 10; - // TODO(b/149090705): Implement an alternative to sending broadcast with @hide flag - // FLAG_RECEIVER_INCLUDE_BACKGROUND. Instead of using the flag, find the - // list of registered broadcast receivers and send them directed broadcasts - // to wake them up. See b/147374337. - private static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000; - static final class CompanionHandler extends Handler { CompanionHandler(Looper looper) { super(looper); @@ -498,9 +494,25 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { Log.d(TAG, "learned that statsdReady"); } sayHiToStatsd(); // tell statsd that we're ready too and link to it - mContext.sendBroadcastAsUser(new Intent(StatsManager.ACTION_STATSD_STARTED) - .addFlags(FLAG_RECEIVER_INCLUDE_BACKGROUND), - UserHandle.SYSTEM, android.Manifest.permission.DUMP); + + final Intent intent = new Intent(StatsManager.ACTION_STATSD_STARTED); + // Retrieve list of broadcast receivers for this broadcast & send them directed broadcasts + // to wake them up (if they're in background). + List<ResolveInfo> resolveInfos = + mContext.getPackageManager().queryBroadcastReceiversAsUser( + intent, 0, UserHandle.SYSTEM); + if (resolveInfos == null || resolveInfos.isEmpty()) { + return; // No need to send broadcast. + } + + for (ResolveInfo resolveInfo : resolveInfos) { + Intent intentToSend = new Intent(intent); + intentToSend.setComponent(new ComponentName( + resolveInfo.activityInfo.applicationInfo.packageName, + resolveInfo.activityInfo.name)); + mContext.sendBroadcastAsUser(intentToSend, UserHandle.SYSTEM, + android.Manifest.permission.DUMP); + } } @Override diff --git a/api/current.txt b/api/current.txt index aaaf2f278f90..1bd3aa7997b9 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5943,7 +5943,7 @@ package android.app { method public boolean isImportantConversation(); method public void setAllowBubbles(boolean); method public void setBypassDnd(boolean); - method public void setConversationId(@Nullable String, @Nullable String); + method public void setConversationId(@NonNull String, @NonNull String); method public void setDescription(String); method public void setGroup(String); method public void setImportance(int); @@ -5956,7 +5956,6 @@ package android.app { method public boolean shouldShowLights(); method public boolean shouldVibrate(); method public void writeToParcel(android.os.Parcel, int); - field public static final String CONVERSATION_CHANNEL_ID_FORMAT = "%1$s : %2$s"; field @NonNull public static final android.os.Parcelable.Creator<android.app.NotificationChannel> CREATOR; field public static final String DEFAULT_CHANNEL_ID = "miscellaneous"; } @@ -8190,6 +8189,7 @@ package android.appwidget { field public static final String OPTION_APPWIDGET_MAX_WIDTH = "appWidgetMaxWidth"; field public static final String OPTION_APPWIDGET_MIN_HEIGHT = "appWidgetMinHeight"; field public static final String OPTION_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth"; + field public static final String OPTION_APPWIDGET_RESTORE_COMPLETED = "appWidgetRestoreCompleted"; } public class AppWidgetProvider extends android.content.BroadcastReceiver { @@ -29357,9 +29357,9 @@ package android.media.tv { public abstract class TvInputService extends android.app.Service { ctor public TvInputService(); method public final android.os.IBinder onBind(android.content.Intent); - method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(String); + method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(@NonNull String); method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(@NonNull String, @NonNull String); - method @Nullable public abstract android.media.tv.TvInputService.Session onCreateSession(String); + method @Nullable public abstract android.media.tv.TvInputService.Session onCreateSession(@NonNull String); method @Nullable public android.media.tv.TvInputService.Session onCreateSession(@NonNull String, @NonNull String); field public static final int PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND = 100; // 0x64 field public static final int PRIORITY_HINT_USE_CASE_TYPE_LIVE = 400; // 0x190 @@ -48171,6 +48171,7 @@ package android.telephony { method public String getMmsUserAgent(); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNai(); method public String getNetworkCountryIso(); + method @NonNull public String getNetworkCountryIso(int); method public String getNetworkOperator(); method public String getNetworkOperatorName(); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getNetworkSelectionMode(); diff --git a/api/system-current.txt b/api/system-current.txt index 358ca22b1dd1..eb5fcc052c2c 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4944,30 +4944,32 @@ package android.media.tv.tuner { package android.media.tv.tuner.dvr { - public class Dvr implements java.lang.AutoCloseable { - ctor protected Dvr(int); + public class DvrPlayback implements java.lang.AutoCloseable { method public int attachFilter(@NonNull android.media.tv.tuner.filter.Filter); method public void close(); method public int configure(@NonNull android.media.tv.tuner.dvr.DvrSettings); method public int detachFilter(@NonNull android.media.tv.tuner.filter.Filter); method public int flush(); + method public long read(long); + method public long read(@NonNull byte[], long, long); method public void setFileDescriptor(@NonNull android.os.ParcelFileDescriptor); method public int start(); method public int stop(); - field public static final int TYPE_PLAYBACK = 1; // 0x1 - field public static final int TYPE_RECORD = 0; // 0x0 - } - - public class DvrPlayback extends android.media.tv.tuner.dvr.Dvr { - method public long read(long); - method public long read(@NonNull byte[], long, long); field public static final int PLAYBACK_STATUS_ALMOST_EMPTY = 2; // 0x2 field public static final int PLAYBACK_STATUS_ALMOST_FULL = 4; // 0x4 field public static final int PLAYBACK_STATUS_EMPTY = 1; // 0x1 field public static final int PLAYBACK_STATUS_FULL = 8; // 0x8 } - public class DvrRecorder extends android.media.tv.tuner.dvr.Dvr { + public class DvrRecorder implements java.lang.AutoCloseable { + method public int attachFilter(@NonNull android.media.tv.tuner.filter.Filter); + method public void close(); + method public int configure(@NonNull android.media.tv.tuner.dvr.DvrSettings); + method public int detachFilter(@NonNull android.media.tv.tuner.filter.Filter); + method public int flush(); + method public void setFileDescriptor(@NonNull android.os.ParcelFileDescriptor); + method public int start(); + method public int stop(); method public long write(long); method public long write(@NonNull byte[], long, long); } @@ -7198,9 +7200,9 @@ package android.net.wifi { } public final class SoftApCapability implements android.os.Parcelable { + method public boolean areFeaturesSupported(long); method public int describeContents(); method public int getMaxSupportedClients(); - method public boolean isFeatureSupported(long); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApCapability> CREATOR; field public static final long SOFTAP_FEATURE_ACS_OFFLOAD = 1L; // 0x1L @@ -7518,7 +7520,7 @@ package android.net.wifi { } public static interface WifiManager.ScoreChangeCallback { - method public void onScoreChange(int, @NonNull android.net.NetworkScore); + method public void onScoreChange(int, int); method public void onTriggerUpdateOfWifiUsabilityStats(int); } @@ -7544,50 +7546,28 @@ package android.net.wifi { method public void stop(int); } - public class WifiNetworkConnectionStatistics implements android.os.Parcelable { - ctor public WifiNetworkConnectionStatistics(int, int); - ctor public WifiNetworkConnectionStatistics(); - ctor public WifiNetworkConnectionStatistics(android.net.wifi.WifiNetworkConnectionStatistics); - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiNetworkConnectionStatistics> CREATOR; - field public int numConnection; - field public int numUsage; - } - - public final class WifiNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { - method public boolean satisfiedBy(android.net.NetworkSpecifier); - } - - public final class WifiNetworkSuggestion implements android.os.Parcelable { - method @NonNull public android.net.wifi.WifiConfiguration getWifiConfiguration(); - } - - public static final class WifiNetworkSuggestion.Builder { - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public android.net.wifi.WifiNetworkSuggestion.Builder setCarrierId(int); - } - - public final class WifiOemMigrationHook { - method @Nullable public static android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData loadFromConfigStore(); - method @NonNull public static android.net.wifi.WifiOemMigrationHook.SettingsMigrationData loadFromSettings(@NonNull android.content.Context); + public final class WifiMigration { + method @Nullable public static android.net.wifi.WifiMigration.ConfigStoreMigrationData loadFromConfigStore(); + method @NonNull public static android.net.wifi.WifiMigration.SettingsMigrationData loadFromSettings(@NonNull android.content.Context); + method public static void removeConfigStore(); } - public static final class WifiOemMigrationHook.ConfigStoreMigrationData implements android.os.Parcelable { + public static final class WifiMigration.ConfigStoreMigrationData implements android.os.Parcelable { method public int describeContents(); method @Nullable public java.util.List<android.net.wifi.WifiConfiguration> getUserSavedNetworkConfigurations(); method @Nullable public android.net.wifi.SoftApConfiguration getUserSoftApConfiguration(); method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData> CREATOR; + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiMigration.ConfigStoreMigrationData> CREATOR; } - public static final class WifiOemMigrationHook.ConfigStoreMigrationData.Builder { - ctor public WifiOemMigrationHook.ConfigStoreMigrationData.Builder(); - method @NonNull public android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData build(); - method @NonNull public android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData.Builder setUserSavedNetworkConfigurations(@NonNull java.util.List<android.net.wifi.WifiConfiguration>); - method @NonNull public android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData.Builder setUserSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration); + public static final class WifiMigration.ConfigStoreMigrationData.Builder { + ctor public WifiMigration.ConfigStoreMigrationData.Builder(); + method @NonNull public android.net.wifi.WifiMigration.ConfigStoreMigrationData build(); + method @NonNull public android.net.wifi.WifiMigration.ConfigStoreMigrationData.Builder setUserSavedNetworkConfigurations(@NonNull java.util.List<android.net.wifi.WifiConfiguration>); + method @NonNull public android.net.wifi.WifiMigration.ConfigStoreMigrationData.Builder setUserSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration); } - public static final class WifiOemMigrationHook.SettingsMigrationData implements android.os.Parcelable { + public static final class WifiMigration.SettingsMigrationData implements android.os.Parcelable { method public int describeContents(); method @Nullable public String getP2pDeviceName(); method public boolean isP2pFactoryResetPending(); @@ -7597,19 +7577,42 @@ package android.net.wifi { method public boolean isVerboseLoggingEnabled(); method public boolean isWakeUpEnabled(); method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiOemMigrationHook.SettingsMigrationData> CREATOR; + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiMigration.SettingsMigrationData> CREATOR; + } + + public static final class WifiMigration.SettingsMigrationData.Builder { + ctor public WifiMigration.SettingsMigrationData.Builder(); + method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData build(); + method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setP2pDeviceName(@Nullable String); + method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setP2pFactoryResetPending(boolean); + method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setScanAlwaysAvailable(boolean); + method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setScanThrottleEnabled(boolean); + method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setSoftApTimeoutEnabled(boolean); + method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setVerboseLoggingEnabled(boolean); + method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setWakeUpEnabled(boolean); + } + + public class WifiNetworkConnectionStatistics implements android.os.Parcelable { + ctor public WifiNetworkConnectionStatistics(int, int); + ctor public WifiNetworkConnectionStatistics(); + ctor public WifiNetworkConnectionStatistics(android.net.wifi.WifiNetworkConnectionStatistics); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiNetworkConnectionStatistics> CREATOR; + field public int numConnection; + field public int numUsage; } - public static final class WifiOemMigrationHook.SettingsMigrationData.Builder { - ctor public WifiOemMigrationHook.SettingsMigrationData.Builder(); - method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData build(); - method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setP2pDeviceName(@Nullable String); - method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setP2pFactoryResetPending(boolean); - method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setScanAlwaysAvailable(boolean); - method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setScanThrottleEnabled(boolean); - method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setSoftApTimeoutEnabled(boolean); - method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setVerboseLoggingEnabled(boolean); - method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setWakeUpEnabled(boolean); + public final class WifiNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { + method public boolean satisfiedBy(android.net.NetworkSpecifier); + } + + public final class WifiNetworkSuggestion implements android.os.Parcelable { + method @NonNull public android.net.wifi.WifiConfiguration getWifiConfiguration(); + } + + public static final class WifiNetworkSuggestion.Builder { + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public android.net.wifi.WifiNetworkSuggestion.Builder setCarrierId(int); } public class WifiScanner { @@ -11727,7 +11730,6 @@ package android.telephony { method public int getMaxNumberOfSimultaneouslyActiveSims(); method public static long getMaxNumberVerificationTimeoutMillis(); method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getMergedImsisFromGroup(); - method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getNetworkCountryIso(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmask(); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState(); method public int getSimApplicationState(); @@ -11785,7 +11787,6 @@ package android.telephony { method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAlwaysAllowMmsData(boolean); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAlwaysReportSignalStrength(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallForwarding(@NonNull android.telephony.CallForwardingInfo); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallWaitingStatus(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean); @@ -11800,7 +11801,6 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(@NonNull String, int, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPolicyDataEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRadioEnabled(boolean); diff --git a/api/test-current.txt b/api/test-current.txt index 2d15c0ea0ffb..0f8694f7435b 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -118,7 +118,6 @@ package android.app { method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException; method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void requestPictureInPictureMode(@NonNull android.os.IBinder); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeDockedStack(android.graphics.Rect, android.graphics.Rect); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizePinnedStack(int, android.graphics.Rect, boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeTask(int, android.graphics.Rect); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setDisplayToSingleTaskInstance(int); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException; @@ -413,6 +412,12 @@ package android.app { field public static final String COLUMN_MEDIASTORE_URI = "mediastore_uri"; } + public class DreamManager { + method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void setActiveDream(@NonNull android.content.ComponentName); + method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void startDream(@NonNull android.content.ComponentName); + method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void stopDream(); + } + public final class NotificationChannel implements android.os.Parcelable { method public int getOriginalImportance(); method public boolean isBlockableSystem(); @@ -792,6 +797,7 @@ package android.content { field public static final String BUGREPORT_SERVICE = "bugreport"; field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture"; field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle"; + field public static final String DREAM_SERVICE = "dream"; field public static final String ETHERNET_SERVICE = "ethernet"; field public static final String NETWORK_STACK_SERVICE = "network_stack"; field public static final String PERMISSION_SERVICE = "permission"; @@ -975,6 +981,14 @@ package android.content.pm { field @Nullable public final String backgroundPermission; } + public final class ProviderInfoList implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public static android.content.pm.ProviderInfoList fromList(@NonNull java.util.List<android.content.pm.ProviderInfo>); + method @NonNull public java.util.List<android.content.pm.ProviderInfo> getList(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ProviderInfoList> CREATOR; + } + public final class ShortcutInfo implements android.os.Parcelable { method public boolean isVisibleToPublisher(); } @@ -2463,7 +2477,9 @@ package android.os { } public final class Parcel { + method public boolean allowSquashing(); method public int readExceptionCode(); + method public void restoreAllowSquashing(boolean); } public class ParcelFileDescriptor implements java.io.Closeable android.os.Parcelable { @@ -3753,7 +3769,6 @@ package android.telephony { method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context); method public int getEmergencyNumberDbVersion(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag(); - method @NonNull @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNetworkCountryIso(int); method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion(); method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile(); diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp index 95de6c506795..1e200c52a207 100644 --- a/cmds/hid/jni/com_android_commands_hid_Device.cpp +++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp @@ -146,6 +146,8 @@ std::unique_ptr<Device> Device::open(int32_t id, const char* name, int32_t vid, struct uhid_event ev = {}; ev.type = UHID_CREATE2; strlcpy(reinterpret_cast<char*>(ev.u.create2.name), name, sizeof(ev.u.create2.name)); + std::string uniq = android::base::StringPrintf("Id: %d", id); + strlcpy(reinterpret_cast<char*>(ev.u.create2.uniq), uniq.c_str(), sizeof(ev.u.create2.uniq)); memcpy(&ev.u.create2.rd_data, descriptor.data(), size * sizeof(ev.u.create2.rd_data[0])); ev.u.create2.rd_size = size; ev.u.create2.bus = bus; diff --git a/cmds/idmap2/idmap2/CreateMultiple.cpp b/cmds/idmap2/idmap2/CreateMultiple.cpp index 0b0541fb6221..d4e888fd3119 100644 --- a/cmds/idmap2/idmap2/CreateMultiple.cpp +++ b/cmds/idmap2/idmap2/CreateMultiple.cpp @@ -31,6 +31,7 @@ #include "idmap2/Idmap.h" #include "idmap2/Policies.h" #include "idmap2/SysTrace.h" +#include "Commands.h" using android::ApkAssets; using android::base::StringPrintf; @@ -105,32 +106,34 @@ Result<Unit> CreateMultiple(const std::vector<std::string>& args) { continue; } - const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); - if (!overlay_apk) { - LOG(WARNING) << "failed to load apk " << overlay_apk_path.c_str(); - continue; - } - - const auto idmap = - Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, !ignore_overlayable); - if (!idmap) { - LOG(WARNING) << "failed to create idmap"; - continue; - } - - umask(kIdmapFilePermissionMask); - std::ofstream fout(idmap_path); - if (fout.fail()) { - LOG(WARNING) << "failed to open idmap path " << idmap_path.c_str(); - continue; - } - - BinaryStreamVisitor visitor(fout); - (*idmap)->accept(&visitor); - fout.close(); - if (fout.fail()) { - LOG(WARNING) << "failed to write to idmap path %s" << idmap_path.c_str(); - continue; + if (!Verify(std::vector<std::string>({"--idmap-path", idmap_path}))) { + const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); + if (!overlay_apk) { + LOG(WARNING) << "failed to load apk " << overlay_apk_path.c_str(); + continue; + } + + const auto idmap = + Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, !ignore_overlayable); + if (!idmap) { + LOG(WARNING) << "failed to create idmap"; + continue; + } + + umask(kIdmapFilePermissionMask); + std::ofstream fout(idmap_path); + if (fout.fail()) { + LOG(WARNING) << "failed to open idmap path " << idmap_path.c_str(); + continue; + } + + BinaryStreamVisitor visitor(fout); + (*idmap)->accept(&visitor); + fout.close(); + if (fout.fail()) { + LOG(WARNING) << "failed to write to idmap path %s" << idmap_path.c_str(); + continue; + } } idmap_paths.emplace_back(idmap_path); diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index 2229e1c7ca07..d79123b6a972 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -20,9 +20,9 @@ #include <dirent.h> #include <errno.h> - #include <mutex> #include <set> +#include <thread> #include <android-base/file.h> #include <android-base/properties.h> @@ -42,6 +42,7 @@ #include "frameworks/base/core/proto/android/os/backtrace.proto.h" #include "frameworks/base/core/proto/android/os/data.proto.h" #include "frameworks/base/core/proto/android/util/log.proto.h" +#include "frameworks/base/core/proto/android/util/textdump.proto.h" #include "incidentd_util.h" namespace android { @@ -135,7 +136,7 @@ status_t FileSection::Execute(ReportWriter* writer) const { status_t ihStatus = wait_child(pid); if (ihStatus != NO_ERROR) { ALOGW("[%s] abnormal child process: %s", this->name.string(), strerror(-ihStatus)); - return ihStatus; + return OK; // Not a fatal error. } return writer->writeSection(buffer); @@ -234,7 +235,7 @@ struct WorkerThreadData : public virtual RefBase { Fpipe pipe; // Lock protects these fields - mutex lock; + std::mutex lock; bool workerDone; status_t workerError; @@ -261,83 +262,47 @@ void sigpipe_handler(int signum) { } } -static void* worker_thread_func(void* cookie) { - // Don't crash the service if we write to a closed pipe (which can happen if - // dumping times out). - signal(SIGPIPE, sigpipe_handler); - - WorkerThreadData* data = (WorkerThreadData*)cookie; - status_t err = data->section->BlockingCall(data->pipe.writeFd()); - - { - unique_lock<mutex> lock(data->lock); - data->workerDone = true; - data->workerError = err; - } - - data->pipe.writeFd().reset(); - data->decStrong(data->section); - // data might be gone now. don't use it after this point in this thread. - return NULL; -} - status_t WorkerThreadSection::Execute(ReportWriter* writer) const { status_t err = NO_ERROR; - pthread_t thread; - pthread_attr_t attr; bool workerDone = false; FdBuffer buffer; - // Data shared between this thread and the worker thread. - sp<WorkerThreadData> data = new WorkerThreadData(this); - - // Create the pipe - if (!data->pipe.init()) { + // Create shared data and pipe + WorkerThreadData data(this); + if (!data.pipe.init()) { return -errno; } - // Create the thread - err = pthread_attr_init(&attr); - if (err != 0) { - return -err; - } - // TODO: Do we need to tweak thread priority? - err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - if (err != 0) { - pthread_attr_destroy(&attr); - return -err; - } - - // The worker thread needs a reference and we can't let the count go to zero - // if that thread is slow to start. - data->incStrong(this); - - err = pthread_create(&thread, &attr, worker_thread_func, (void*)data.get()); - pthread_attr_destroy(&attr); - if (err != 0) { - data->decStrong(this); - return -err; - } + std::thread([&]() { + // Don't crash the service if writing to a closed pipe (may happen if dumping times out) + signal(SIGPIPE, sigpipe_handler); + status_t err = data.section->BlockingCall(data.pipe.writeFd()); + { + std::unique_lock<std::mutex> lock(data.lock); + data.workerDone = true; + data.workerError = err; + // unique_fd is not thread safe. If we don't lock it, reset() may pause half way while + // the other thread executes to the end, calling ~Fpipe, which is a race condition. + data.pipe.writeFd().reset(); + } + }).detach(); // Loop reading until either the timeout or the worker side is done (i.e. eof). - err = buffer.read(data->pipe.readFd().get(), this->timeoutMs); + err = buffer.read(data.pipe.readFd().get(), this->timeoutMs); if (err != NO_ERROR) { ALOGE("[%s] reader failed with error '%s'", this->name.string(), strerror(-err)); } - // Done with the read fd. The worker thread closes the write one so - // we never race and get here first. - data->pipe.readFd().reset(); - // If the worker side is finished, then return its error (which may overwrite // our possible error -- but it's more interesting anyway). If not, then we timed out. { - unique_lock<mutex> lock(data->lock); - if (data->workerError != NO_ERROR) { - err = data->workerError; + std::unique_lock<std::mutex> lock(data.lock); + data.pipe.close(); + if (data.workerError != NO_ERROR) { + err = data.workerError; ALOGE("[%s] worker failed with error '%s'", this->name.string(), strerror(-err)); } - workerDone = data->workerDone; + workerDone = data.workerDone; } writer->setSectionStats(buffer); @@ -473,6 +438,77 @@ status_t DumpsysSection::BlockingCall(unique_fd& pipeWriteFd) const { } // ================================================================================ +TextDumpsysSection::TextDumpsysSection(int id, const char* service, ...) + : WorkerThreadSection(id, REMOTE_CALL_TIMEOUT_MS), mService(service) { + name = "dumpsys "; + name += service; + + va_list args; + va_start(args, service); + while (true) { + const char* arg = va_arg(args, const char*); + if (arg == NULL) { + break; + } + mArgs.add(String16(arg)); + name += " "; + name += arg; + } + va_end(args); +} + +TextDumpsysSection::~TextDumpsysSection() {} + +status_t TextDumpsysSection::BlockingCall(unique_fd& pipeWriteFd) const { + // checkService won't wait for the service to show up like getService will. + sp<IBinder> service = defaultServiceManager()->checkService(mService); + if (service == NULL) { + ALOGW("TextDumpsysSection: Can't lookup service: %s", String8(mService).string()); + return NAME_NOT_FOUND; + } + + // Create pipe + Fpipe dumpPipe; + if (!dumpPipe.init()) { + ALOGW("[%s] failed to setup pipe", this->name.string()); + return -errno; + } + + // Run dumping thread + const uint64_t start = Nanotime(); + std::thread worker([&]() { + // Don't crash the service if writing to a closed pipe (may happen if dumping times out) + signal(SIGPIPE, sigpipe_handler); + status_t err = service->dump(dumpPipe.writeFd().get(), mArgs); + if (err != OK) { + ALOGW("[%s] dump thread failed. Error: %s", this->name.string(), strerror(-err)); + } + dumpPipe.writeFd().reset(); + }); + + // Collect dump content + std::string content; + bool success = ReadFdToString(dumpPipe.readFd(), &content); + worker.join(); // Wait for worker to finish + dumpPipe.readFd().reset(); + if (!success) { + ALOGW("[%s] failed to read data from pipe", this->name.string()); + return -1; + } + + ProtoOutputStream proto; + proto.write(util::TextDumpProto::COMMAND, std::string(name.string())); + proto.write(util::TextDumpProto::CONTENT, content); + proto.write(util::TextDumpProto::DUMP_DURATION_NS, int64_t(Nanotime() - start)); + + if (!proto.flush(pipeWriteFd.get()) && errno == EPIPE) { + ALOGE("[%s] wrote to a broken pipe\n", this->name.string()); + return EPIPE; + } + return OK; +} + +// ================================================================================ // initialization only once in Section.cpp. map<log_id_t, log_time> LogSection::gLastLogsRetrieved; diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h index 0bb9da9aac5b..6162b3ae2ceb 100644 --- a/cmds/incidentd/src/Section.h +++ b/cmds/incidentd/src/Section.h @@ -112,7 +112,8 @@ private: }; /** - * Section that calls dumpsys on a system service. + * Section that calls protobuf dumpsys on a system service, usually + * "dumpsys [service_name] --proto". */ class DumpsysSection : public WorkerThreadSection { public: @@ -127,6 +128,21 @@ private: }; /** + * Section that calls text dumpsys on a system service, usually "dumpsys [service_name]". + */ +class TextDumpsysSection : public WorkerThreadSection { +public: + TextDumpsysSection(int id, const char* service, ...); + virtual ~TextDumpsysSection(); + + virtual status_t BlockingCall(unique_fd& pipeWriteFd) const; + +private: + String16 mService; + Vector<String16> mArgs; +}; + +/** * Section that calls dumpsys on a system service. */ class SystemPropertyDumpsysSection : public WorkerThreadSection { diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 1b0e51e28b59..f36b855faa41 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -393,11 +393,13 @@ message Atom { WifiConnectionResultReported wifi_connection_result_reported = 253 [(module) = "wifi"]; AppFreezeChanged app_freeze_changed = 254 [(module) = "framework"]; SnapshotMergeReported snapshot_merge_reported = 255; + ForegroundServiceAppOpSessionEnded foreground_service_app_op_session_ended = + 256 [(module) = "framework"]; SdkExtensionStatus sdk_extension_status = 354; } // Pulled events will start at field 10000. - // Next: 10075 + // Next: 10076 oneof pulled { WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"]; WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"]; @@ -479,6 +481,7 @@ message Atom { PackageNotificationChannelGroupPreferences package_notification_channel_group_preferences = 10073 [(module) = "framework"]; GnssStats gnss_stats = 10074 [(module) = "framework"]; + AppFeaturesOps app_features_ops = 10075 [(module) = "framework"]; } // DO NOT USE field numbers above 100,000 in AOSP. @@ -3284,12 +3287,12 @@ message OverlayStateChanged { ]; } -/* +/** * Logs foreground service starts and stops. * Note that this is not when a service starts or stops, but when it is * considered foreground. * Logged from - * //frameworks/base/services/core/java/com/android/server/am/ActiveServices.java + * frameworks/base/services/core/java/com/android/server/am/ActiveServices.java */ message ForegroundServiceStateChanged { optional int32 uid = 1 [(is_uid) = true]; @@ -3301,6 +3304,49 @@ message ForegroundServiceStateChanged { EXIT = 2; } optional State state = 3; + + // Whether the fgs is allowed while-in-use permissions, i.e. is considered 'in-use' to the user. + // (If the fgs was started while the app wasn't TOP it usually will be denied these permissions) + optional bool allow_while_in_use_permission = 4; +} + +/** + * Logs the number of times a uid accesses a sensitive AppOp during a foreground service session. + * A foreground service session is any continuous period during which the uid holds at least one + * foreground service; the atom will be pushed when the uid no longer holds any foreground services. + * Accesses initiated while the uid is in the TOP state are ignored. + * Sessions with no attempted accesses are not logged. + * Logged from + * frameworks/base/services/core/java/com/android/server/am/ActiveServices.java + */ +message ForegroundServiceAppOpSessionEnded { + optional int32 uid = 1 [(is_uid) = true]; + + // The operation's name. + // To the extent possible, preserve the mapping from AppOpsManager.OP_ constants. + // Only these named ops are actually logged. + enum AppOpName { + OP_NONE = -1; // Also represents UNKNOWN. + OP_COARSE_LOCATION = 0; + OP_FINE_LOCATION = 1; + OP_CAMERA = 26; + OP_RECORD_AUDIO = 27; + } + optional AppOpName app_op_name = 2 [default = OP_NONE]; + + // The uid's permission mode for accessing the AppOp during this fgs session. + enum Mode { + MODE_UNKNOWN = 0; + MODE_ALLOWED = 1; // Always allowed + MODE_IGNORED = 2; // Denied + MODE_FOREGROUND = 3; // Allow-while-in-use (or allowed-one-time) + } + optional Mode app_op_mode = 3; + + // Number of times this AppOp was requested and allowed. + optional int32 count_ops_accepted = 4; + // Number of times this AppOp was requested but denied. + optional int32 count_ops_rejected = 5; } /** @@ -7548,6 +7594,51 @@ message AppOps { } /** + * Historical app ops data per package and features. + */ +message AppFeaturesOps { + // Uid of the package requesting the op + optional int32 uid = 1 [(is_uid) = true]; + + // Name of the package performing the op + optional string package_name = 2; + + // feature id; provided by developer when accessing related API, limited at 50 chars by API. + // Features must be provided through manifest using <feature> tag available in R and above. + optional string feature_id = 3; + + // operation id; maps to the OPSTR_* constants in AppOpsManager.java + optional string op = 4; + + // The number of times the op was granted while the app was in the + // foreground (only for trusted requests) + optional int64 trusted_foreground_granted_count = 5; + + // The number of times the op was granted while the app was in the + // background (only for trusted requests) + optional int64 trusted_background_granted_count = 6; + + // The number of times the op was rejected while the app was in the + // foreground (only for trusted requests) + optional int64 trusted_foreground_rejected_count = 7; + + // The number of times the op was rejected while the app was in the + // background (only for trusted requests) + optional int64 trusted_background_rejected_count = 8; + + // For long-running operations, total duration of the operation + // while the app was in the foreground (only for trusted requests) + optional int64 trusted_foreground_duration_millis = 9; + + // For long-running operations, total duration of the operation + // while the app was in the background (only for trusted requests) + optional int64 trusted_background_duration_millis = 10; + + // Whether AppOps is guarded by Runtime permission + optional bool is_runtime_permission = 11; +} + +/** * Location Manager API Usage information(e.g. API under usage, * API call's parameters). * Logged from: @@ -8143,7 +8234,7 @@ message FrameTimingHistogram { */ message CameraActionEvent { // Camera session duration - optional int64 duration = 1; + optional int64 duration_millis = 1; // Camera API level used optional int32 api_level = 2; diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index 3b0667d1e377..316a018ef214 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -22,6 +22,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.StringRes; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; @@ -61,6 +62,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.regex.Pattern; /** * This class describes an {@link AccessibilityService}. The system notifies an @@ -552,6 +554,13 @@ public class AccessibilityServiceInfo implements Parcelable { */ private int mHtmlDescriptionRes; + // Used for html description of accessibility service. The <img> src tag must follow the + // prefix rule. e.g. <img src="R.drawable.fileName"/> + private static final String IMG_PREFIX = "R.drawable."; + private static final String ANCHOR_TAG = "a"; + private static final List<String> UNSUPPORTED_TAG_LIST = new ArrayList<>( + Collections.singletonList(ANCHOR_TAG)); + /** * Creates a new instance. */ @@ -782,12 +791,10 @@ public class AccessibilityServiceInfo implements Parcelable { } /** - * The animated image resource id. - * <p> - * <strong>Statically set from - * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> - * </p> + * Gets the animated image resource id. + * * @return The animated image resource id. + * * @hide */ public int getAnimatedImageRes() { @@ -797,10 +804,12 @@ public class AccessibilityServiceInfo implements Parcelable { /** * The animated image drawable. * <p> + * Image can not exceed the screen size. * <strong>Statically set from * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> * </p> - * @return The animated image drawable. + * @return The animated image drawable, or null if the resource is invalid or the image + * exceed the screen size. */ @Nullable public Drawable loadAnimatedImage(@NonNull Context context) { @@ -808,11 +817,8 @@ public class AccessibilityServiceInfo implements Parcelable { return null; } - final PackageManager packageManager = context.getPackageManager(); - final String packageName = mComponentName.getPackageName(); - final ApplicationInfo applicationInfo = mResolveInfo.serviceInfo.applicationInfo; - - return packageManager.getDrawable(packageName, mAnimatedImageRes, applicationInfo); + return loadSafeAnimatedImage(context, mResolveInfo.serviceInfo.applicationInfo, + mAnimatedImageRes); } /** @@ -924,16 +930,17 @@ public class AccessibilityServiceInfo implements Parcelable { } /** - * The localized html description of the accessibility service. + * The localized and restricted html description of the accessibility service. * <p> + * Filters the <img> tag which do not meet the custom specification and the <a> tag. * <strong>Statically set from * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> * </p> - * @return The localized html description. + * @return The localized and restricted html description. */ @Nullable public String loadHtmlDescription(@NonNull PackageManager packageManager) { - if (mHtmlDescriptionRes == 0) { + if (mHtmlDescriptionRes == /* invalid */ 0) { return null; } @@ -941,7 +948,7 @@ public class AccessibilityServiceInfo implements Parcelable { final CharSequence htmlDescription = packageManager.getText(serviceInfo.packageName, mHtmlDescriptionRes, serviceInfo.applicationInfo); if (htmlDescription != null) { - return htmlDescription.toString().trim(); + return getFilteredHtmlText(htmlDescription.toString().trim()); } return null; } @@ -1414,4 +1421,103 @@ public class AccessibilityServiceInfo implements Parcelable { return new AccessibilityServiceInfo[size]; } }; + + /** + * Gets the filtered html string for + * {@link android.accessibilityservice.AccessibilityServiceInfo} and + * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It filters + * the <img> tag which do not meet the custom specification and the <a> tag. + * + * @param text the target text is html format. + * @return the filtered html string. + * + * @hide + */ + public static @NonNull String getFilteredHtmlText(@NonNull String text) { + final String replacementStart = "<invalidtag "; + final String replacementEnd = "</invalidtag>"; + + for (String tag : UNSUPPORTED_TAG_LIST) { + final String regexStart = "(?i)<" + tag + "(\\s+|>)"; + final String regexEnd = "(?i)</" + tag + "\\s*>"; + text = Pattern.compile(regexStart).matcher(text).replaceAll(replacementStart); + text = Pattern.compile(regexEnd).matcher(text).replaceAll(replacementEnd); + } + + final String regexInvalidImgTag = "(?i)<img\\s+(?!src\\s*=\\s*\"(?-i)" + IMG_PREFIX + ")"; + text = Pattern.compile(regexInvalidImgTag).matcher(text).replaceAll( + replacementStart); + + return text; + } + + /** + * Loads the animated image for + * {@link android.accessibilityservice.AccessibilityServiceInfo} and + * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It checks the resource + * whether to exceed the screen size. + * + * @param context the current context. + * @param applicationInfo the current application. + * @param resId the animated image resource id. + * @return the animated image which is safe. + * + * @hide + */ + @Nullable + public static Drawable loadSafeAnimatedImage(@NonNull Context context, + @NonNull ApplicationInfo applicationInfo, @StringRes int resId) { + if (resId == /* invalid */ 0) { + return null; + } + + final PackageManager packageManager = context.getPackageManager(); + final String packageName = applicationInfo.packageName; + final Drawable bannerDrawable = packageManager.getDrawable(packageName, resId, + applicationInfo); + if (bannerDrawable == null) { + return null; + } + + final boolean isImageWidthOverScreenLength = + bannerDrawable.getIntrinsicWidth() > getScreenWidthPixels(context); + final boolean isImageHeightOverScreenLength = + bannerDrawable.getIntrinsicHeight() > getScreenHeightPixels(context); + + return (isImageWidthOverScreenLength || isImageHeightOverScreenLength) + ? null + : bannerDrawable; + } + + /** + * Gets the width of the screen. + * + * @param context the current context. + * @return the width of the screen in term of pixels. + * + * @hide + */ + private static int getScreenWidthPixels(@NonNull Context context) { + final Resources resources = context.getResources(); + final int screenWidthDp = resources.getConfiguration().screenWidthDp; + + return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenWidthDp, + resources.getDisplayMetrics())); + } + + /** + * Gets the height of the screen. + * + * @param context the current context. + * @return the height of the screen in term of pixels. + * + * @hide + */ + private static int getScreenHeightPixels(@NonNull Context context) { + final Resources resources = context.getResources(); + final int screenHeightDp = resources.getConfiguration().screenHeightDp; + + return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenHeightDp, + resources.getDisplayMetrics())); + } } diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java index 62096792d764..a812f29a8bc7 100644 --- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java @@ -18,6 +18,7 @@ package android.accessibilityservice; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.StringRes; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -28,13 +29,19 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; +import android.text.TextUtils; import android.util.AttributeSet; +import android.util.TypedValue; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; /** * Activities of interest to users with accessibility needs may request to be targets of the @@ -87,6 +94,13 @@ public final class AccessibilityShortcutInfo { */ private final int mHtmlDescriptionRes; + // Used for html description of accessibility service. The <img> src tag must follow the + // prefix rule. e.g. <img src="R.drawable.fileName"/> + private static final String IMG_PREFIX = "R.drawable."; + private static final String ANCHOR_TAG = "a"; + private static final List<String> UNSUPPORTED_TAG_LIST = new ArrayList<>( + Collections.singletonList(ANCHOR_TAG)); + /** * Creates a new instance. * @@ -134,7 +148,7 @@ public final class AccessibilityShortcutInfo { // Gets animated image mAnimatedImageRes = asAttributes.getResourceId( com.android.internal.R.styleable - .AccessibilityShortcutTarget_animatedImageDrawable, 0); + .AccessibilityShortcutTarget_animatedImageDrawable, /* defValue= */ 0); // Gets html description mHtmlDescriptionRes = asAttributes.getResourceId( com.android.internal.R.styleable.AccessibilityShortcutTarget_htmlDescription, @@ -192,7 +206,7 @@ public final class AccessibilityShortcutInfo { } /** - * The animated image resource id of the accessibility shortcut target. + * Gets the animated image resource id. * * @return The animated image resource id. * @@ -205,7 +219,8 @@ public final class AccessibilityShortcutInfo { /** * The animated image drawable of the accessibility shortcut target. * - * @return The animated image drawable. + * @return The animated image drawable, or null if the resource is invalid or the image + * exceed the screen size. */ @Nullable public Drawable loadAnimatedImage(@NonNull Context context) { @@ -213,21 +228,20 @@ public final class AccessibilityShortcutInfo { return null; } - final PackageManager packageManager = context.getPackageManager(); - final String packageName = mComponentName.getPackageName(); - final ApplicationInfo applicationInfo = mActivityInfo.applicationInfo; - - return packageManager.getDrawable(packageName, mAnimatedImageRes, applicationInfo); + return loadSafeAnimatedImage(context, mActivityInfo.applicationInfo, mAnimatedImageRes); } /** - * The localized html description of the accessibility shortcut target. + * The localized and restricted html description of the accessibility shortcut target. + * It filters the <img> tag which do not meet the custom specification and the <a> tag. * - * @return The localized html description. + * @return The localized and restricted html description. */ @Nullable public String loadHtmlDescription(@NonNull PackageManager packageManager) { - return loadResourceString(packageManager, mActivityInfo, mHtmlDescriptionRes); + final String htmlDescription = loadResourceString(packageManager, mActivityInfo, + mHtmlDescriptionRes); + return TextUtils.isEmpty(htmlDescription) ? null : getFilteredHtmlText(htmlDescription); } /** @@ -291,4 +305,103 @@ public final class AccessibilityShortcutInfo { stringBuilder.append("]"); return stringBuilder.toString(); } + + /** + * Gets the filtered html string for + * {@link android.accessibilityservice.AccessibilityServiceInfo} and + * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It filters + * the <img> tag which do not meet the custom specification and the <a> tag. + * + * @param text the target text is html format. + * @return the filtered html string. + * + * @hide + */ + public static @NonNull String getFilteredHtmlText(@NonNull String text) { + final String replacementStart = "<invalidtag "; + final String replacementEnd = "</invalidtag>"; + + for (String tag : UNSUPPORTED_TAG_LIST) { + final String regexStart = "(?i)<" + tag + "(\\s+|>)"; + final String regexEnd = "(?i)</" + tag + "\\s*>"; + text = Pattern.compile(regexStart).matcher(text).replaceAll(replacementStart); + text = Pattern.compile(regexEnd).matcher(text).replaceAll(replacementEnd); + } + + final String regexInvalidImgTag = "(?i)<img\\s+(?!src\\s*=\\s*\"(?-i)" + IMG_PREFIX + ")"; + text = Pattern.compile(regexInvalidImgTag).matcher(text).replaceAll( + replacementStart); + + return text; + } + + /** + * Loads the animated image for + * {@link android.accessibilityservice.AccessibilityServiceInfo} and + * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It checks the resource + * whether to exceed the screen size. + * + * @param context the current context. + * @param applicationInfo the current application. + * @param resId the animated image resource id. + * @return the animated image which is safe. + * + * @hide + */ + @Nullable + public static Drawable loadSafeAnimatedImage(@NonNull Context context, + @NonNull ApplicationInfo applicationInfo, @StringRes int resId) { + if (resId == /* invalid */ 0) { + return null; + } + + final PackageManager packageManager = context.getPackageManager(); + final String packageName = applicationInfo.packageName; + final Drawable bannerDrawable = packageManager.getDrawable(packageName, resId, + applicationInfo); + if (bannerDrawable == null) { + return null; + } + + final boolean isImageWidthOverScreenLength = + bannerDrawable.getIntrinsicWidth() > getScreenWidthPixels(context); + final boolean isImageHeightOverScreenLength = + bannerDrawable.getIntrinsicHeight() > getScreenHeightPixels(context); + + return (isImageWidthOverScreenLength || isImageHeightOverScreenLength) + ? null + : bannerDrawable; + } + + /** + * Gets the width of the screen. + * + * @param context the current context. + * @return the width of the screen in term of pixels. + * + * @hide + */ + private static int getScreenWidthPixels(@NonNull Context context) { + final Resources resources = context.getResources(); + final int screenWidthDp = resources.getConfiguration().screenWidthDp; + + return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenWidthDp, + resources.getDisplayMetrics())); + } + + /** + * Gets the height of the screen. + * + * @param context the current context. + * @return the height of the screen in term of pixels. + * + * @hide + */ + private static int getScreenHeightPixels(@NonNull Context context) { + final Resources resources = context.getResources(); + final int screenHeightDp = resources.getConfiguration().screenHeightDp; + + return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenHeightDp, + resources.getDisplayMetrics())); + } } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 82fdb90be165..b51bbdf62286 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -2012,15 +2012,16 @@ public class ActivityManager { /** See {@link android.view.Surface.Rotation} */ @Surface.Rotation private int mRotation; + /** The size of the snapshot before scaling */ + private final Point mTaskSize; private final Rect mContentInsets; - // Whether this snapshot is a down-sampled version of the full resolution, used mainly for - // low-ram devices + // Whether this snapshot is a down-sampled version of the high resolution snapshot, used + // mainly for loading snapshots quickly from disk when user is flinging fast private final boolean mIsLowResolution; // Whether or not the snapshot is a real snapshot or an app-theme generated snapshot due to // the task having a secure window or having previews disabled private final boolean mIsRealSnapshot; private final int mWindowingMode; - private final float mScale; private final int mSystemUiVisibility; private final boolean mIsTranslucent; // Must be one of the named color spaces, otherwise, always use SRGB color space. @@ -2028,9 +2029,9 @@ public class ActivityManager { public TaskSnapshot(long id, @NonNull ComponentName topActivityComponent, GraphicBuffer snapshot, - @NonNull ColorSpace colorSpace, int orientation, int rotation, Rect contentInsets, - boolean isLowResolution, float scale, boolean isRealSnapshot, int windowingMode, - int systemUiVisibility, boolean isTranslucent) { + @NonNull ColorSpace colorSpace, int orientation, int rotation, Point taskSize, + Rect contentInsets, boolean isLowResolution, boolean isRealSnapshot, + int windowingMode, int systemUiVisibility, boolean isTranslucent) { mId = id; mTopActivityComponent = topActivityComponent; mSnapshot = snapshot; @@ -2038,9 +2039,9 @@ public class ActivityManager { ? ColorSpace.get(ColorSpace.Named.SRGB) : colorSpace; mOrientation = orientation; mRotation = rotation; + mTaskSize = new Point(taskSize); mContentInsets = new Rect(contentInsets); mIsLowResolution = isLowResolution; - mScale = scale; mIsRealSnapshot = isRealSnapshot; mWindowingMode = windowingMode; mSystemUiVisibility = systemUiVisibility; @@ -2057,9 +2058,9 @@ public class ActivityManager { : ColorSpace.get(ColorSpace.Named.SRGB); mOrientation = source.readInt(); mRotation = source.readInt(); + mTaskSize = source.readParcelable(null /* classLoader */); mContentInsets = source.readParcelable(null /* classLoader */); mIsLowResolution = source.readBoolean(); - mScale = source.readFloat(); mIsRealSnapshot = source.readBoolean(); mWindowingMode = source.readInt(); mSystemUiVisibility = source.readInt(); @@ -2111,6 +2112,14 @@ public class ActivityManager { } /** + * @return The size of the task at the point this snapshot was taken. + */ + @UnsupportedAppUsage + public Point getTaskSize() { + return mTaskSize; + } + + /** * @return The system/content insets on the snapshot. These can be clipped off in order to * remove any areas behind system bars in the snapshot. */ @@ -2159,14 +2168,6 @@ public class ActivityManager { return mSystemUiVisibility; } - /** - * @return The scale this snapshot was taken in. - */ - @UnsupportedAppUsage - public float getScale() { - return mScale; - } - @Override public int describeContents() { return 0; @@ -2180,9 +2181,9 @@ public class ActivityManager { dest.writeInt(mColorSpace.getId()); dest.writeInt(mOrientation); dest.writeInt(mRotation); + dest.writeParcelable(mTaskSize, 0); dest.writeParcelable(mContentInsets, 0); dest.writeBoolean(mIsLowResolution); - dest.writeFloat(mScale); dest.writeBoolean(mIsRealSnapshot); dest.writeInt(mWindowingMode); dest.writeInt(mSystemUiVisibility); @@ -2200,9 +2201,11 @@ public class ActivityManager { + " mColorSpace=" + mColorSpace.toString() + " mOrientation=" + mOrientation + " mRotation=" + mRotation + + " mTaskSize=" + mTaskSize.toString() + " mContentInsets=" + mContentInsets.toShortString() - + " mIsLowResolution=" + mIsLowResolution + " mScale=" + mScale - + " mIsRealSnapshot=" + mIsRealSnapshot + " mWindowingMode=" + mWindowingMode + + " mIsLowResolution=" + mIsLowResolution + + " mIsRealSnapshot=" + mIsRealSnapshot + + " mWindowingMode=" + mWindowingMode + " mSystemUiVisibility=" + mSystemUiVisibility + " mIsTranslucent=" + mIsTranslucent; } @@ -2224,9 +2227,8 @@ public class ActivityManager { private ColorSpace mColorSpace; private int mOrientation; private int mRotation; + private Point mTaskSize; private Rect mContentInsets; - private boolean mIsLowResolution; - private float mScaleFraction; private boolean mIsRealSnapshot; private int mWindowingMode; private int mSystemUiVisibility; @@ -2263,25 +2265,16 @@ public class ActivityManager { return this; } - public Builder setContentInsets(Rect contentInsets) { - mContentInsets = contentInsets; - return this; - } - /** - * Set to true if this is a low-resolution snapshot stored in *_reduced.jpg. + * Sets the original size of the task */ - public Builder setIsLowResolution(boolean isLowResolution) { - mIsLowResolution = isLowResolution; + public Builder setTaskSize(Point size) { + mTaskSize = size; return this; } - public float getScaleFraction() { - return mScaleFraction; - } - - public Builder setScaleFraction(float scaleFraction) { - mScaleFraction = scaleFraction; + public Builder setContentInsets(Rect contentInsets) { + mContentInsets = contentInsets; return this; } @@ -2322,9 +2315,12 @@ public class ActivityManager { mColorSpace, mOrientation, mRotation, + mTaskSize, mContentInsets, - mIsLowResolution, - mScaleFraction, + // When building a TaskSnapshot with the Builder class, isLowResolution + // is always false. Low-res snapshots are only created when loading from + // disk. + false /* isLowResolution */, mIsRealSnapshot, mWindowingMode, mSystemUiVisibility, diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java index 9ba56cf40161..fb315add36d2 100644 --- a/core/java/android/app/ActivityTaskManager.java +++ b/core/java/android/app/ActivityTaskManager.java @@ -215,8 +215,7 @@ public class ActivityTaskManager { public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop, boolean animate, Rect initialBounds, boolean showRecents) throws SecurityException { try { - return getService().setTaskWindowingModeSplitScreenPrimary(taskId, createMode, toTop, - animate, initialBounds, showRecents); + return getService().setTaskWindowingModeSplitScreenPrimary(taskId, toTop); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -362,25 +361,6 @@ public class ActivityTaskManager { } /** - * Resize the input stack id to the given bounds with animate setting. - * @param stackId Id of the stack to resize. - * @param bounds Bounds to resize the stack to or {@code null} for fullscreen. - * @param animate Whether we should play an animation for resizing stack. - */ - @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) - public void resizePinnedStack(int stackId, Rect bounds, boolean animate) { - try { - if (animate) { - getService().animateResizePinnedStack(stackId, bounds, -1 /* animationDuration */); - } else { - getService().resizePinnedStack(bounds, null /* tempPinnedTaskBounds */); - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Resize task to given bounds. * @param taskId Id of task to resize. * @param bounds Bounds to resize task. diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 92dd91a877a9..0ed5aec58924 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -68,6 +68,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ParceledListSlice; import android.content.pm.ProviderInfo; +import android.content.pm.ProviderInfoList; import android.content.pm.ServiceInfo; import android.content.res.AssetManager; import android.content.res.CompatibilityInfo; @@ -1011,8 +1012,9 @@ public final class ActivityThread extends ClientTransactionHandler { sendMessage(H.STOP_SERVICE, token); } + @Override public final void bindApplication(String processName, ApplicationInfo appInfo, - List<ProviderInfo> providers, ComponentName instrumentationName, + ProviderInfoList providerList, ComponentName instrumentationName, ProfilerInfo profilerInfo, Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, IUiAutomationConnection instrumentationUiConnection, int debugMode, @@ -1052,7 +1054,7 @@ public final class ActivityThread extends ClientTransactionHandler { AppBindData data = new AppBindData(); data.processName = processName; data.appInfo = appInfo; - data.providers = providers; + data.providers = providerList.getList(); data.instrumentationName = instrumentationName; data.instrumentationArgs = instrumentationArgs; data.instrumentationWatcher = instrumentationWatcher; diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 8f02f1555edf..a53fc3508001 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -6910,11 +6910,7 @@ public class AppOpsManager { * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}. */ public int unsafeCheckOpRaw(@NonNull String op, int uid, @NonNull String packageName) { - try { - return mService.checkOperationRaw(strOpToOp(op), uid, packageName); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return unsafeCheckOpRawNoThrow(op, uid, packageName); } /** @@ -6923,8 +6919,17 @@ public class AppOpsManager { * {@link #MODE_FOREGROUND}. */ public int unsafeCheckOpRawNoThrow(@NonNull String op, int uid, @NonNull String packageName) { + return unsafeCheckOpRawNoThrow(strOpToOp(op), uid, packageName); + } + + /** + * Returns the <em>raw</em> mode associated with the op. + * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}. + * @hide + */ + public int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName) { try { - return mService.checkOperationRaw(strOpToOp(op), uid, packageName); + return mService.checkOperationRaw(op, uid, packageName); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/DreamManager.java b/core/java/android/app/DreamManager.java new file mode 100644 index 000000000000..fe13b8f26d78 --- /dev/null +++ b/core/java/android/app/DreamManager.java @@ -0,0 +1,102 @@ +/* + * 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 android.app; + +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemService; +import android.annotation.TestApi; +import android.annotation.UserHandleAware; +import android.content.ComponentName; +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.service.dreams.DreamService; +import android.service.dreams.IDreamManager; + +/** + * @hide + */ +@SystemService(Context.DREAM_SERVICE) +@TestApi +public class DreamManager { + private final IDreamManager mService; + private final Context mContext; + + /** + * @hide + */ + public DreamManager(Context context) throws ServiceManager.ServiceNotFoundException { + mService = IDreamManager.Stub.asInterface( + ServiceManager.getServiceOrThrow(DreamService.DREAM_SERVICE)); + mContext = context; + } + + /** + * Starts dream service with name "name". + * + * <p>This is only used for testing the dream service APIs. + * + * @hide + */ + @TestApi + @UserHandleAware + @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) + public void startDream(@NonNull ComponentName name) { + try { + mService.testDream(mContext.getUserId(), name); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Stops the dream service on the device if one is started. + * + * <p> This is only used for testing the dream service APIs. + * + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) + public void stopDream() { + try { + mService.awaken(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Sets the active dream on the device to be "dreamComponent". + * + * <p>This is only used for testing the dream service APIs. + * + * @hide + */ + @TestApi + @UserHandleAware + @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) + public void setActiveDream(@NonNull ComponentName dreamComponent) { + ComponentName[] dreams = {dreamComponent}; + try { + mService.setDreamComponentsForUser(mContext.getUserId(), dreams); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } +} diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 463c8c9a4528..6f0611e68cda 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -528,29 +528,6 @@ interface IActivityManager { boolean unlockUser(int userid, in byte[] token, in byte[] secret, in IProgressListener listener); void killPackageDependents(in String packageName, int userId); - /** - * Resizes the docked stack, and all other stacks as the result of the dock stack bounds change. - * - * @param dockedBounds The bounds for the docked stack. - * @param tempDockedTaskBounds The temporary bounds for the tasks in the docked stack, which - * might be different from the stack bounds to allow more - * flexibility while resizing, or {@code null} if they should be the - * same as the stack bounds. - * @param tempDockedTaskInsetBounds The temporary bounds for the tasks to calculate the insets. - * When resizing, we usually "freeze" the layout of a task. To - * achieve that, we also need to "freeze" the insets, which - * gets achieved by changing task bounds but not bounds used - * to calculate the insets in this transient state - * @param tempOtherTaskBounds The temporary bounds for the tasks in all other stacks, or - * {@code null} if they should be the same as the stack bounds. - * @param tempOtherTaskInsetBounds Like {@code tempDockedTaskInsetBounds}, but for the other - * stacks. - * @throws RemoteException - */ - @UnsupportedAppUsage - void resizeDockedStack(in Rect dockedBounds, in Rect tempDockedTaskBounds, - in Rect tempDockedTaskInsetBounds, - in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds); @UnsupportedAppUsage void removeStack(int stackId); void makePackageIdle(String packageName, int userId); diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 5b61402314c4..7c8926341555 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -98,6 +98,14 @@ interface IActivityTaskManager { in ProfilerInfo profilerInfo, in Bundle options, int userId); boolean startNextMatchingActivity(in IBinder callingActivity, in Intent intent, in Bundle options); + + /** + * The DreamActivity has to be started in a special way that does not involve the PackageParser. + * The DreamActivity is a framework component inserted in the dream application process. Hence, + * it is not declared in the application's manifest and cannot be parsed. startDreamActivity + * creates the activity and starts it without reaching out to the PackageParser. + */ + boolean startDreamActivity(in Intent intent); int startActivityIntentSender(in IApplicationThread caller, in IIntentSender target, in IBinder whitelistToken, in Intent fillInIntent, in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode, @@ -236,32 +244,7 @@ interface IActivityTaskManager { */ boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop); void moveTaskToStack(int taskId, int stackId, boolean toTop); - /** - * Resizes the input pinned stack to the given bounds with animation. - * - * @param stackId Id of the pinned stack to resize. - * @param bounds Bounds to resize the stack to or {@code null} for fullscreen. - * @param animationDuration The duration of the resize animation in milliseconds or -1 if the - * default animation duration should be used. - * @throws RemoteException - */ - void animateResizePinnedStack(int stackId, in Rect bounds, int animationDuration); - boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop, - boolean animate, in Rect initialBounds, boolean showRecents); - /** - * Use the offset to adjust the stack boundary with animation. - * - * @param stackId Id of the stack to adjust. - * @param compareBounds Offset is only applied if the current pinned stack bounds is equal to - * the compareBounds. - * @param xOffset The horizontal offset. - * @param yOffset The vertical offset. - * @param animationDuration The duration of the resize animation in milliseconds or -1 if the - * default animation duration should be used. - * @throws RemoteException - */ - void offsetPinnedStackBounds(int stackId, in Rect compareBounds, int xOffset, int yOffset, - int animationDuration); + boolean setTaskWindowingModeSplitScreenPrimary(int taskId, boolean toTop); /** * Removes stacks in the input windowing modes from the system if they are of activity type * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED @@ -371,23 +354,10 @@ interface IActivityTaskManager { void startLocalVoiceInteraction(in IBinder token, in Bundle options); void stopLocalVoiceInteraction(in IBinder token); boolean supportsLocalVoiceInteraction(); - void notifyPinnedStackAnimationStarted(); - void notifyPinnedStackAnimationEnded(); // Get device configuration ConfigurationInfo getDeviceConfigurationInfo(); - /** - * Resizes the pinned stack. - * - * @param pinnedBounds The bounds for the pinned stack. - * @param tempPinnedTaskBounds The temporary bounds for the tasks in the pinned stack, which - * might be different from the stack bounds to allow more - * flexibility while resizing, or {@code null} if they should be the - * same as the stack bounds. - */ - void resizePinnedStack(in Rect pinnedBounds, in Rect tempPinnedTaskBounds); - void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback, in CharSequence message); diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index c33c515f062c..1f6e4cac199a 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -30,6 +30,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ParceledListSlice; import android.content.pm.ProviderInfo; +import android.content.pm.ProviderInfoList; import android.content.pm.ServiceInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; @@ -66,7 +67,7 @@ oneway interface IApplicationThread { @UnsupportedAppUsage void scheduleStopService(IBinder token); void bindApplication(in String packageName, in ApplicationInfo info, - in List<ProviderInfo> providers, in ComponentName testName, + in ProviderInfoList providerList, in ComponentName testName, in ProfilerInfo profilerInfo, in Bundle testArguments, IInstrumentationWatcher testWatcher, IUiAutomationConnection uiAutomationConnection, int debugMode, boolean enableBinderTracking, boolean trackAllocation, diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index 37bdda0b0393..28b28dad5b82 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -46,16 +46,6 @@ oneway interface ITaskStackListener { void onPinnedActivityRestartAttempt(boolean clearedTask); /** - * Called whenever the pinned stack is starting animating a resize. - */ - void onPinnedStackAnimationStarted(); - - /** - * Called whenever the pinned stack is done animating a resize. - */ - void onPinnedStackAnimationEnded(); - - /** * Called when we launched an activity that we forced to be resizable. * * @param packageName Package name of the top activity in the task. diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 7212be82d1a9..3f2ec4487668 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -15,6 +15,7 @@ */ package android.app; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; @@ -63,6 +64,7 @@ public final class NotificationChannel implements Parcelable { * string takes two arguments, in this order: the * {@link #getId()} of the original notification channel, and the * {@link ShortcutInfo#getId() id} of the conversation. + * @hide */ public static final String CONVERSATION_CHANNEL_ID_FORMAT = "%1$s : %2$s"; @@ -554,8 +556,8 @@ public final class NotificationChannel implements Parcelable { } /** - * Sets this channel as being person-centric. Different settings and functionality may be - * exposed for people-centric channels. + * Sets this channel as being converastion-centric. Different settings and functionality may be + * exposed for conversation-centric channels. * * @param parentChannelId The {@link #getId()} id} of the generic channel that notifications of * this type would be posted to in absence of a specific conversation id. @@ -564,8 +566,8 @@ public final class NotificationChannel implements Parcelable { * @param conversationId The {@link ShortcutInfo#getId()} of the shortcut representing this * channel's conversation. */ - public void setConversationId(@Nullable String parentChannelId, - @Nullable String conversationId) { + public void setConversationId(@NonNull String parentChannelId, + @NonNull String conversationId) { mParentId = parentChannelId; mConversationId = conversationId; } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 88edb05ba968..cbbdf639d036 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1573,6 +1573,15 @@ public class NotificationManager { PRIORITY_CATEGORY_CONVERSATIONS, }; + /** @hide */ + @IntDef(prefix = { "PRIORITY_SENDERS_" }, value = { + PRIORITY_SENDERS_ANY, + PRIORITY_SENDERS_CONTACTS, + PRIORITY_SENDERS_STARRED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PrioritySenders {} + /** Any sender is prioritized. */ public static final int PRIORITY_SENDERS_ANY = 0; /** Saved contacts are prioritized. */ @@ -1816,8 +1825,9 @@ public class NotificationManager { * @param suppressedVisualEffects which visual interruptions should be suppressed from * notifications that are filtered by DND. */ - public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders, - int suppressedVisualEffects, int priorityConversationSenders) { + public Policy(int priorityCategories, @PrioritySenders int priorityCallSenders, + @PrioritySenders int priorityMessageSenders, + int suppressedVisualEffects, @ConversationSenders int priorityConversationSenders) { this(priorityCategories, priorityCallSenders, priorityMessageSenders, suppressedVisualEffects, STATE_UNSET, priorityConversationSenders); } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 4369680e7781..d04630c747a3 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -186,7 +186,6 @@ import android.telephony.TelephonyFrameworkInitializer; import android.telephony.TelephonyRegistryManager; import android.util.ArrayMap; import android.util.Log; -import android.util.Slog; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.WindowManager; @@ -223,8 +222,6 @@ import java.util.Objects; public final class SystemServiceRegistry { private static final String TAG = "SystemServiceRegistry"; - private static final boolean ENABLE_SERVICE_NOT_FOUND_WTF = true; - // Service registry information. // This information is never changed once static initialization has completed. private static final Map<Class<?>, String> SYSTEM_SERVICE_NAMES = @@ -1334,6 +1331,13 @@ public final class SystemServiceRegistry { IBinder b = ServiceManager.getServiceOrThrow(Context.APP_INTEGRITY_SERVICE); return new AppIntegrityManager(IAppIntegrityManager.Stub.asInterface(b)); }}); + registerService(Context.DREAM_SERVICE, DreamManager.class, + new CachedServiceFetcher<DreamManager>() { + @Override + public DreamManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new DreamManager(ctx); + }}); sInitializing = true; try { @@ -1370,29 +1374,8 @@ public final class SystemServiceRegistry { * @hide */ public static Object getSystemService(ContextImpl ctx, String name) { - if (name == null) { - return null; - } - final ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name); - if (ENABLE_SERVICE_NOT_FOUND_WTF && fetcher == null) { - // This should be a caller bug. - Slog.wtf(TAG, "Unknown manager requested: " + name); - return null; - } - - final Object ret = fetcher.getService(ctx); - if (ENABLE_SERVICE_NOT_FOUND_WTF && ret == null) { - // Some services do return null in certain situations, so don't do WTF for them. - switch (name) { - case Context.CONTENT_CAPTURE_MANAGER_SERVICE: - case Context.APP_PREDICTION_SERVICE: - case Context.INCREMENTAL_SERVICE: - return null; - } - Slog.wtf(TAG, "Manager wrapper not available: " + name); - return null; - } - return ret; + ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name); + return fetcher != null ? fetcher.getService(ctx) : null; } /** @@ -1400,15 +1383,7 @@ public final class SystemServiceRegistry { * @hide */ public static String getSystemServiceName(Class<?> serviceClass) { - if (serviceClass == null) { - return null; - } - final String serviceName = SYSTEM_SERVICE_NAMES.get(serviceClass); - if (ENABLE_SERVICE_NOT_FOUND_WTF && serviceName == null) { - // This should be a caller bug. - Slog.wtf(TAG, "Unknown manager requested: " + serviceClass.getCanonicalName()); - } - return serviceName; + return SYSTEM_SERVICE_NAMES.get(serviceClass); } /** @@ -1705,9 +1680,7 @@ public final class SystemServiceRegistry { try { cache.wait(); } catch (InterruptedException e) { - // This shouldn't normally happen, but if someone interrupts the - // thread, it will. - Slog.wtf(TAG, "getService() interrupted"); + Log.w(TAG, "getService() interrupted"); Thread.currentThread().interrupt(); return null; } diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index da0aadb3ea48..b892b8e51c88 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -58,16 +58,6 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { @Override @UnsupportedAppUsage - public void onPinnedStackAnimationStarted() throws RemoteException { - } - - @Override - @UnsupportedAppUsage - public void onPinnedStackAnimationEnded() throws RemoteException { - } - - @Override - @UnsupportedAppUsage public void onActivityForcedResizable(String packageName, int taskId, int reason) throws RemoteException { } diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java index f0eedf3d18f2..7f436401dbf4 100644 --- a/core/java/android/app/prediction/AppPredictor.java +++ b/core/java/android/app/prediction/AppPredictor.java @@ -260,6 +260,7 @@ public final class AppPredictor { Log.e(TAG, "Failed to notify app target event", e); e.rethrowAsRuntimeException(); } + mRegisteredCallbacks.clear(); } else { throw new IllegalStateException("This client has already been destroyed."); } diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 6dea1c69ce86..ccd8199b8373 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -182,6 +182,16 @@ public class AppWidgetManager { public static final String EXTRA_APPWIDGET_ID = "appWidgetId"; /** + * A bundle extra that contains whether or not an app has finished restoring a widget. + * <p> After restore, the app should set OPTION_APPWIDGET_RESTORE_COMPLETED to true on its + * widgets followed by calling {@link #updateAppWidget} to update the views. + * + * @see #updateAppWidgetOptions(int, Bundle) + */ + public static final String OPTION_APPWIDGET_RESTORE_COMPLETED = "appWidgetRestoreCompleted"; + + + /** * A bundle extra that contains the lower bound on the current width, in dips, of a widget instance. */ public static final String OPTION_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth"; diff --git a/core/java/android/appwidget/AppWidgetProvider.java b/core/java/android/appwidget/AppWidgetProvider.java index ab91edfeca24..a5d2198a6e17 100644 --- a/core/java/android/appwidget/AppWidgetProvider.java +++ b/core/java/android/appwidget/AppWidgetProvider.java @@ -200,6 +200,9 @@ public class AppWidgetProvider extends BroadcastReceiver { * provider can immediately generate new RemoteViews suitable for its newly-restored set * of instances. * + * <p>In addition, you should set {@link AppWidgetManager#OPTION_APPWIDGET_RESTORE_COMPLETED} + * to true indicate if a widget has been restored successfully from the provider's side. + * * {@more} * * @param context diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index c6e84b7e46b0..536b6c33b142 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5157,6 +5157,17 @@ public abstract class Context { public static final String LIGHTS_SERVICE = "lights"; /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.app.DreamManager} for controlling Dream states. + * + * @see #getSystemService(String) + + * @hide + */ + @TestApi + public static final String DREAM_SERVICE = "dream"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 9d1c677f35c6..4c6fef2e1856 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -1630,6 +1630,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { @SuppressWarnings("unchecked") public void writeToParcel(Parcel dest, int parcelableFlags) { + if (dest.maybeWriteSquashed(this)) { + return; + } super.writeToParcel(dest, parcelableFlags); dest.writeString(taskAffinity); dest.writeString(permission); @@ -1700,9 +1703,12 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR = new Parcelable.Creator<ApplicationInfo>() { + @Override public ApplicationInfo createFromParcel(Parcel source) { - return new ApplicationInfo(source); + return source.readSquashed(ApplicationInfo::new); } + + @Override public ApplicationInfo[] newArray(int size) { return new ApplicationInfo[size]; } diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java index 8b41c04b3549..362098c447ce 100644 --- a/core/java/android/content/pm/ComponentInfo.java +++ b/core/java/android/content/pm/ComponentInfo.java @@ -20,7 +20,6 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.graphics.drawable.Drawable; import android.os.Parcel; -import android.os.Parcelable; import android.util.Printer; /** @@ -197,12 +196,7 @@ public class ComponentInfo extends PackageItemInfo { public void writeToParcel(Parcel dest, int parcelableFlags) { super.writeToParcel(dest, parcelableFlags); - if ((parcelableFlags & Parcelable.PARCELABLE_ELIDE_DUPLICATES) != 0) { - dest.writeInt(0); - } else { - dest.writeInt(1); - applicationInfo.writeToParcel(dest, parcelableFlags); - } + applicationInfo.writeToParcel(dest, parcelableFlags); dest.writeString(processName); dest.writeString(splitName); dest.writeInt(descriptionRes); @@ -213,10 +207,7 @@ public class ComponentInfo extends PackageItemInfo { protected ComponentInfo(Parcel source) { super(source); - final boolean hasApplicationInfo = (source.readInt() != 0); - if (hasApplicationInfo) { - applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source); - } + applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source); processName = source.readString(); splitName = source.readString(); descriptionRes = source.readInt(); diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index 36fa5728b34e..85c698f3fb0c 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -439,6 +439,8 @@ public class PackageInfo implements Parcelable { @Override public void writeToParcel(Parcel dest, int parcelableFlags) { + // Allow ApplicationInfo to be squashed. + final boolean prevAllowSquashing = dest.allowSquashing(); dest.writeString(packageName); dest.writeStringArray(splitNames); dest.writeInt(versionCode); @@ -457,10 +459,10 @@ public class PackageInfo implements Parcelable { dest.writeLong(firstInstallTime); dest.writeLong(lastUpdateTime); dest.writeIntArray(gids); - dest.writeTypedArray(activities, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); - dest.writeTypedArray(receivers, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); - dest.writeTypedArray(services, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); - dest.writeTypedArray(providers, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); + dest.writeTypedArray(activities, parcelableFlags); + dest.writeTypedArray(receivers, parcelableFlags); + dest.writeTypedArray(services, parcelableFlags); + dest.writeTypedArray(providers, parcelableFlags); dest.writeTypedArray(instrumentation, parcelableFlags); dest.writeTypedArray(permissions, parcelableFlags); dest.writeStringArray(requestedPermissions); @@ -488,6 +490,7 @@ public class PackageInfo implements Parcelable { dest.writeInt(0); } dest.writeBoolean(isApex); + dest.restoreAllowSquashing(prevAllowSquashing); } public static final @android.annotation.NonNull Parcelable.Creator<PackageInfo> CREATOR @@ -550,21 +553,5 @@ public class PackageInfo implements Parcelable { signingInfo = SigningInfo.CREATOR.createFromParcel(source); } isApex = source.readBoolean(); - // The component lists were flattened with the redundant ApplicationInfo - // instances omitted. Distribute the canonical one here as appropriate. - if (applicationInfo != null) { - propagateApplicationInfo(applicationInfo, activities); - propagateApplicationInfo(applicationInfo, receivers); - propagateApplicationInfo(applicationInfo, services); - propagateApplicationInfo(applicationInfo, providers); - } - } - - private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) { - if (components != null) { - for (ComponentInfo ci : components) { - ci.applicationInfo = appInfo; - } - } } } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index f2ec938b3d9d..fa751d380580 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -7853,11 +7853,16 @@ public abstract class PackageManager { /** * Returns if the provided drawable represents the default activity icon provided by the system. * - * PackageManager provides a default icon for any package/activity if the app itself does not - * define one or if the system encountered any error when loading the icon. + * PackageManager silently returns a default application icon for any package/activity if the + * app itself does not define one or if the system encountered any error when loading the icon. + * + * Developers can use this to check implement app specific logic around retrying or caching. * * @return true if the drawable represents the default activity icon, false otherwise * @see #getDefaultActivityIcon() + * @see PackageItemInfo#loadDefaultIcon(PackageManager) + * @see #getActivityIcon + * @see LauncherActivityInfo#getIcon(int) */ public boolean isDefaultApplicationIcon(@NonNull Drawable drawable) { int resId = drawable instanceof AdaptiveIconDrawable diff --git a/core/java/android/content/pm/ProviderInfoList.aidl b/core/java/android/content/pm/ProviderInfoList.aidl new file mode 100644 index 000000000000..bb576d77003d --- /dev/null +++ b/core/java/android/content/pm/ProviderInfoList.aidl @@ -0,0 +1,19 @@ +/* + * 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 android.content.pm; + +parcelable ProviderInfoList; diff --git a/core/java/android/content/pm/ProviderInfoList.java b/core/java/android/content/pm/ProviderInfoList.java new file mode 100644 index 000000000000..566be2e32fe0 --- /dev/null +++ b/core/java/android/content/pm/ProviderInfoList.java @@ -0,0 +1,87 @@ +/* + * 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 android.content.pm; + +import android.annotation.NonNull; +import android.annotation.TestApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; + +/** + * Equivalent to List<ProviderInfo>, but it "squashes" the ApplicationInfo in the elements. + * + * @hide + */ +@TestApi +public final class ProviderInfoList implements Parcelable { + private final List<ProviderInfo> mList; + + private ProviderInfoList(Parcel source) { + final ArrayList<ProviderInfo> list = new ArrayList<>(); + source.readTypedList(list, ProviderInfo.CREATOR); + mList = list; + } + + private ProviderInfoList(List<ProviderInfo> list) { + mList = list; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + // Allow ApplicationInfo to be squashed. + final boolean prevAllowSquashing = dest.allowSquashing(); + dest.writeTypedList(mList, flags); + dest.restoreAllowSquashing(prevAllowSquashing); + } + + public static final @android.annotation.NonNull Parcelable.Creator<ProviderInfoList> CREATOR + = new Parcelable.Creator<ProviderInfoList>() { + @Override + public ProviderInfoList createFromParcel(@NonNull Parcel source) { + return new ProviderInfoList(source); + } + + @Override + public ProviderInfoList[] newArray(int size) { + return new ProviderInfoList[size]; + } + }; + + /** + * Return the stored list. + */ + @NonNull + public List<ProviderInfo> getList() { + return mList; + } + + /** + * Create a new instance with a {@code list}. The passed list will be shared with the new + * instance, so the caller shouldn't modify it. + */ + @NonNull + public static ProviderInfoList fromList(@NonNull List<ProviderInfo> list) { + return new ProviderInfoList(list); + } +} diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java index 9e40f46c3c21..9da0f20d1006 100644 --- a/core/java/android/content/res/ResourcesKey.java +++ b/core/java/android/content/res/ResourcesKey.java @@ -54,7 +54,6 @@ public final class ResourcesKey { private final int mHash; - @UnsupportedAppUsage public ResourcesKey(@Nullable String resDir, @Nullable String[] splitResDirs, @Nullable String[] overlayDirs, @@ -85,6 +84,18 @@ public final class ResourcesKey { mHash = hash; } + @UnsupportedAppUsage + public ResourcesKey(@Nullable String resDir, + @Nullable String[] splitResDirs, + @Nullable String[] overlayDirs, + @Nullable String[] libDirs, + int displayId, + @Nullable Configuration overrideConfig, + @Nullable CompatibilityInfo compatInfo) { + this(resDir, splitResDirs, overlayDirs, libDirs, displayId, overrideConfig, compatInfo, + null); + } + public boolean hasOverrideConfiguration() { return !Configuration.EMPTY.equals(mOverrideConfiguration); } diff --git a/core/java/android/debug/AdbManagerInternal.java b/core/java/android/debug/AdbManagerInternal.java index 0bd9f19f91fe..d730129507d7 100644 --- a/core/java/android/debug/AdbManagerInternal.java +++ b/core/java/android/debug/AdbManagerInternal.java @@ -53,4 +53,14 @@ public abstract class AdbManagerInternal { * Returns the file that contains all of the ADB keys and their last used time. */ public abstract File getAdbTempKeysFile(); + + /** + * Starts adbd for a transport. + */ + public abstract void startAdbdForTransport(byte transportType); + + /** + * Stops adbd for a transport. + */ + public abstract void stopAdbdForTransport(byte transportType); } diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 1a4dac78855f..f0b7b5fa5a1a 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -28,6 +28,7 @@ import android.util.ExceptionUtils; import android.util.Log; import android.util.Size; import android.util.SizeF; +import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; @@ -1827,6 +1828,179 @@ public final class Parcel { } /** + * A map used by {@link #maybeWriteSquashed} to keep track of what parcelables have + * been seen, and what positions they were written. The value is the absolute position of + * each parcelable. + */ + private ArrayMap<Parcelable, Integer> mWrittenSquashableParcelables; + + private void ensureWrittenSquashableParcelables() { + if (mWrittenSquashableParcelables != null) { + return; + } + mWrittenSquashableParcelables = new ArrayMap<>(); + } + + private boolean mAllowSquashing = false; + + /** + * Allow "squashing" writes in {@link #maybeWriteSquashed}. This allows subsequent calls to + * {@link #maybeWriteSquashed(Parcelable)} to "squash" the same instances into one in a Parcel. + * + * Typically, this method is called at the beginning of {@link Parcelable#writeToParcel}. The + * caller must retain the return value from this method and call {@link #restoreAllowSquashing} + * with it. + * + * See {@link #maybeWriteSquashed(Parcelable)} for the details. + * + * @see #restoreAllowSquashing(boolean) + * @see #maybeWriteSquashed(Parcelable) + * @see #readSquashed(SquashReadHelper) + * + * @hide + */ + @TestApi + public boolean allowSquashing() { + boolean previous = mAllowSquashing; + mAllowSquashing = true; + return previous; + } + + /** + * @see #allowSquashing() + * @hide + */ + @TestApi + public void restoreAllowSquashing(boolean previous) { + mAllowSquashing = previous; + if (!mAllowSquashing) { + mWrittenSquashableParcelables = null; + } + } + + private void resetSqaushingState() { + if (mAllowSquashing) { + Slog.wtf(TAG, "allowSquashing wasn't restored."); + } + mWrittenSquashableParcelables = null; + mReadSquashableParcelables = null; + mAllowSquashing = false; + } + + /** + * A map used by {@link #readSquashed} to cache parcelables. It's a map from + * an absolute position in a Parcel to the parcelable stored at the position. + */ + private ArrayMap<Integer, Parcelable> mReadSquashableParcelables; + + private void ensureReadSquashableParcelables() { + if (mReadSquashableParcelables != null) { + return; + } + mReadSquashableParcelables = new ArrayMap<>(); + } + + /** + * Write a parcelable with "squash" -- that is, when the same instance is written to the + * same Parcelable multiple times, instead of writing the entire instance multiple times, + * only write it once, and in subsequent writes we'll only write the offset to the original + * object. + * + * This approach does not work of the resulting Parcel is copied with {@link #appendFrom} with + * a non-zero offset, so we do not enable this behavior by default. Instead, we only enable + * it between {@link #allowSquashing} and {@link #restoreAllowSquashing}, in order to make sure + * we only do so within each "top level" Parcelable. + * + * Usage: Use this method in {@link Parcelable#writeToParcel}. + * If this method returns TRUE, it's a subsequent call, and the offset is already written, + * so the caller doesn't have to do anything. If this method returns FALSE, it's the first + * time for the instance to be written to this parcel. The caller has to proceed with its + * {@link Parcelable#writeToParcel}. + * + * (See {@code ApplicationInfo} for the example.) + * + * @param p the target Parcelable to write. + * + * @see #allowSquashing() + * @see #restoreAllowSquashing(boolean) + * @see #readSquashed(SquashReadHelper) + * + * @hide + */ + public boolean maybeWriteSquashed(@NonNull Parcelable p) { + if (!mAllowSquashing) { + // Don't squash, and don't put it in the map either. + writeInt(0); + return false; + } + ensureWrittenSquashableParcelables(); + final Integer firstPos = mWrittenSquashableParcelables.get(p); + if (firstPos != null) { + // Already written. + // Write the relative offset from the current position to the first position. + final int pos = dataPosition(); + + // We want the offset from the next byte of this integer, so we need to +4. + writeInt(pos - firstPos + 4); + return true; + } + // First time seen, write a marker. + writeInt(0); + + // Remember the position. + final int pos = dataPosition(); + mWrittenSquashableParcelables.put(p, pos); + + // Return false and let the caller actually write the content. + return false; + } + + /** + * Helper function that's used by {@link #readSquashed(SquashReadHelper)} + * @hide + */ + public interface SquashReadHelper<T> { + /** Read and instantiate {@code T} from a Parcel. */ + @NonNull + T readRawParceled(@NonNull Parcel p); + } + + /** + * Read a {@link Parcelable} that's written with {@link #maybeWriteSquashed}. + * + * @param reader a callback function that instantiates an instance from a parcel. + * Typicallly, a lambda to the instructor that takes a {@link Parcel} is passed. + * + * @see #maybeWriteSquashed(Parcelable) + * + * @hide + */ + @SuppressWarnings("unchecked") + @Nullable + public <T extends Parcelable> T readSquashed(SquashReadHelper<T> reader) { + final int offset = readInt(); + final int pos = dataPosition(); + + if (offset == 0) { + // First time read. Unparcel, and remember it. + final T p = reader.readRawParceled(this); + ensureReadSquashableParcelables(); + mReadSquashableParcelables.put(pos, p); + return p; + } + // Subsequent read. + final int firstAbsolutePos = pos - offset; + + final Parcelable p = mReadSquashableParcelables.get(firstAbsolutePos); + if (p == null) { + Slog.wtfStack(TAG, "Map doesn't contain offset " + + firstAbsolutePos + + " : contains=" + new ArrayList<>(mReadSquashableParcelables.keySet())); + } + return (T) p; + } + + /** * Write a generic serializable object in to a Parcel. It is strongly * recommended that this method be avoided, since the serialization * overhead is extremely large, and this approach will be much slower than @@ -3247,6 +3421,7 @@ public final class Parcel { } private void freeBuffer() { + resetSqaushingState(); if (mOwnsNativeParcelObject) { updateNativeSize(nativeFreeBuffer(mNativePtr)); } @@ -3254,6 +3429,7 @@ public final class Parcel { } private void destroy() { + resetSqaushingState(); if (mNativePtr != 0) { if (mOwnsNativeParcelObject) { nativeDestroy(mNativePtr); @@ -3261,7 +3437,6 @@ public final class Parcel { } mNativePtr = 0; } - mReadWriteHelper = null; } @Override diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java index 5cb33615fe22..50d8d8079ed5 100644 --- a/core/java/android/os/image/DynamicSystemClient.java +++ b/core/java/android/os/image/DynamicSystemClient.java @@ -96,9 +96,6 @@ public class DynamicSystemClient { private static final String TAG = "DynSystemClient"; - private static final long DEFAULT_USERDATA_SIZE = (10L << 30); - - /** Listener for installation status updates. */ public interface OnStatusChangedListener { /** @@ -386,7 +383,7 @@ public class DynamicSystemClient { @SystemApi @TestApi public void start(@NonNull Uri systemUrl, @BytesLong long systemSize) { - start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE); + start(systemUrl, systemSize, 0 /* Use the default userdata size */); } /** diff --git a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl index c389b1a1a5ff..dd434b440af4 100644 --- a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl +++ b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl @@ -28,5 +28,5 @@ import android.service.autofill.InlinePresentation; oneway interface IInlineSuggestionRenderService { void renderSuggestion(in IInlineSuggestionUiCallback callback, in InlinePresentation presentation, int width, int height, - in IBinder hostInputToken); + in IBinder hostInputToken, int displayId); } diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java index 29069e7035f9..fcdefac3163b 100644 --- a/core/java/android/service/autofill/InlineSuggestionRenderService.java +++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java @@ -31,6 +31,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.util.Log; +import android.view.Display; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.View; @@ -61,7 +62,8 @@ public abstract class InlineSuggestionRenderService extends Service { private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true); private void handleRenderSuggestion(IInlineSuggestionUiCallback callback, - InlinePresentation presentation, int width, int height, IBinder hostInputToken) { + InlinePresentation presentation, int width, int height, IBinder hostInputToken, + int displayId) { if (hostInputToken == null) { try { callback.onError(); @@ -70,26 +72,46 @@ public abstract class InlineSuggestionRenderService extends Service { } return; } - final SurfaceControlViewHost host = new SurfaceControlViewHost(this, this.getDisplay(), - hostInputToken); - final SurfaceControl surface = host.getSurfacePackage().getSurfaceControl(); - final View suggestionView = onRenderSuggestion(presentation, width, height); - - final InlineSuggestionRoot suggestionRoot = new InlineSuggestionRoot(this, callback); - suggestionRoot.addView(suggestionView); - suggestionRoot.setOnClickListener((v) -> { - try { - callback.onAutofill(); - } catch (RemoteException e) { - Log.w(TAG, "RemoteException calling onAutofill()"); + // When we create the UI it should be for the IME display + updateDisplay(displayId); + try { + final View suggestionView = onRenderSuggestion(presentation, width, height); + if (suggestionView == null) { + try { + callback.onError(); + } catch (RemoteException e) { + Log.w(TAG, "Null suggestion view returned by renderer"); + } + return; } - }); - WindowManager.LayoutParams lp = - new WindowManager.LayoutParams(width, height, - WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT); - host.addView(suggestionRoot, lp); + final InlineSuggestionRoot suggestionRoot = new InlineSuggestionRoot(this, callback); + suggestionRoot.addView(suggestionView); + WindowManager.LayoutParams lp = + new WindowManager.LayoutParams(width, height, + WindowManager.LayoutParams.TYPE_APPLICATION, 0, + PixelFormat.TRANSPARENT); + + final SurfaceControlViewHost host = new SurfaceControlViewHost(this, getDisplay(), + hostInputToken); + host.addView(suggestionRoot, lp); + suggestionRoot.setOnClickListener((v) -> { + try { + callback.onAutofill(); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException calling onAutofill()"); + } + }); + + sendResult(callback, host.getSurfacePackage().getSurfaceControl()); + } finally { + updateDisplay(Display.DEFAULT_DISPLAY); + } + } + + private void sendResult(@NonNull IInlineSuggestionUiCallback callback, + @Nullable SurfaceControl surface) { try { callback.onContent(surface); } catch (RemoteException e) { @@ -105,11 +127,11 @@ public abstract class InlineSuggestionRenderService extends Service { @Override public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback, @NonNull InlinePresentation presentation, int width, int height, - @Nullable IBinder hostInputToken) { + @Nullable IBinder hostInputToken, int displayId) { mHandler.sendMessage(obtainMessage( InlineSuggestionRenderService::handleRenderSuggestion, InlineSuggestionRenderService.this, callback, presentation, - width, height, hostInputToken)); + width, height, hostInputToken, displayId)); } }.asBinder(); } diff --git a/core/java/android/service/controls/Control.java b/core/java/android/service/controls/Control.java index 2d1d0ede62ca..0cffe7180923 100644 --- a/core/java/android/service/controls/Control.java +++ b/core/java/android/service/controls/Control.java @@ -394,6 +394,11 @@ public final class Control implements Parcelable { return this; } + /** + * @param deviceType the device type for the {@link Control}. Setting an invalid value not + * in {@link DeviceTypes} will set it to {@link DeviceTypes#TYPE_UNKNOWN}. + * @return {@code this} + */ @NonNull public StatelessBuilder setDeviceType(@DeviceTypes.DeviceType int deviceType) { if (!DeviceTypes.validDeviceType(deviceType)) { @@ -416,6 +421,10 @@ public final class Control implements Parcelable { return this; } + /** + * @param subtitle the user facing subtitle for the {@link Control} + * @return {@code this} + */ @NonNull public StatelessBuilder setSubtitle(@NonNull CharSequence subtitle) { Preconditions.checkNotNull(subtitle); @@ -423,12 +432,22 @@ public final class Control implements Parcelable { return this; } + /** + * @param structure the user facing name of the structure for the {@link Control}. + * {@code null} indicates that it's not associated with any structure. + * @return {@code this} + */ @NonNull public StatelessBuilder setStructure(@Nullable CharSequence structure) { mStructure = structure; return this; } + /** + * @param zone the user facing name of the zone for the {@link Control}. {@code null} + * indicates that it's not associated with any zone. + * @return {@code this} + */ @NonNull public StatelessBuilder setZone(@Nullable CharSequence zone) { mZone = zone; @@ -446,12 +465,20 @@ public final class Control implements Parcelable { return this; } + /** + * @param customIcon an {@link Icon} to override the one determined by the device type. + * @return {@code this} + */ @NonNull public StatelessBuilder setCustomIcon(@Nullable Icon customIcon) { mCustomIcon = customIcon; return this; } + /** + * @param customColor a list of colors to override the ones determined by the device type. + * @return {@code this} + */ @NonNull public StatelessBuilder setCustomColor(@Nullable ColorStateList customColor) { mCustomColor = customColor; @@ -459,7 +486,7 @@ public final class Control implements Parcelable { } /** - * Build a {@link Control} + * Build a stateless {@link Control} * @return a valid {@link Control} */ @NonNull @@ -482,7 +509,7 @@ public final class Control implements Parcelable { /** * Builder class for {@link Control}. * - * This class facilitates the creation of {@link Control}. + * This class facilitates the creation of {@link Control} with an associated state. * It provides the following defaults for non-optional parameters: * <ul> * <li> Device type: {@link DeviceTypes#TYPE_UNKNOWN} @@ -551,6 +578,11 @@ public final class Control implements Parcelable { return this; } + /** + * @param deviceType the device type for the {@link Control}. Setting an invalid value not + * in {@link DeviceTypes} will set it to {@link DeviceTypes#TYPE_UNKNOWN}. + * @return {@code this} + */ @NonNull public StatefulBuilder setDeviceType(@DeviceTypes.DeviceType int deviceType) { if (!DeviceTypes.validDeviceType(deviceType)) { @@ -573,6 +605,10 @@ public final class Control implements Parcelable { return this; } + /** + * @param subtitle the user facing subtitle for the {@link Control} + * @return {@code this} + */ @NonNull public StatefulBuilder setSubtitle(@NonNull CharSequence subtitle) { Preconditions.checkNotNull(subtitle); @@ -580,12 +616,22 @@ public final class Control implements Parcelable { return this; } + /** + * @param structure the user facing name of the structure for the {@link Control}. + * {@code null} indicates that it's not associated with any structure. + * @return {@code this} + */ @NonNull public StatefulBuilder setStructure(@Nullable CharSequence structure) { mStructure = structure; return this; } + /** + * @param zone the user facing name of the zone for the {@link Control}. {@code null} + * indicates that it's not associated with any zone. + * @return {@code this} + */ @NonNull public StatefulBuilder setZone(@Nullable CharSequence zone) { mZone = zone; @@ -603,18 +649,31 @@ public final class Control implements Parcelable { return this; } + /** + * @param customIcon an {@link Icon} to override the one determined by the device type. + * @return {@code this} + */ @NonNull public StatefulBuilder setCustomIcon(@Nullable Icon customIcon) { mCustomIcon = customIcon; return this; } + /** + * @param customColor a list of colors to override the ones determined by the device type. + * @return {@code this} + */ @NonNull public StatefulBuilder setCustomColor(@Nullable ColorStateList customColor) { mCustomColor = customColor; return this; } + /** + * @param status the status of the {@link Control}. Setting an invalid value not in + * {@link Control} will set it to {@link Control#STATUS_UNKNOWN}. + * @return {@code this} + */ @NonNull public StatefulBuilder setStatus(@Status int status) { if (status < 0 || status >= NUM_STATUS) { @@ -626,6 +685,10 @@ public final class Control implements Parcelable { return this; } + /** + * @param controlTemplate a template for the {@link Control} + * @return {@code this} + */ @NonNull public StatefulBuilder setControlTemplate(@NonNull ControlTemplate controlTemplate) { Preconditions.checkNotNull(controlTemplate); @@ -633,6 +696,10 @@ public final class Control implements Parcelable { return this; } + /** + * @param statusText a user facing text representing the status of the {@link Control}. + * @return {@code this} + */ @NonNull public StatefulBuilder setStatusText(@NonNull CharSequence statusText) { Preconditions.checkNotNull(statusText); @@ -640,6 +707,10 @@ public final class Control implements Parcelable { return this; } + /** + * Build a stateless {@link Control} + * @return a valid {@link Control} + */ @NonNull public Control build() { return new Control(mControlId, diff --git a/core/java/android/service/controls/DeviceTypes.java b/core/java/android/service/controls/DeviceTypes.java index 8dbb9cf8f9f7..6594d2cf4ba2 100644 --- a/core/java/android/service/controls/DeviceTypes.java +++ b/core/java/android/service/controls/DeviceTypes.java @@ -21,6 +21,9 @@ import android.annotation.IntDef; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +/** + * Device types for {@link Control}. + */ public class DeviceTypes { // Update this when adding new concrete types. Does not count TYPE_UNKNOWN diff --git a/core/java/android/service/controls/actions/BooleanAction.java b/core/java/android/service/controls/actions/BooleanAction.java index 02593353bfc8..b794eadf0f90 100644 --- a/core/java/android/service/controls/actions/BooleanAction.java +++ b/core/java/android/service/controls/actions/BooleanAction.java @@ -19,10 +19,15 @@ package android.service.controls.actions; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Bundle; +import android.service.controls.Control; +import android.service.controls.templates.ToggleRangeTemplate; import android.service.controls.templates.ToggleTemplate; /** - * Action sent by a {@link ToggleTemplate} + * Action sent by user toggling a {@link Control} between checked/unchecked. + * + * This action is available when the {@link Control} was constructed with either a + * {@link ToggleTemplate} or a {@link ToggleRangeTemplate}. */ public final class BooleanAction extends ControlAction { @@ -40,8 +45,8 @@ public final class BooleanAction extends ControlAction { } /** - * @param templateId the identifier of the {@link ToggleTemplate} that originated this action. - * @param newState new value for the state displayed by the {@link ToggleTemplate}. + * @param templateId the identifier of the template that originated this action. + * @param newState new value for the state displayed by the template. * @param challengeValue a value sent by the user along with the action to authenticate. {@code} * null is sent when no authentication is needed or has not been * requested. @@ -64,8 +69,7 @@ public final class BooleanAction extends ControlAction { /** * The new state set for the button in the corresponding {@link ToggleTemplate}. * - * @return {@code true} if the button was toggled from an {@code off} state to an {@code on} - * state. + * @return {@code true} if the button was toggled from unchecked to checked. */ public boolean getNewState() { return mNewState; diff --git a/core/java/android/service/controls/actions/CommandAction.java b/core/java/android/service/controls/actions/CommandAction.java index 84d60805c3e9..a560fa4268aa 100644 --- a/core/java/android/service/controls/actions/CommandAction.java +++ b/core/java/android/service/controls/actions/CommandAction.java @@ -19,15 +19,32 @@ package android.service.controls.actions; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Bundle; +import android.service.controls.Control; +import android.service.controls.templates.StatelessTemplate; +/** + * A simple {@link ControlAction} indicating that the user has interacted with a {@link Control} + * created using a {@link StatelessTemplate}. + */ public final class CommandAction extends ControlAction { private static final @ActionType int TYPE = TYPE_COMMAND; + /** + * @param templateId the identifier of the {@link StatelessTemplate} that originated this + * action. + * @param challengeValue a value sent by the user along with the action to authenticate. {@code} + * null is sent when no authentication is needed or has not been + * requested. + */ public CommandAction(@NonNull String templateId, @Nullable String challengeValue) { super(templateId, challengeValue); } + /** + * @param templateId the identifier of the {@link StatelessTemplate} that originated this + * action. + */ public CommandAction(@NonNull String templateId) { this(templateId, null); } @@ -40,6 +57,9 @@ public final class CommandAction extends ControlAction { super(b); } + /** + * @return {@link ControlAction#TYPE_COMMAND} + */ @Override public int getActionType() { return TYPE; diff --git a/core/java/android/service/controls/actions/ControlAction.java b/core/java/android/service/controls/actions/ControlAction.java index 4141da805b5a..45e63d772d73 100644 --- a/core/java/android/service/controls/actions/ControlAction.java +++ b/core/java/android/service/controls/actions/ControlAction.java @@ -21,6 +21,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Bundle; +import android.service.controls.Control; import android.service.controls.IControlsActionCallback; import android.service.controls.templates.ControlTemplate; import android.util.Log; @@ -31,7 +32,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * An abstract action that is executed from a {@link ControlTemplate}. + * An abstract action indicating a user interaction with a {@link Control}. * * The action may have a value to authenticate the input, when the provider has requested it to * complete the action. @@ -58,6 +59,9 @@ public abstract class ControlAction { }) public @interface ActionType {}; + /** + * Object returned when there is an unparcelling error. + */ public static final @NonNull ControlAction ERROR_ACTION = new ControlAction() { @Override public int getActionType() { @@ -65,6 +69,9 @@ public abstract class ControlAction { } }; + /** + * The identifier of {@link #ERROR_ACTION} + */ public static final @ActionType int TYPE_ERROR = -1; /** @@ -77,10 +84,19 @@ public abstract class ControlAction { */ public static final @ActionType int TYPE_FLOAT = 2; + /** + * The identifier of {@link MultiFloatAction}. + */ public static final @ActionType int TYPE_MULTI_FLOAT = 3; + /** + * The identifier of {@link ModeAction}. + */ public static final @ActionType int TYPE_MODE = 4; + /** + * The identifier of {@link CommandAction}. + */ public static final @ActionType int TYPE_COMMAND = 5; diff --git a/core/java/android/service/controls/actions/ModeAction.java b/core/java/android/service/controls/actions/ModeAction.java index ca40974d929b..c0e24adb18f3 100644 --- a/core/java/android/service/controls/actions/ModeAction.java +++ b/core/java/android/service/controls/actions/ModeAction.java @@ -19,7 +19,15 @@ package android.service.controls.actions; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Bundle; +import android.service.controls.Control; +import android.service.controls.templates.TemperatureControlTemplate; +/** + * Action sent by the user to indicate a change of mode. + * + * This action is available when the {@link Control} was created with a + * {@link TemperatureControlTemplate}. + */ public final class ModeAction extends ControlAction { private static final @ActionType int TYPE = TYPE_MODE; @@ -27,16 +35,32 @@ public final class ModeAction extends ControlAction { private final int mNewMode; + /** + * @return {@link ControlAction#TYPE_MODE}. + */ @Override public int getActionType() { return TYPE; } + /** + * @param templateId the identifier of the {@link TemperatureControlTemplate} that originated + * this action. + * @param newMode new value for the mode. + * @param challengeValue a value sent by the user along with the action to authenticate. {@code} + * null is sent when no authentication is needed or has not been + * requested. + */ public ModeAction(@NonNull String templateId, int newMode, @Nullable String challengeValue) { super(templateId, challengeValue); mNewMode = newMode; } + /** + * @param templateId the identifier of the {@link TemperatureControlTemplate} that originated + * this action. + * @param newMode new value for the mode. + */ public ModeAction(@NonNull String templateId, int newMode) { this(templateId, newMode, null); } diff --git a/core/java/android/service/controls/templates/ControlTemplate.java b/core/java/android/service/controls/templates/ControlTemplate.java index a5156e36f2ce..30efd80db106 100644 --- a/core/java/android/service/controls/templates/ControlTemplate.java +++ b/core/java/android/service/controls/templates/ControlTemplate.java @@ -57,6 +57,9 @@ public abstract class ControlTemplate { } }; + /** + * Object returned when there is an unparcelling error. + */ public static final @NonNull ControlTemplate ERROR_TEMPLATE = new ControlTemplate("") { @Override public int getTemplateType() { @@ -80,6 +83,9 @@ public abstract class ControlTemplate { }) public @interface TemplateType {} + /** + * Type identifier of {@link #ERROR_TEMPLATE}. + */ public static final @TemplateType int TYPE_ERROR = -1; /** @@ -102,10 +108,19 @@ public abstract class ControlTemplate { */ public static final @TemplateType int TYPE_THUMBNAIL = 3; + /** + * Type identifier of {@link ToggleRangeTemplate}. + */ public static final @TemplateType int TYPE_TOGGLE_RANGE = 6; + /** + * Type identifier of {@link TemperatureControlTemplate}. + */ public static final @TemplateType int TYPE_TEMPERATURE = 7; + /** + * Type identifier of {@link StatelessTemplate}. + */ public static final @TemplateType int TYPE_STATELESS = 8; private @NonNull final String mTemplateId; diff --git a/core/java/android/service/controls/templates/RangeTemplate.java b/core/java/android/service/controls/templates/RangeTemplate.java index fe0d16707c9d..0d977d30f7c1 100644 --- a/core/java/android/service/controls/templates/RangeTemplate.java +++ b/core/java/android/service/controls/templates/RangeTemplate.java @@ -19,7 +19,6 @@ package android.service.controls.templates; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Bundle; -import android.os.Parcel; import android.service.controls.Control; import android.service.controls.actions.FloatAction; @@ -85,7 +84,7 @@ public final class RangeTemplate extends ControlTemplate { } /** - * Construct a new {@link RangeTemplate} from a {@link Parcel}. + * Construct a new {@link RangeTemplate} from a {@link Bundle}. * * @throws IllegalArgumentException if the parameters passed do not make a valid range * @see RangeTemplate#RangeTemplate(String, float, float, float, float, CharSequence) diff --git a/core/java/android/service/controls/templates/StatelessTemplate.java b/core/java/android/service/controls/templates/StatelessTemplate.java index 3f98beac9cf8..c052412f0e4c 100644 --- a/core/java/android/service/controls/templates/StatelessTemplate.java +++ b/core/java/android/service/controls/templates/StatelessTemplate.java @@ -18,22 +18,36 @@ package android.service.controls.templates; import android.annotation.NonNull; import android.os.Bundle; +import android.service.controls.Control; +import android.service.controls.actions.CommandAction; +/** + * A template for a {@link Control} which has no state. + * + * @see CommandAction + */ public final class StatelessTemplate extends ControlTemplate { + /** + * @return {@link ControlTemplate#TYPE_STATELESS} + */ @Override public int getTemplateType() { return TYPE_STATELESS; } /** - * @param b + * Construct a new {@link StatelessTemplate} from a {@link Bundle} * @hide */ StatelessTemplate(@NonNull Bundle b) { super(b); } + /** + * Construct a new {@link StatelessTemplate} + * @param templateId the identifier for this template + */ public StatelessTemplate(@NonNull String templateId) { super(templateId); } diff --git a/core/java/android/service/controls/templates/TemperatureControlTemplate.java b/core/java/android/service/controls/templates/TemperatureControlTemplate.java index 9d8dca6278c5..0818c7e4fb82 100644 --- a/core/java/android/service/controls/templates/TemperatureControlTemplate.java +++ b/core/java/android/service/controls/templates/TemperatureControlTemplate.java @@ -19,6 +19,7 @@ package android.service.controls.templates; import android.annotation.IntDef; import android.annotation.NonNull; import android.os.Bundle; +import android.service.controls.Control; import android.util.Log; import com.android.internal.util.Preconditions; @@ -26,6 +27,13 @@ import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +/** + * A template for a temperature related {@link Control} that supports multiple modes. + * + * Both the current mode and the active mode for the control can be specified. The combination of + * the {@link Control#getDeviceType} and the current and active mode will determine colors and + * transitions for the UI element. + */ public final class TemperatureControlTemplate extends ControlTemplate { private static final String TAG = "ThermostatTemplate"; @@ -51,6 +59,7 @@ public final class TemperatureControlTemplate extends ControlTemplate { public @interface Mode {} private static final int NUM_MODES = 6; + public static final @Mode int MODE_UNKNOWN = 0; public static final @Mode int MODE_OFF = 1; @@ -102,6 +111,18 @@ public final class TemperatureControlTemplate extends ControlTemplate { private final @Mode int mCurrentActiveMode; private final @ModeFlag int mModes; + /** + * Construct a new {@link TemperatureControlTemplate}. + * + * The current and active mode have to be among the ones supported by the flags. + * + * @param templateId the identifier for this template object + * @param controlTemplate a template to use for interaction with the user + * @param currentMode the current mode for the {@link Control} + * @param currentActiveMode the current active mode for the {@link Control} + * @param modesFlag a flag representing the available modes for the {@link Control} + * @throws IllegalArgumentException if the parameters passed do not make a valid template. + */ public TemperatureControlTemplate(@NonNull String templateId, @NonNull ControlTemplate controlTemplate, @Mode int currentMode, @@ -179,6 +200,9 @@ public final class TemperatureControlTemplate extends ControlTemplate { return mModes; } + /** + * @return {@link ControlTemplate#TYPE_TEMPERATURE} + */ @Override public int getTemplateType() { return TYPE; diff --git a/core/java/android/service/controls/templates/ToggleRangeTemplate.java b/core/java/android/service/controls/templates/ToggleRangeTemplate.java index af43b94699d2..cd6a2fc45612 100644 --- a/core/java/android/service/controls/templates/ToggleRangeTemplate.java +++ b/core/java/android/service/controls/templates/ToggleRangeTemplate.java @@ -18,9 +18,16 @@ package android.service.controls.templates; import android.annotation.NonNull; import android.os.Bundle; +import android.service.controls.Control; import com.android.internal.util.Preconditions; +/** + * A template for a {@link Control} supporting toggling and a range. + * + * @see ToggleTemplate + * @see RangeTemplate + */ public final class ToggleRangeTemplate extends ControlTemplate { private static final @TemplateType int TYPE = TYPE_TOGGLE_RANGE; @@ -40,6 +47,12 @@ public final class ToggleRangeTemplate extends ControlTemplate { mRangeTemplate = new RangeTemplate(b.getBundle(KEY_RANGE)); } + /** + * Constructs a new {@link ToggleRangeTemplate}. + * @param templateId the identifier for this template. + * @param button a {@link ControlButton} to use for the toggle interface + * @param range a {@link RangeTemplate} to use for the range interface + */ public ToggleRangeTemplate(@NonNull String templateId, @NonNull ControlButton button, @NonNull RangeTemplate range) { @@ -50,6 +63,14 @@ public final class ToggleRangeTemplate extends ControlTemplate { mRangeTemplate = range; } + /** + * Constructs a new {@link ToggleRangeTemplate}. + * @param templateId the identifier for this template. + * @param checked true if the toggle should be rendered as active. + * @param actionDescription action description for the button. + * @param range {@link RangeTemplate} to use for the range interface + * @see ControlButton + */ public ToggleRangeTemplate(@NonNull String templateId, boolean checked, @NonNull CharSequence actionDescription, @@ -86,6 +107,9 @@ public final class ToggleRangeTemplate extends ControlTemplate { return mControlButton.getActionDescription(); } + /** + * @return {@link ControlTemplate#TYPE_TOGGLE_RANGE} + */ @Override public int getTemplateType() { return TYPE; diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java new file mode 100644 index 000000000000..8cdd24e0884d --- /dev/null +++ b/core/java/android/service/dreams/DreamActivity.java @@ -0,0 +1,59 @@ +/* + * 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 android.service.dreams; + +import android.annotation.Nullable; +import android.app.Activity; +import android.os.Bundle; + +/** + * The Activity used by the {@link DreamService} to draw screensaver content + * on the screen. This activity runs in dream application's process, but is started by a + * specialized method: {@link com.android.server.wm.ActivityTaskManagerService#startDreamActivity}. + * Hence, it does not have to be declared in the dream application's manifest. + * + * We use an activity as the dream canvas, because it interacts easier with other activities on + * the screen (compared to a hover window). However, the DreamService is in charge of the dream and + * it receives all Window.Callbacks from its main window. Since a window can have only one callback + * receiver, the activity will not receive any window callbacks. + * + * Prior to the DreamActivity, the DreamService used to work with a hovering window and give the + * screensaver application control over that window. The DreamActivity is a replacement to that + * hover window. Using an activity allows for better-defined interactions with the rest of the + * activities on screen. The switch to DreamActivity should be transparent to the screensaver + * application, i.e. the application will still use DreamService APIs and not notice that the + * system is using an activity behind the scenes. + * + * @hide + */ +public class DreamActivity extends Activity { + static final String EXTRA_CALLBACK = "binder"; + + public DreamActivity() {} + + @Override + public void onCreate(@Nullable Bundle bundle) { + super.onCreate(bundle); + + DreamService.DreamServiceWrapper callback = + (DreamService.DreamServiceWrapper) getIntent().getIBinderExtra(EXTRA_CALLBACK); + + if (callback != null) { + callback.onActivityCreated(this); + } + } +} diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java index ff7cef9d945a..41fdd0bfe477 100644 --- a/core/java/android/service/dreams/DreamManagerInternal.java +++ b/core/java/android/service/dreams/DreamManagerInternal.java @@ -16,6 +16,8 @@ package android.service.dreams; +import android.content.ComponentName; + /** * Dream manager local system service interface. * @@ -42,4 +44,13 @@ public abstract class DreamManagerInternal { * Called by the power manager to determine whether a dream is running. */ public abstract boolean isDreaming(); + + /** + * Called by the ActivityTaskManagerService to verify that the startDreamActivity + * request comes from the current active dream component. + * + * @param doze If true returns the current active doze component. Otherwise, returns the + * active dream component. + */ + public abstract ComponentName getActiveDreamComponent(boolean doze); } diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index de4a5511fcdb..002d4b8d195d 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -15,25 +15,29 @@ */ package android.service.dreams; +import static android.view.WindowManager.LayoutParams.TYPE_DREAM; + import android.annotation.IdRes; import android.annotation.LayoutRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.app.Activity; +import android.app.ActivityTaskManager; import android.app.AlarmManager; import android.app.Service; import android.compat.annotation.UnsupportedAppUsage; import android.content.Intent; -import android.graphics.PixelFormat; -import android.graphics.drawable.ColorDrawable; import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.IRemoteCallback; +import android.os.Looper; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.Log; import android.util.MathUtils; import android.util.Slog; import android.view.ActionMode; @@ -48,10 +52,8 @@ import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; -import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; -import com.android.internal.policy.PhoneWindow; import com.android.internal.util.DumpUtils; import com.android.internal.util.DumpUtils.Dump; @@ -176,10 +178,11 @@ public class DreamService extends Service implements Window.Callback { */ public static final String DREAM_META_DATA = "android.service.dream"; - private final IDreamManager mSandman; - private final Handler mHandler = new Handler(); - private IBinder mWindowToken; + private final IDreamManager mDreamManager; + private final Handler mHandler = new Handler(Looper.getMainLooper()); + private IBinder mDreamToken; private Window mWindow; + private Activity mActivity; private boolean mInteractive; private boolean mLowProfile = true; private boolean mFullscreen; @@ -195,8 +198,11 @@ public class DreamService extends Service implements Window.Callback { private boolean mDebug = false; + private DreamServiceWrapper mDreamServiceWrapper; + private Runnable mDispatchAfterOnAttachedToWindow; + public DreamService() { - mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); + mDreamManager = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); } /** @@ -602,6 +608,8 @@ public class DreamService extends Service implements Window.Callback { * Marks this dream as windowless. Only available to doze dreams. * * @hide + * + * TODO: Remove @UnsupportedAppUsage. */ @UnsupportedAppUsage public void setWindowless(boolean windowless) { @@ -670,14 +678,14 @@ public class DreamService extends Service implements Window.Callback { } private void updateDoze() { - if (mWindowToken == null) { - Slog.w(TAG, "Updating doze without a window token."); + if (mDreamToken == null) { + Slog.w(TAG, "Updating doze without a dream token."); return; } if (mDozing) { try { - mSandman.startDozing(mWindowToken, mDozeScreenState, mDozeScreenBrightness); + mDreamManager.startDozing(mDreamToken, mDozeScreenState, mDozeScreenBrightness); } catch (RemoteException ex) { // system server died } @@ -700,7 +708,7 @@ public class DreamService extends Service implements Window.Callback { if (mDozing) { mDozing = false; try { - mSandman.stopDozing(mWindowToken); + mDreamManager.stopDozing(mDreamToken); } catch (RemoteException ex) { // system server died } @@ -882,7 +890,8 @@ public class DreamService extends Service implements Window.Callback { @Override public final IBinder onBind(Intent intent) { if (mDebug) Slog.v(TAG, "onBind() intent = " + intent); - return new DreamServiceWrapper(); + mDreamServiceWrapper = new DreamServiceWrapper(); + return mDreamServiceWrapper; } /** @@ -895,20 +904,26 @@ public class DreamService extends Service implements Window.Callback { public final void finish() { if (mDebug) Slog.v(TAG, "finish(): mFinished=" + mFinished); + if (mActivity != null) { + if (!mActivity.isFinishing()) { + // In case the activity is not finished yet, do it now. + mActivity.finishAndRemoveTask(); + return; + } + } else if (!mWindowless) { + Slog.w(TAG, "Finish was called before the dream was attached."); + } + if (!mFinished) { mFinished = true; - if (mWindowToken == null) { - Slog.w(TAG, "Finish was called before the dream was attached."); - } else { - try { - mSandman.finishSelf(mWindowToken, true /*immediate*/); - } catch (RemoteException ex) { - // system server died - } + try { + // finishSelf will unbind the dream controller from the dream service. This will + // trigger DreamService.this.onDestroy and DreamService.this will die. + mDreamManager.finishSelf(mDreamToken, true /*immediate*/); + } catch (RemoteException ex) { + // system server died } - - stopSelf(); // if launched via any other means } } @@ -938,11 +953,11 @@ public class DreamService extends Service implements Window.Callback { // Now tell the system we are waking gently, unless we already told // it we were finishing immediately. if (!fromSystem && !mFinished) { - if (mWindowToken == null) { + if (mActivity == null) { Slog.w(TAG, "WakeUp was called before the dream was attached."); } else { try { - mSandman.finishSelf(mWindowToken, false /*immediate*/); + mDreamManager.finishSelf(mDreamToken, false /*immediate*/); } catch (RemoteException ex) { // system server died } @@ -977,20 +992,14 @@ public class DreamService extends Service implements Window.Callback { onDreamingStopped(); } - if (mWindow != null) { - // force our window to be removed synchronously - if (mDebug) Slog.v(TAG, "detach(): Removing window from window manager"); - mWindow.getWindowManager().removeViewImmediate(mWindow.getDecorView()); - mWindow = null; + if (mActivity != null && !mActivity.isFinishing()) { + mActivity.finishAndRemoveTask(); + } else { + finish(); } - if (mWindowToken != null) { - // the following will print a log message if it finds any other leaked windows - WindowManagerGlobal.getInstance().closeAll(mWindowToken, - this.getClass().getName(), "Dream"); - mWindowToken = null; - mCanDoze = false; - } + mDreamToken = null; + mCanDoze = false; } /** @@ -998,95 +1007,107 @@ public class DreamService extends Service implements Window.Callback { * * Must run on mHandler. * - * @param windowToken A window token that will allow a window to be created in the correct layer. + * @param dreamToken Token for this dream service. * @param started A callback that will be invoked once onDreamingStarted has completed. */ - private final void attach(IBinder windowToken, boolean canDoze, IRemoteCallback started) { - if (mWindowToken != null) { - Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken); + private void attach(IBinder dreamToken, boolean canDoze, IRemoteCallback started) { + if (mDreamToken != null) { + Slog.e(TAG, "attach() called when dream with token=" + mDreamToken + + " already attached"); return; } if (mFinished || mWaking) { Slog.w(TAG, "attach() called after dream already finished"); try { - mSandman.finishSelf(windowToken, true /*immediate*/); + mDreamManager.finishSelf(dreamToken, true /*immediate*/); } catch (RemoteException ex) { // system server died } return; } - mWindowToken = windowToken; + mDreamToken = dreamToken; mCanDoze = canDoze; if (mWindowless && !mCanDoze) { throw new IllegalStateException("Only doze dreams can be windowless"); } - if (!mWindowless) { - mWindow = new PhoneWindow(this); - mWindow.setCallback(this); - mWindow.requestFeature(Window.FEATURE_NO_TITLE); - mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000)); - mWindow.setFormat(PixelFormat.OPAQUE); - if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s", - windowToken, WindowManager.LayoutParams.TYPE_DREAM)); - - WindowManager.LayoutParams lp = mWindow.getAttributes(); - lp.type = WindowManager.LayoutParams.TYPE_DREAM; - lp.token = windowToken; - lp.windowAnimations = com.android.internal.R.style.Animation_Dream; - lp.flags |= ( WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR - | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD - | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON - | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0) - | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0) - ); - mWindow.setAttributes(lp); - // Workaround: Currently low-profile and in-window system bar backgrounds don't go - // along well. Dreams usually don't need such bars anyways, so disable them by default. - mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - mWindow.setWindowManager(null, windowToken, "dream", true); + mDispatchAfterOnAttachedToWindow = () -> { + if (mWindow != null || mWindowless) { + mStarted = true; + try { + onDreamingStarted(); + } finally { + try { + started.sendResult(null); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + }; - applySystemUiVisibilityFlags( - (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), - View.SYSTEM_UI_FLAG_LOW_PROFILE); + // We need to defer calling onDreamingStarted until after the activity is created. + // If the dream is windowless, we can call it immediately. Otherwise, we wait + // for the DreamActivity to report onActivityCreated via + // DreamServiceWrapper.onActivityCreated. + if (!mWindowless) { + Intent i = new Intent(this, DreamActivity.class); + i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + i.putExtra(DreamActivity.EXTRA_CALLBACK, mDreamServiceWrapper); try { - getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); - } catch (WindowManager.BadTokenException ex) { - // This can happen because the dream manager service will remove the token - // immediately without necessarily waiting for the dream to start. - // We should receive a finish message soon. - Slog.i(TAG, "attach() called after window token already removed, dream will " - + "finish soon"); - mWindow = null; - return; + if (!ActivityTaskManager.getService().startDreamActivity(i)) { + detach(); + return; + } + } catch (RemoteException e) { + Log.w(TAG, "Could not connect to activity task manager to start dream activity"); + e.rethrowFromSystemServer(); } + } else { + mDispatchAfterOnAttachedToWindow.run(); } - // We need to defer calling onDreamingStarted until after onWindowAttached, - // which is posted to the handler by addView, so we post onDreamingStarted - // to the handler also. Need to watch out here in case detach occurs before - // this callback is invoked. - mHandler.post(new Runnable() { - @Override - public void run() { - if (mWindow != null || mWindowless) { - if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()"); - mStarted = true; - try { - onDreamingStarted(); - } finally { - try { - started.sendResult(null); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + } + + private void onWindowCreated(Window w) { + mWindow = w; + mWindow.setCallback(this); + mWindow.setType(TYPE_DREAM); + mWindow.requestFeature(Window.FEATURE_NO_TITLE); + + WindowManager.LayoutParams lp = mWindow.getAttributes(); + lp.windowAnimations = com.android.internal.R.style.Animation_Dream; + lp.flags |= (WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR + | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED + | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD + | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON + | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED + | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0) + | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0) + ); + mWindow.setAttributes(lp); + // Workaround: Currently low-profile and in-window system bar backgrounds don't go + // along well. Dreams usually don't need such bars anyways, so disable them by default. + mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + + applySystemUiVisibilityFlags( + (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), + View.SYSTEM_UI_FLAG_LOW_PROFILE); + + mWindow.getDecorView().addOnAttachStateChangeListener( + new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + mDispatchAfterOnAttachedToWindow.run(); } - } - } - }); + + @Override + public void onViewDetachedFromWindow(View v) { + finish(); + } + }); } private boolean getWindowFlagValue(int flag, boolean defaultValue) { @@ -1131,10 +1152,10 @@ public class DreamService extends Service implements Window.Callback { /** @hide */ protected void dumpOnHandler(FileDescriptor fd, PrintWriter pw, String[] args) { pw.print(TAG + ": "); - if (mWindowToken == null) { + if (mFinished) { pw.println("stopped"); } else { - pw.println("running (token=" + mWindowToken + ")"); + pw.println("running (dreamToken=" + mDreamToken + ")"); } pw.println(" window: " + mWindow); pw.print(" flags:"); @@ -1156,36 +1177,32 @@ public class DreamService extends Service implements Window.Callback { return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON); } - private final class DreamServiceWrapper extends IDreamService.Stub { + /** + * The DreamServiceWrapper is used as a gateway to the system_server, where DreamController + * uses it to control the DreamService. It is also used to receive callbacks from the + * DreamActivity. + */ + final class DreamServiceWrapper extends IDreamService.Stub { @Override - public void attach(final IBinder windowToken, final boolean canDoze, + public void attach(final IBinder dreamToken, final boolean canDoze, IRemoteCallback started) { - mHandler.post(new Runnable() { - @Override - public void run() { - DreamService.this.attach(windowToken, canDoze, started); - } - }); + mHandler.post(() -> DreamService.this.attach(dreamToken, canDoze, started)); } @Override public void detach() { - mHandler.post(new Runnable() { - @Override - public void run() { - DreamService.this.detach(); - } - }); + mHandler.post(DreamService.this::detach); } @Override public void wakeUp() { - mHandler.post(new Runnable() { - @Override - public void run() { - DreamService.this.wakeUp(true /*fromSystem*/); - } - }); + mHandler.post(() -> DreamService.this.wakeUp(true /*fromSystem*/)); + } + + /** @hide */ + void onActivityCreated(DreamActivity a) { + mActivity = a; + onWindowCreated(a.getWindow()); } } } diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl index d254ffdb6986..6496de3e15a0 100644 --- a/core/java/android/service/dreams/IDreamManager.aidl +++ b/core/java/android/service/dreams/IDreamManager.aidl @@ -31,12 +31,14 @@ interface IDreamManager { void setDreamComponents(in ComponentName[] componentNames); @UnsupportedAppUsage ComponentName[] getDreamComponents(); - ComponentName getDefaultDreamComponent(); - void testDream(in ComponentName componentName); + ComponentName getDefaultDreamComponentForUser(int userId); + void testDream(int userId, in ComponentName componentName); @UnsupportedAppUsage boolean isDreaming(); void finishSelf(in IBinder token, boolean immediate); void startDozing(in IBinder token, int screenState, int screenBrightness); void stopDozing(in IBinder token); void forceAmbientDisplayEnabled(boolean enabled); + ComponentName[] getDreamComponentsForUser(int userId); + void setDreamComponentsForUser(int userId, in ComponentName[] componentNames); } diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java index e976e18602c1..975e75ccaeeb 100644 --- a/core/java/android/service/notification/NotificationAssistantService.java +++ b/core/java/android/service/notification/NotificationAssistantService.java @@ -184,7 +184,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS /** * Implement this to know when the notification panel is revealed * - * @param items Number of items on the panel at time of opening + * @param items Number of notifications on the panel at time of opening */ public void onPanelRevealed(int items) { diff --git a/core/java/android/service/notification/OWNERS b/core/java/android/service/notification/OWNERS new file mode 100644 index 000000000000..2e94be5bf329 --- /dev/null +++ b/core/java/android/service/notification/OWNERS @@ -0,0 +1,4 @@ +juliacr@google.com +beverlyt@google.com +dsandler@android.com +pixel@google.com
\ No newline at end of file diff --git a/core/java/android/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java index 7b24ba997307..9a555c161300 100644 --- a/core/java/android/util/proto/ProtoOutputStream.java +++ b/core/java/android/util/proto/ProtoOutputStream.java @@ -32,7 +32,7 @@ import java.io.UnsupportedEncodingException; * <p> * This API is not as convenient or type safe as the standard protobuf * classes. If possible, the best recommended library is to use protobuf lite. - * However, in environements (such as the Android platform itself), a + * However, in environments (such as the Android platform itself), a * more memory efficient version is necessary. * * <p>Each write method takes an ID code from the protoc generated classes diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java index 31fc16188814..0ab856ec5b4d 100644 --- a/core/java/android/view/DisplayCutout.java +++ b/core/java/android/view/DisplayCutout.java @@ -554,26 +554,12 @@ public final class DisplayCutout { */ public DisplayCutout inset(int insetLeft, int insetTop, int insetRight, int insetBottom) { if (insetLeft == 0 && insetTop == 0 && insetRight == 0 && insetBottom == 0 - || isBoundsEmpty()) { + || (isBoundsEmpty() && mWaterfallInsets.equals(Insets.NONE))) { return this; } - Rect safeInsets = new Rect(mSafeInsets); - - // Note: it's not really well defined what happens when the inset is negative, because we - // don't know if the safe inset needs to expand in general. - if (insetTop > 0 || safeInsets.top > 0) { - safeInsets.top = atLeastZero(safeInsets.top - insetTop); - } - if (insetBottom > 0 || safeInsets.bottom > 0) { - safeInsets.bottom = atLeastZero(safeInsets.bottom - insetBottom); - } - if (insetLeft > 0 || safeInsets.left > 0) { - safeInsets.left = atLeastZero(safeInsets.left - insetLeft); - } - if (insetRight > 0 || safeInsets.right > 0) { - safeInsets.right = atLeastZero(safeInsets.right - insetRight); - } + Rect safeInsets = insetInsets(insetLeft, insetTop, insetRight, insetBottom, + new Rect(mSafeInsets)); // If we are not cutting off part of the cutout by insetting it on bottom/right, and we also // don't move it around, we can avoid the allocation and copy of the instance. @@ -581,6 +567,9 @@ public final class DisplayCutout { return this; } + Rect waterfallInsets = insetInsets(insetLeft, insetTop, insetRight, insetBottom, + mWaterfallInsets.toRect()); + Rect[] bounds = mBounds.getRects(); for (int i = 0; i < bounds.length; ++i) { if (!bounds[i].equals(ZERO_RECT)) { @@ -588,7 +577,27 @@ public final class DisplayCutout { } } - return new DisplayCutout(safeInsets, mWaterfallInsets, bounds, false /* copyArguments */); + return new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds, + false /* copyArguments */); + } + + private Rect insetInsets(int insetLeft, int insetTop, int insetRight, int insetBottom, + Rect insets) { + // Note: it's not really well defined what happens when the inset is negative, because we + // don't know if the safe inset needs to expand in general. + if (insetTop > 0 || insets.top > 0) { + insets.top = atLeastZero(insets.top - insetTop); + } + if (insetBottom > 0 || insets.bottom > 0) { + insets.bottom = atLeastZero(insets.bottom - insetBottom); + } + if (insetLeft > 0 || insets.left > 0) { + insets.left = atLeastZero(insets.left - insetLeft); + } + if (insetRight > 0 || insets.right > 0) { + insets.right = atLeastZero(insets.right - insetRight); + } + return insets; } /** diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl index cb82f168d991..1cf83a3f1e79 100644 --- a/core/java/android/view/IPinnedStackController.aidl +++ b/core/java/android/view/IPinnedStackController.aidl @@ -25,32 +25,8 @@ import android.graphics.Rect; * @hide */ interface IPinnedStackController { - - /** - * Notifies the controller that the PiP is currently minimized. - */ - oneway void setIsMinimized(boolean isMinimized); - /** * @return what WM considers to be the current device rotation. */ int getDisplayRotation(); - - /** - * Notifies the controller to actually start the PiP animation. - * The bounds would be calculated based on the last save reentry fraction internally. - * {@param destinationBounds} is the stack bounds of the final PiP window - * and {@param sourceRectHint} is the source bounds hint used when entering picture-in-picture, - * expect the same bound passed via IPinnedStackListener#onPrepareAnimation. - * {@param animationDuration} suggests the animation duration transitioning to PiP window. - */ - void startAnimation(in Rect destinationBounds, in Rect sourceRectHint, int animationDuration); - - /** - * Notifies the controller to reset on bounds animation, if there is any. - * This could happen when screen rotation is happening and we need to notify the WM to reset - * any running bounds animation on the pinned stack. - * {@param bounds} here is the final destination bounds. - */ - void resetBoundsAnimation(in Rect bounds); } diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl index d01c9330dcb2..596d55aebc6d 100644 --- a/core/java/android/view/IPinnedStackListener.aidl +++ b/core/java/android/view/IPinnedStackListener.aidl @@ -43,8 +43,7 @@ oneway interface IPinnedStackListener { * pinned stack (the final bounds if animating, the current bounds if not), * which may be helpful in calculating dependent animation bounds. */ - void onMovementBoundsChanged(in Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment); + void onMovementBoundsChanged(in Rect animatingBounds, boolean fromImeAdjustment); /** * Called when window manager decides to adjust the pinned stack bounds because of the IME, or @@ -55,12 +54,6 @@ oneway interface IPinnedStackListener { void onImeVisibilityChanged(boolean imeVisible, int imeHeight); /** - * Called when window manager decides to adjust the minimized state, or when the listener - * is first registered to allow the listener to synchronized its state with the controller. - */ - void onMinimizedStateChanged(boolean isMinimized); - - /** * Called when the set of actions for the current PiP activity changes, or when the listener * is first registered to allow the listener to synchronized its state with the controller. */ @@ -99,13 +92,4 @@ oneway interface IPinnedStackListener { * Called by the window manager when the aspect ratio is reset. */ void onAspectRatioChanged(float aspectRatio); - - /** - * Called by the window manager to notify the listener to prepare for PiP animation. - * Internally, the target bounds would be calculated from the given {@param aspectRatio} - * and {@param bounds}, the saved reentry snap fraction also contributes. - * Caller would wait for a IPinnedStackController#startAnimation callback to actually - * start the animation, see details in IPinnedStackController. - */ - void onPrepareAnimation(in Rect sourceRectHint, float aspectRatio, in Rect bounds); } diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl index 762366eb6295..a60a5cca08bd 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -67,23 +67,11 @@ interface IRecentsAnimationController { void setAnimationTargetsBehindSystemBars(boolean behindSystemBars); /** - * Informs the system that the primary split-screen stack should be minimized. - */ - void setSplitScreenMinimized(boolean minimized); - - /** * Hides the current input method if one is showing. */ void hideCurrentInputMethod(); /** - * This call is deprecated, use #setDeferCancelUntilNextTransition() instead - * TODO(138144750): Remove this method once there are no callers - * @deprecated - */ - void setCancelWithDeferredScreenshot(boolean screenshot); - - /** * Clean up the screenshot of previous task which was created during recents animation that * was cancelled by a stack order change. * diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 17303478fa50..73601d968040 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -444,8 +444,7 @@ interface IWindowManager WindowContentFrameStats getWindowContentFrameStats(IBinder token); /** - * @return the dock side the current docked stack is at; must be one of the - * WindowManagerGlobal.DOCKED_* values + * This is a no-op. */ @UnsupportedAppUsage int getDockedStackSide(); @@ -457,27 +456,11 @@ interface IWindowManager void setDockedStackDividerTouchRegion(in Rect touchableRegion); /** - * Registers a listener that will be called when the dock divider changes its visibility or when - * the docked stack gets added/removed. - */ - @UnsupportedAppUsage - void registerDockedStackListener(IDockedStackListener listener); - - /** * Registers a listener that will be called when the pinned stack state changes. */ void registerPinnedStackListener(int displayId, IPinnedStackListener listener); /** - * Updates the dim layer used while resizing. - * - * @param visible Whether the dim layer should be visible. - * @param targetWindowingMode The windowing mode of the stack the dim layer should be placed on. - * @param alpha The translucency of the dim layer, between 0 and 1. - */ - void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha); - - /** * Requests Keyboard Shortcuts from the displayed window. * * @param receiver The receiver to deliver the results to. diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java index 5f9c4801ee66..cb9e746543f6 100644 --- a/core/java/android/view/InputEvent.java +++ b/core/java/android/view/InputEvent.java @@ -233,6 +233,21 @@ public abstract class InputEvent implements Parcelable { return mSeq; } + /** + * Gets the ID of this event. This is generated when an event is created and preserved until its + * last stage. It won't change just because the event crosses process boundary, but should + * change when making a copy with modifications. + * <p> + * To avoid exposing app usage to other processes this ID is generated from a CSPRNG. Therefore + * there isn't 100% guarantee on the uniqueness of this ID, though the chance of ID collisions + * is considerably low. The rule of thumb is not to rely on the uniqueness for production logic, + * but a good source for tracking an event (e.g. logging and profiling). + * + * @return The ID of this event. + * @hide + */ + public abstract int getId(); + public int describeContents() { return 0; } diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index c91096e5aa25..e249c777caf6 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -1265,6 +1265,7 @@ public class KeyEvent extends InputEvent implements Parcelable { private KeyEvent mNext; + private int mId; @UnsupportedAppUsage private int mDeviceId; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @@ -1350,9 +1351,9 @@ public class KeyEvent extends InputEvent implements Parcelable { private static native String nativeKeyCodeToString(int keyCode); private static native int nativeKeyCodeFromString(String keyCode); + private static native int nativeNextId(); - private KeyEvent() { - } + private KeyEvent() {} /** * Create a new key event. @@ -1362,6 +1363,7 @@ public class KeyEvent extends InputEvent implements Parcelable { * @param code The key code. */ public KeyEvent(int action, int code) { + mId = nativeNextId(); mAction = action; mKeyCode = code; mRepeatCount = 0; @@ -1383,6 +1385,7 @@ public class KeyEvent extends InputEvent implements Parcelable { */ public KeyEvent(long downTime, long eventTime, int action, int code, int repeat) { + mId = nativeNextId(); mDownTime = downTime; mEventTime = eventTime; mAction = action; @@ -1407,6 +1410,7 @@ public class KeyEvent extends InputEvent implements Parcelable { */ public KeyEvent(long downTime, long eventTime, int action, int code, int repeat, int metaState) { + mId = nativeNextId(); mDownTime = downTime; mEventTime = eventTime; mAction = action; @@ -1435,6 +1439,7 @@ public class KeyEvent extends InputEvent implements Parcelable { public KeyEvent(long downTime, long eventTime, int action, int code, int repeat, int metaState, int deviceId, int scancode) { + mId = nativeNextId(); mDownTime = downTime; mEventTime = eventTime; mAction = action; @@ -1465,6 +1470,7 @@ public class KeyEvent extends InputEvent implements Parcelable { public KeyEvent(long downTime, long eventTime, int action, int code, int repeat, int metaState, int deviceId, int scancode, int flags) { + mId = nativeNextId(); mDownTime = downTime; mEventTime = eventTime; mAction = action; @@ -1497,6 +1503,7 @@ public class KeyEvent extends InputEvent implements Parcelable { public KeyEvent(long downTime, long eventTime, int action, int code, int repeat, int metaState, int deviceId, int scancode, int flags, int source) { + mId = nativeNextId(); mDownTime = downTime; mEventTime = eventTime; mAction = action; @@ -1523,6 +1530,7 @@ public class KeyEvent extends InputEvent implements Parcelable { * @param flags The flags for this key event */ public KeyEvent(long time, String characters, int deviceId, int flags) { + mId = nativeNextId(); mDownTime = time; mEventTime = time; mCharacters = characters; @@ -1539,6 +1547,7 @@ public class KeyEvent extends InputEvent implements Parcelable { * Make an exact copy of an existing key event. */ public KeyEvent(KeyEvent origEvent) { + mId = origEvent.mId; mDownTime = origEvent.mDownTime; mEventTime = origEvent.mEventTime; mAction = origEvent.mAction; @@ -1567,6 +1576,7 @@ public class KeyEvent extends InputEvent implements Parcelable { */ @Deprecated public KeyEvent(KeyEvent origEvent, long eventTime, int newRepeat) { + mId = nativeNextId(); // Not an exact copy so assign a new ID. mDownTime = origEvent.mDownTime; mEventTime = eventTime; mAction = origEvent.mAction; @@ -1598,15 +1608,16 @@ public class KeyEvent extends InputEvent implements Parcelable { } /** - * Obtains a (potentially recycled) key event. + * Obtains a (potentially recycled) key event. Used by native code to create a Java object. * * @hide */ - public static KeyEvent obtain(long downTime, long eventTime, int action, + public static KeyEvent obtain(int id, long downTime, long eventTime, int action, int code, int repeat, int metaState, int deviceId, int scancode, int flags, int source, int displayId, @Nullable byte[] hmac, String characters) { KeyEvent ev = obtain(); + ev.mId = id; ev.mDownTime = downTime; ev.mEventTime = eventTime; ev.mAction = action; @@ -1628,12 +1639,24 @@ public class KeyEvent extends InputEvent implements Parcelable { * * @hide */ + public static KeyEvent obtain(long downTime, long eventTime, int action, + int code, int repeat, int metaState, + int deviceId, int scanCode, int flags, int source, int displayId, String characters) { + return obtain(nativeNextId(), downTime, eventTime, action, code, repeat, metaState, + deviceId, scanCode, flags, source, displayId, null /* hmac */, characters); + } + + /** + * Obtains a (potentially recycled) key event. + * + * @hide + */ @UnsupportedAppUsage public static KeyEvent obtain(long downTime, long eventTime, int action, int code, int repeat, int metaState, int deviceId, int scancode, int flags, int source, String characters) { return obtain(downTime, eventTime, action, code, repeat, metaState, deviceId, scancode, - flags, source, INVALID_DISPLAY, null /* hmac */, characters); + flags, source, INVALID_DISPLAY, characters); } /** @@ -1645,6 +1668,7 @@ public class KeyEvent extends InputEvent implements Parcelable { */ public static KeyEvent obtain(KeyEvent other) { KeyEvent ev = obtain(); + ev.mId = other.mId; ev.mDownTime = other.mDownTime; ev.mEventTime = other.mEventTime; ev.mAction = other.mAction; @@ -1695,6 +1719,12 @@ public class KeyEvent extends InputEvent implements Parcelable { // Do nothing. } + /** @hide */ + @Override + public int getId() { + return mId; + } + /** * Create a new key event that is the same as the given one, but whose * event time and repeat count are replaced with the given value. @@ -1723,6 +1753,7 @@ public class KeyEvent extends InputEvent implements Parcelable { public static KeyEvent changeTimeRepeat(KeyEvent event, long eventTime, int newRepeat, int newFlags) { KeyEvent ret = new KeyEvent(event); + ret.mId = nativeNextId(); // Not an exact copy so assign a new ID. ret.mEventTime = eventTime; ret.mRepeatCount = newRepeat; ret.mFlags = newFlags; @@ -1736,6 +1767,7 @@ public class KeyEvent extends InputEvent implements Parcelable { * @param action The new action code of the event. */ private KeyEvent(KeyEvent origEvent, int action) { + mId = nativeNextId(); // Not an exact copy so assign a new ID. mDownTime = origEvent.mDownTime; mEventTime = origEvent.mEventTime; mAction = action; @@ -1772,6 +1804,7 @@ public class KeyEvent extends InputEvent implements Parcelable { */ public static KeyEvent changeFlags(KeyEvent event, int flags) { event = new KeyEvent(event); + event.mId = nativeNextId(); // Not an exact copy so assign a new ID. event.mFlags = flags; return event; } @@ -1941,7 +1974,6 @@ public class KeyEvent extends InputEvent implements Parcelable { /** @hide */ public static final boolean isWakeKey(int keyCode) { switch (keyCode) { - case KeyEvent.KEYCODE_BACK: case KeyEvent.KEYCODE_CAMERA: case KeyEvent.KEYCODE_MENU: case KeyEvent.KEYCODE_PAIRING: @@ -3096,6 +3128,7 @@ public class KeyEvent extends InputEvent implements Parcelable { } private KeyEvent(Parcel in) { + mId = in.readInt(); mDeviceId = in.readInt(); mSource = in.readInt(); mDisplayId = in.readInt(); @@ -3115,6 +3148,7 @@ public class KeyEvent extends InputEvent implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeInt(PARCEL_TOKEN_KEY_EVENT); + out.writeInt(mId); out.writeInt(mDeviceId); out.writeInt(mSource); out.writeInt(mDisplayId); diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 006847656cac..19eff72ca814 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -1550,6 +1550,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { private static native long nativeCopy(long destNativePtr, long sourceNativePtr, boolean keepHistory); @CriticalNative + private static native int nativeGetId(long nativePtr); + @CriticalNative private static native int nativeGetDeviceId(long nativePtr); @CriticalNative private static native int nativeGetSource(long nativePtr); @@ -2024,6 +2026,12 @@ public final class MotionEvent extends InputEvent implements Parcelable { } } + /** @hide */ + @Override + public int getId() { + return nativeGetId(mNativePtr); + } + /** {@inheritDoc} */ @Override public final int getDeviceId() { diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index 680a8789a6b8..4badede2b093 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -111,6 +111,7 @@ public class SurfaceControlViewHost { @NonNull WindowlessWindowManager wwm) { mWm = wwm; mViewRoot = new ViewRootImpl(c, d, mWm); + mViewRoot.forceDisableBLAST(); mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection(); } @@ -135,6 +136,7 @@ public class SurfaceControlViewHost { mWm = new WindowlessWindowManager(context.getResources().getConfiguration(), mSurfaceControl, hostToken); mViewRoot = new ViewRootImpl(context, display, mWm); + mViewRoot.forceDisableBLAST(); mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection(); } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 73707ca889dd..fb7c04afeab0 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -392,7 +392,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall * This gets called on a RenderThread worker thread, so members accessed here must * be protected by a lock. */ - final boolean useBLAST = WindowManagerGlobal.useBLAST(); + final boolean useBLAST = viewRoot.useBLAST(); viewRoot.registerRtFrameCallback(frame -> { try { final SurfaceControl.Transaction t = useBLAST ? @@ -930,7 +930,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mSurfaceHeight); } } else if ((layoutSizeChanged || positionChanged) && - WindowManagerGlobal.useBLAST()) { + viewRoot.useBLAST()) { viewRoot.setUseBLASTSyncTransaction(); } mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius); @@ -1132,9 +1132,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t, Rect position, long frameNumber) { - if (frameNumber > 0 && !WindowManagerGlobal.useBLAST()) { - final ViewRootImpl viewRoot = getViewRootImpl(); - + final ViewRootImpl viewRoot = getViewRootImpl(); + if (frameNumber > 0 && viewRoot != null && !viewRoot.useBLAST()) { t.deferTransactionUntil(surface, viewRoot.getRenderSurfaceControl(), frameNumber); } @@ -1150,8 +1149,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } private void setParentSpaceRectangle(Rect position, long frameNumber) { - final boolean useBLAST = WindowManagerGlobal.useBLAST(); final ViewRootImpl viewRoot = getViewRootImpl(); + final boolean useBLAST = viewRoot.useBLAST(); final SurfaceControl.Transaction t = useBLAST ? viewRoot.getBLASTSyncTransaction() : mRtTransaction; @@ -1211,7 +1210,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall @Override public void positionLost(long frameNumber) { - boolean useBLAST = WindowManagerGlobal.useBLAST(); + final ViewRootImpl viewRoot = getViewRootImpl(); + boolean useBLAST = viewRoot != null && viewRoot.useBLAST(); if (DEBUG) { Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d", System.identityHashCode(this), frameNumber)); @@ -1222,8 +1222,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall return; } - final ViewRootImpl viewRoot = getViewRootImpl(); - final SurfaceControl.Transaction t = useBLAST ? (viewRoot != null ? viewRoot.getBLASTSyncTransaction() : mRtTransaction) : mRtTransaction; @@ -1646,7 +1644,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } private void updateScreenMatrixForEmbeddedHierarchy() { - getBoundsOnScreen(mTmpRect, true); + getBoundsOnScreen(mTmpRect); mTmpMatrix.reset(); mTmpMatrix.setTranslate(mTmpRect.left, mTmpRect.top); mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth, diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 11ab5724d927..15d18d145bb0 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -29794,7 +29794,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // accessibility CharSequence contentDescription = getContentDescription(); - stream.addProperty("accessibility:contentDescription", + stream.addUserProperty("accessibility:contentDescription", contentDescription == null ? "" : contentDescription.toString()); stream.addProperty("accessibility:labelFor", getLabelFor()); stream.addProperty("accessibility:importantForAccessibility", getImportantForAccessibility()); diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 2f44fe039cf5..8a5be75b6c31 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -1209,6 +1209,7 @@ public class ViewDebug { ByteArrayOutputStream baOut = new ByteArrayOutputStream(); final ViewHierarchyEncoder encoder = new ViewHierarchyEncoder(baOut); + encoder.setUserPropertiesEnabled(false); encoder.addProperty("window:left", view.mAttachInfo.mWindowLeft); encoder.addProperty("window:top", view.mAttachInfo.mWindowTop); view.encode(encoder); diff --git a/core/java/android/view/ViewHierarchyEncoder.java b/core/java/android/view/ViewHierarchyEncoder.java index b0e0524a0d85..ace05a6b6bf9 100644 --- a/core/java/android/view/ViewHierarchyEncoder.java +++ b/core/java/android/view/ViewHierarchyEncoder.java @@ -66,10 +66,16 @@ public class ViewHierarchyEncoder { private short mPropertyId = 1; private Charset mCharset = Charset.forName("utf-8"); + private boolean mUserPropertiesEnabled = true; + public ViewHierarchyEncoder(@NonNull ByteArrayOutputStream stream) { mStream = new DataOutputStream(stream); } + public void setUserPropertiesEnabled(boolean enabled) { + mUserPropertiesEnabled = enabled; + } + public void beginObject(@NonNull Object o) { startPropertyMap(); addProperty("meta:__name__", o.getClass().getName()); @@ -121,6 +127,17 @@ public class ViewHierarchyEncoder { } /** + * Encodes a user defined property if they are allowed to be encoded + * + * @see #setUserPropertiesEnabled(boolean) + */ + public void addUserProperty(@NonNull String name, @Nullable String s) { + if (mUserPropertiesEnabled) { + addProperty(name, s); + } + } + + /** * Writes the given name as the property name, and leaves it to the callee * to fill in value for this property. */ diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 148b32744e30..918b5a35d4db 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -322,7 +322,7 @@ public final class ViewRootImpl implements ViewParent, */ private boolean mForceNextConfigUpdate; - private final boolean mUseBLASTAdapter; + private boolean mUseBLASTAdapter; /** * Signals that compatibility booleans have been initialized according to @@ -5379,7 +5379,8 @@ public final class ViewRootImpl implements ViewParent, if (mTracePrefix == null) { mTracePrefix = getClass().getSimpleName(); } - Trace.traceBegin(traceTag, mTracePrefix + " seq#=" + q.mEvent.getSequenceNumber()); + Trace.traceBegin(traceTag, mTracePrefix + " id=0x" + + Integer.toHexString(q.mEvent.getId())); } } @@ -7986,12 +7987,13 @@ public final class ViewRootImpl implements ViewParent, private void deliverInputEvent(QueuedInputEvent q) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent", - q.mEvent.getSequenceNumber()); + q.mEvent.getId()); if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent src=0x" + Integer.toHexString(q.mEvent.getSource()) + " eventTimeNano=" - + q.mEvent.getEventTimeNano() + " seq#=" + q.mEvent.getSequenceNumber()); + + q.mEvent.getEventTimeNano() + " id=0x" + + Integer.toHexString(q.mEvent.getId())); } try { if (mInputEventConsistencyVerifier != null) { @@ -8032,7 +8034,7 @@ public final class ViewRootImpl implements ViewParent, private void finishInputEvent(QueuedInputEvent q) { Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent", - q.mEvent.getSequenceNumber()); + q.mEvent.getId()); if (q.mReceiver != null) { boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0; @@ -9639,4 +9641,16 @@ public final class ViewRootImpl implements ViewParent, public void onDescendantUnbufferedRequested() { mUnbufferedInputSource = mView.mUnbufferedInputSource; } + + /** + * Force disabling use of the BLAST adapter regardless of the system + * flag. Needs to be called before addView. + */ + void forceDisableBLAST() { + mUseBLASTAdapter = false; + } + + boolean useBLAST() { + return mUseBLASTAdapter; + } } diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index d40f505f87a0..6435b42efca0 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -16,6 +16,11 @@ package android.view; +import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; +import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; + import android.annotation.NonNull; import android.app.ResourcesManager; import android.compat.annotation.UnsupportedAppUsage; @@ -67,10 +72,6 @@ public final class WindowManagerImpl implements WindowManager { private IBinder mDefaultToken; - private boolean mIsViewAdded; - private View mLastView; - private WindowManager.LayoutParams mLastParams; - public WindowManagerImpl(Context context) { this(context, null); } @@ -102,9 +103,6 @@ public final class WindowManagerImpl implements WindowManager { public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow); - mIsViewAdded = true; - mLastView = view; - mLastParams = (WindowManager.LayoutParams) params; } @Override @@ -247,21 +245,19 @@ public final class WindowManagerImpl implements WindowManager { } private WindowInsets computeWindowInsets() { - // TODO(window-context): This can only be properly implemented + // TODO(b/118118435): This can only be properly implemented // once we flip the new insets mode flag. - if (mParentWindow != null) { - if (mParentWindow.getDecorView().isAttachedToWindow()) { - return mParentWindow.getDecorView().getViewRootImpl() - .getWindowInsets(true /* forceConstruct */); - } - return getWindowInsetsFromServer(mParentWindow.getAttributes()); - } - if (mIsViewAdded) { - return mLastView.getViewRootImpl().getWindowInsets(true /* forceConstruct */); - } else { - return getWindowInsetsFromServer(new WindowManager.LayoutParams()); - } - + // Initialize params which used for obtaining all system insets. + final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); + params.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; + params.token = (mParentWindow != null) ? mParentWindow.getContext().getActivityToken() + : mContext.getActivityToken(); + params.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + params.setFitInsetsTypes(0); + params.setFitInsetsSides(0); + + return getWindowInsetsFromServer(params); } private WindowInsets getWindowInsetsFromServer(WindowManager.LayoutParams attrs) { diff --git a/core/java/android/view/WindowMetrics.java b/core/java/android/view/WindowMetrics.java index 8caf5b7fc725..ab5a06eb99de 100644 --- a/core/java/android/view/WindowMetrics.java +++ b/core/java/android/view/WindowMetrics.java @@ -17,6 +17,7 @@ package android.view; import android.annotation.NonNull; +import android.graphics.Point; import android.util.Size; /** @@ -40,6 +41,30 @@ public final class WindowMetrics { /** * Returns the size of the window. + * <p> + * <b>Note that this reports a different size than {@link Display#getSize(Point)}.</b> + * This method reports the window size including all system bars area, while + * {@link Display#getSize(Point)} reports the area excluding navigation bars and display cutout + * areas. The value reported by {@link Display#getSize(Point)} can be obtained by using: + * <pre class="prettyprint"> + * final WindowMetrics metrics = windowManager.getCurrentMetrics(); + * // Gets all excluding insets + * final WindowInsets windowInsets = metrics.getWindowInsets(); + * Insets insets = windowInsets.getInsets(WindowInsets.Type.navigationBars()); + * final DisplayCutout cutout = windowInsets.getCutout(); + * if (cutout != null) { + * final Insets cutoutSafeInsets = Insets.of(cutout.getSafeInsetsLeft(), ...); + * insets = insets.max(insets, cutoutSafeInsets); + * } + * + * int insetsWidth = insets.right + insets.left; + * int insetsHeight = insets.top + insets.bottom; + * + * // Legacy size that Display#getSize reports + * final Size legacySize = new Size(metrics.getWidth() - insetsWidth, + * metrics.getHeight() - insetsHeight); + * </pre> + * </p> * * @return window size in pixel. */ diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java index 5700dda3a2e3..e50da40cdc16 100644 --- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java +++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java @@ -22,7 +22,9 @@ import android.app.ActivityThread; import android.os.Bundle; import android.os.IBinder; import android.os.LocaleList; +import android.os.Parcel; import android.os.Parcelable; +import android.view.Display; import android.view.inline.InlinePresentationSpec; import com.android.internal.util.DataClass; @@ -67,7 +69,11 @@ public final class InlineSuggestionsRequest implements Parcelable { */ private @NonNull LocaleList mSupportedLocales; - // TODO(b/149609075): the generated code needs to be manually fixed due to the bug. + /** + * The extras state propagated from the IME to pass extra data. + */ + private @Nullable Bundle mExtras; + /** * The host input token of the IME that made the request. This will be set by the system for * safety reasons. @@ -77,9 +83,12 @@ public final class InlineSuggestionsRequest implements Parcelable { private @Nullable IBinder mHostInputToken; /** - * The extras state propagated from the IME to pass extra data. + * The host display id of the IME that made the request. This will be set by the system for + * safety reasons. + * + * @hide */ - private @Nullable Bundle mExtras; + private int mHostDisplayId; /** * @hide @@ -89,6 +98,24 @@ public final class InlineSuggestionsRequest implements Parcelable { mHostInputToken = hostInputToken; } + // TODO(b/149609075): remove once IBinder parcelling is natively supported + private void parcelHostInputToken(@NonNull Parcel parcel, int flags) { + parcel.writeStrongBinder(mHostInputToken); + } + + // TODO(b/149609075): remove once IBinder parcelling is natively supported + private @Nullable IBinder unparcelHostInputToken(Parcel parcel) { + return parcel.readStrongBinder(); + } + + /** + * @hide + * @see {@link #mHostDisplayId}. + */ + public void setHostDisplayId(int hostDisplayId) { + mHostDisplayId = hostDisplayId; + } + private void onConstructed() { Preconditions.checkState(mMaxSuggestionCount >= mPresentationSpecs.size()); } @@ -111,10 +138,17 @@ public final class InlineSuggestionsRequest implements Parcelable { } @Nullable + private static int defaultHostDisplayId() { + return Display.INVALID_DISPLAY; + } + + @Nullable private static Bundle defaultExtras() { return null; } + + /** @hide */ abstract static class BaseBuilder { abstract Builder setPresentationSpecs(@NonNull List<InlinePresentationSpec> value); @@ -122,6 +156,8 @@ public final class InlineSuggestionsRequest implements Parcelable { abstract Builder setHostPackageName(@Nullable String value); abstract Builder setHostInputToken(IBinder hostInputToken); + + abstract Builder setHostDisplayId(int value); } @@ -145,8 +181,9 @@ public final class InlineSuggestionsRequest implements Parcelable { @NonNull List<InlinePresentationSpec> presentationSpecs, @NonNull String hostPackageName, @NonNull LocaleList supportedLocales, + @Nullable Bundle extras, @Nullable IBinder hostInputToken, - @Nullable Bundle extras) { + int hostDisplayId) { this.mMaxSuggestionCount = maxSuggestionCount; this.mPresentationSpecs = presentationSpecs; com.android.internal.util.AnnotationValidations.validate( @@ -157,8 +194,9 @@ public final class InlineSuggestionsRequest implements Parcelable { this.mSupportedLocales = supportedLocales; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mSupportedLocales); - this.mHostInputToken = hostInputToken; this.mExtras = extras; + this.mHostInputToken = hostInputToken; + this.mHostDisplayId = hostDisplayId; onConstructed(); } @@ -202,6 +240,14 @@ public final class InlineSuggestionsRequest implements Parcelable { } /** + * The extras state propagated from the IME to pass extra data. + */ + @DataClass.Generated.Member + public @Nullable Bundle getExtras() { + return mExtras; + } + + /** * The host input token of the IME that made the request. This will be set by the system for * safety reasons. * @@ -213,11 +259,14 @@ public final class InlineSuggestionsRequest implements Parcelable { } /** - * The extras state propagated from the IME to pass extra data. + * The host display id of the IME that made the request. This will be set by the system for + * safety reasons. + * + * @hide */ @DataClass.Generated.Member - public @Nullable Bundle getExtras() { - return mExtras; + public int getHostDisplayId() { + return mHostDisplayId; } @Override @@ -231,8 +280,9 @@ public final class InlineSuggestionsRequest implements Parcelable { "presentationSpecs = " + mPresentationSpecs + ", " + "hostPackageName = " + mHostPackageName + ", " + "supportedLocales = " + mSupportedLocales + ", " + + "extras = " + mExtras + ", " + "hostInputToken = " + mHostInputToken + ", " + - "extras = " + mExtras + + "hostDisplayId = " + mHostDisplayId + " }"; } @@ -253,8 +303,9 @@ public final class InlineSuggestionsRequest implements Parcelable { && java.util.Objects.equals(mPresentationSpecs, that.mPresentationSpecs) && java.util.Objects.equals(mHostPackageName, that.mHostPackageName) && java.util.Objects.equals(mSupportedLocales, that.mSupportedLocales) + && java.util.Objects.equals(mExtras, that.mExtras) && java.util.Objects.equals(mHostInputToken, that.mHostInputToken) - && java.util.Objects.equals(mExtras, that.mExtras); + && mHostDisplayId == that.mHostDisplayId; } @Override @@ -268,27 +319,29 @@ public final class InlineSuggestionsRequest implements Parcelable { _hash = 31 * _hash + java.util.Objects.hashCode(mPresentationSpecs); _hash = 31 * _hash + java.util.Objects.hashCode(mHostPackageName); _hash = 31 * _hash + java.util.Objects.hashCode(mSupportedLocales); - _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken); _hash = 31 * _hash + java.util.Objects.hashCode(mExtras); + _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken); + _hash = 31 * _hash + mHostDisplayId; return _hash; } @Override @DataClass.Generated.Member - public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } byte flg = 0; - if (mHostInputToken != null) flg |= 0x10; - if (mExtras != null) flg |= 0x20; + if (mExtras != null) flg |= 0x10; + if (mHostInputToken != null) flg |= 0x20; dest.writeByte(flg); dest.writeInt(mMaxSuggestionCount); dest.writeParcelableList(mPresentationSpecs, flags); dest.writeString(mHostPackageName); dest.writeTypedObject(mSupportedLocales, flags); - if (mHostInputToken != null) dest.writeStrongBinder(mHostInputToken); if (mExtras != null) dest.writeBundle(mExtras); + parcelHostInputToken(dest, flags); + dest.writeInt(mHostDisplayId); } @Override @@ -298,7 +351,7 @@ public final class InlineSuggestionsRequest implements Parcelable { /** @hide */ @SuppressWarnings({"unchecked", "RedundantCast"}) @DataClass.Generated.Member - /* package-private */ InlineSuggestionsRequest(@NonNull android.os.Parcel in) { + /* package-private */ InlineSuggestionsRequest(@NonNull Parcel in) { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } @@ -308,8 +361,9 @@ public final class InlineSuggestionsRequest implements Parcelable { in.readParcelableList(presentationSpecs, InlinePresentationSpec.class.getClassLoader()); String hostPackageName = in.readString(); LocaleList supportedLocales = (LocaleList) in.readTypedObject(LocaleList.CREATOR); - IBinder hostInputToken = (flg & 0x10) == 0 ? null : in.readStrongBinder(); - Bundle extras = (flg & 0x20) == 0 ? null : in.readBundle(); + Bundle extras = (flg & 0x10) == 0 ? null : in.readBundle(); + IBinder hostInputToken = unparcelHostInputToken(in); + int hostDisplayId = in.readInt(); this.mMaxSuggestionCount = maxSuggestionCount; this.mPresentationSpecs = presentationSpecs; @@ -321,8 +375,9 @@ public final class InlineSuggestionsRequest implements Parcelable { this.mSupportedLocales = supportedLocales; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mSupportedLocales); - this.mHostInputToken = hostInputToken; this.mExtras = extras; + this.mHostInputToken = hostInputToken; + this.mHostDisplayId = hostDisplayId; onConstructed(); } @@ -336,7 +391,7 @@ public final class InlineSuggestionsRequest implements Parcelable { } @Override - public InlineSuggestionsRequest createFromParcel(@NonNull android.os.Parcel in) { + public InlineSuggestionsRequest createFromParcel(@NonNull Parcel in) { return new InlineSuggestionsRequest(in); } }; @@ -352,8 +407,9 @@ public final class InlineSuggestionsRequest implements Parcelable { private @NonNull List<InlinePresentationSpec> mPresentationSpecs; private @NonNull String mHostPackageName; private @NonNull LocaleList mSupportedLocales; - private @Nullable IBinder mHostInputToken; private @Nullable Bundle mExtras; + private @Nullable IBinder mHostInputToken; + private int mHostDisplayId; private long mBuilderFieldsSet = 0L; @@ -436,6 +492,17 @@ public final class InlineSuggestionsRequest implements Parcelable { } /** + * The extras state propagated from the IME to pass extra data. + */ + @DataClass.Generated.Member + public @NonNull Builder setExtras(@Nullable Bundle value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x10; + mExtras = value; + return this; + } + + /** * The host input token of the IME that made the request. This will be set by the system for * safety reasons. * @@ -445,26 +512,30 @@ public final class InlineSuggestionsRequest implements Parcelable { @Override @NonNull Builder setHostInputToken(@Nullable IBinder value) { checkNotUsed(); - mBuilderFieldsSet |= 0x10; + mBuilderFieldsSet |= 0x20; mHostInputToken = value; return this; } /** - * The extras state propagated from the IME to pass extra data. + * The host display id of the IME that made the request. This will be set by the system for + * safety reasons. + * + * @hide */ @DataClass.Generated.Member - public @NonNull Builder setExtras(@Nullable Bundle value) { + @Override + @NonNull Builder setHostDisplayId(int value) { checkNotUsed(); - mBuilderFieldsSet |= 0x20; - mExtras = value; + mBuilderFieldsSet |= 0x40; + mHostDisplayId = value; return this; } /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull InlineSuggestionsRequest build() { checkNotUsed(); - mBuilderFieldsSet |= 0x40; // Mark builder used + mBuilderFieldsSet |= 0x80; // Mark builder used if ((mBuilderFieldsSet & 0x1) == 0) { mMaxSuggestionCount = defaultMaxSuggestionCount(); @@ -476,23 +547,27 @@ public final class InlineSuggestionsRequest implements Parcelable { mSupportedLocales = defaultSupportedLocales(); } if ((mBuilderFieldsSet & 0x10) == 0) { - mHostInputToken = defaultHostInputToken(); + mExtras = defaultExtras(); } if ((mBuilderFieldsSet & 0x20) == 0) { - mExtras = defaultExtras(); + mHostInputToken = defaultHostInputToken(); + } + if ((mBuilderFieldsSet & 0x40) == 0) { + mHostDisplayId = defaultHostDisplayId(); } InlineSuggestionsRequest o = new InlineSuggestionsRequest( mMaxSuggestionCount, mPresentationSpecs, mHostPackageName, mSupportedLocales, + mExtras, mHostInputToken, - mExtras); + mHostDisplayId); return o; } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x40) != 0) { + if ((mBuilderFieldsSet & 0x80) != 0) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } @@ -500,10 +575,10 @@ public final class InlineSuggestionsRequest implements Parcelable { } @DataClass.Generated( - time = 1581747892762L, + time = 1582339908980L, codegenVersion = "1.0.14", sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java", - inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate @android.annotation.Nullable android.os.Bundle mExtras\npublic void setHostInputToken(android.os.IBinder)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nclass BaseBuilder extends java.lang.Object implements []") + inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.Nullable android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic void setHostInputToken(android.os.IBinder)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated private void __metadata() {} diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 2d27a789ebcb..53541f786da0 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -369,10 +369,22 @@ public abstract class WebSettings { public abstract boolean getDisplayZoomControls(); /** - * Enables or disables file access within WebView. File access is enabled by - * default. Note that this enables or disables file system access only. - * Assets and resources are still accessible using file:///android_asset and - * file:///android_res. + * Enables or disables file access within WebView. + * Note that this enables or disables file system access only. Assets and resources + * are still accessible using file:///android_asset and file:///android_res. + * <p class="note"> + * <b>Note:</b> Apps should not open {@code file://} URLs from any external source in + * WebView, don't enable this if your app accepts arbitrary URLs from external sources. + * It's recommended to always use + * <a href="{@docRoot}reference/androidx/webkit/WebViewAssetLoader"> + * androidx.webkit.WebViewAssetLoader</a> to access files including assets and resources over + * {@code http(s)://} schemes, instead of {@code file://} URLs. To prevent possible security + * issues targeting {@link android.os.Build.VERSION_CODES#Q} and earlier, you should explicitly + * set this value to {@code false}. + * <p> + * The default value is {@code true} for apps targeting + * {@link android.os.Build.VERSION_CODES#Q} and below, and {@code false} when targeting + * {@link android.os.Build.VERSION_CODES#R} and above. */ public abstract void setAllowFileAccess(boolean allow); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 815cc5cbb10d..f3243aaf5b7d 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -13174,7 +13174,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener stream.addProperty("text:selectionStart", getSelectionStart()); stream.addProperty("text:selectionEnd", getSelectionEnd()); stream.addProperty("text:curTextColor", mCurTextColor); - stream.addProperty("text:text", mText == null ? null : mText.toString()); + stream.addUserProperty("text:text", mText == null ? null : mText.toString()); stream.addProperty("text:gravity", mGravity); } diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index bb404654b741..5cdcab029877 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -412,8 +412,13 @@ public class AccessibilityShortcutController { * Class to wrap TextToSpeech for shortcut dialog spoken feedback. */ private class TtsPrompt implements TextToSpeech.OnInitListener { + private static final int RETRY_MILLIS = 1000; + private final CharSequence mText; + + private int mRetryCount = 3; private boolean mDismiss; + private boolean mLanguageReady = false; private TextToSpeech mTts; TtsPrompt(String serviceName) { @@ -437,17 +442,15 @@ public class AccessibilityShortcutController { playNotificationTone(); return; } - mHandler.sendMessage(PooledLambda.obtainMessage(TtsPrompt::play, this)); + mHandler.sendMessage(PooledLambda.obtainMessage( + TtsPrompt::waitForTtsReady, this)); } private void play() { if (mDismiss) { return; } - int status = TextToSpeech.ERROR; - if (setLanguage(Locale.getDefault())) { - status = mTts.speak(mText, TextToSpeech.QUEUE_FLUSH, null, null); - } + final int status = mTts.speak(mText, TextToSpeech.QUEUE_FLUSH, null, null); if (status != TextToSpeech.SUCCESS) { Slog.d(TAG, "Tts play fail"); playNotificationTone(); @@ -455,21 +458,42 @@ public class AccessibilityShortcutController { } /** - * @return false if tts language is not available + * Waiting for tts is ready to speak. Trying again if tts language pack is not available + * or tts voice data is not installed yet. */ - private boolean setLanguage(final Locale locale) { - int status = mTts.isLanguageAvailable(locale); - if (status == TextToSpeech.LANG_MISSING_DATA - || status == TextToSpeech.LANG_NOT_SUPPORTED) { - return false; + private void waitForTtsReady() { + if (mDismiss) { + return; + } + if (!mLanguageReady) { + final int status = mTts.setLanguage(Locale.getDefault()); + // True if language is available and TTS#loadVoice has called once + // that trigger TTS service to start initialization. + mLanguageReady = status != TextToSpeech.LANG_MISSING_DATA + && status != TextToSpeech.LANG_NOT_SUPPORTED; } - mTts.setLanguage(locale); - Voice voice = mTts.getVoice(); - if (voice == null || (voice.getFeatures() != null && voice.getFeatures() - .contains(TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED))) { - return false; + if (mLanguageReady) { + final Voice voice = mTts.getVoice(); + final boolean voiceDataInstalled = voice != null + && voice.getFeatures() != null + && !voice.getFeatures().contains( + TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED); + if (voiceDataInstalled) { + mHandler.sendMessage(PooledLambda.obtainMessage( + TtsPrompt::play, this)); + return; + } + } + + if (mRetryCount == 0) { + Slog.d(TAG, "Tts not ready to speak."); + playNotificationTone(); + return; } - return true; + // Retry if TTS service not ready yet. + mRetryCount -= 1; + mHandler.sendMessageDelayed(PooledLambda.obtainMessage( + TtsPrompt::waitForTtsReady, this), RETRY_MILLIS); } } diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java index 382a254b67a6..3876976575ae 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java +++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java @@ -16,6 +16,8 @@ package com.android.internal.inputmethod; +import android.annotation.AnyThread; +import android.annotation.NonNull; import android.view.WindowManager; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; @@ -25,6 +27,7 @@ import java.util.StringJoiner; * Provides useful methods for debugging. */ public final class InputMethodDebug { + /** * Not intended to be instantiated. */ @@ -174,4 +177,71 @@ public final class InputMethodDebug { return joiner.setEmptyValue("(none)").toString(); } + + + /** + * Converts {@link SoftInputShowHideReason} to {@link String} for history dump. + */ + public static String softInputDisplayReasonToString(@SoftInputShowHideReason int reason) { + switch (reason) { + case SoftInputShowHideReason.SHOW_SOFT_INPUT: + return "SHOW_SOFT_INPUT"; + case SoftInputShowHideReason.ATTACH_NEW_INPUT: + return "ATTACH_NEW_INPUT"; + case SoftInputShowHideReason.SHOW_MY_SOFT_INPUT: + return "SHOW_MY_SOFT_INPUT"; + case SoftInputShowHideReason.HIDE_SOFT_INPUT: + return "HIDE_SOFT_INPUT"; + case SoftInputShowHideReason.HIDE_MY_SOFT_INPUT: + return "HIDE_MY_SOFT_INPUT"; + case SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV: + return "SHOW_AUTO_EDITOR_FORWARD_NAV"; + case SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV: + return "SHOW_STATE_VISIBLE_FORWARD_NAV"; + case SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE: + return "SHOW_STATE_ALWAYS_VISIBLE"; + case SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE: + return "SHOW_SETTINGS_ON_CHANGE"; + case SoftInputShowHideReason.HIDE_SWITCH_USER: + return "HIDE_SWITCH_USER"; + case SoftInputShowHideReason.HIDE_INVALID_USER: + return "HIDE_INVALID_USER"; + case SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW: + return "HIDE_UNSPECIFIED_WINDOW"; + case SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV: + return "HIDE_STATE_HIDDEN_FORWARD_NAV"; + case SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE: + return "HIDE_ALWAYS_HIDDEN_STATE"; + case SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND: + return "HIDE_RESET_SHELL_COMMAND"; + case SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE: + return "HIDE_SETTINGS_ON_CHANGE"; + case SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME: + return "HIDE_POWER_BUTTON_GO_HOME"; + case SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED: + return "HIDE_DOCKED_STACK_ATTACHED"; + case SoftInputShowHideReason.HIDE_RECENTS_ANIMATION: + return "HIDE_RECENTS_ANIMATION"; + default: + return "Unknown=" + reason; + } + } + + /** + * Return a fixed size string of the object. + * TODO(b/141738570): Take & return with StringBuilder to make more memory efficient. + */ + @NonNull + @AnyThread + public static String objToString(Object obj) { + if (obj == null) { + return "null"; + } + StringBuilder sb = new StringBuilder(64); + sb.setLength(0); + sb.append(obj.getClass().getName()); + sb.append("@"); + sb.append(Integer.toHexString(obj.hashCode())); + return sb.toString(); + } } diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java new file mode 100644 index 000000000000..79397b81ace7 --- /dev/null +++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java @@ -0,0 +1,143 @@ +/* + * 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.internal.inputmethod; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.IntDef; +import android.view.WindowManager.LayoutParams; + +import java.lang.annotation.Retention; + +/** + * Describes the reason why Soft input window visible / hidden. + */ +@Retention(SOURCE) +@IntDef(value = { + SoftInputShowHideReason.SHOW_SOFT_INPUT, + SoftInputShowHideReason.ATTACH_NEW_INPUT, + SoftInputShowHideReason.SHOW_MY_SOFT_INPUT, + SoftInputShowHideReason.HIDE_SOFT_INPUT, + SoftInputShowHideReason.HIDE_MY_SOFT_INPUT, + SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV, + SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV, + SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE, + SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE, + SoftInputShowHideReason.HIDE_SWITCH_USER, + SoftInputShowHideReason.HIDE_INVALID_USER, + SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW, + SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV, + SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE, + SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND, + SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE, + SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME, + SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED, + SoftInputShowHideReason.HIDE_RECENTS_ANIMATION}) +public @interface SoftInputShowHideReason { + /** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */ + int SHOW_SOFT_INPUT = 0; + + /** Show soft input when {@code InputMethodManagerService#attachNewInputLocked} called. */ + int ATTACH_NEW_INPUT = 1; + + /** Show soft input by {@code InputMethodManagerService#showMySoftInput}. */ + int SHOW_MY_SOFT_INPUT = 2; + + /** + * Hide soft input by + * {@link android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow}. + */ + int HIDE_SOFT_INPUT = 3; + + /** Hide soft input by {@code InputMethodManagerService#hideMySoftInput}. */ + int HIDE_MY_SOFT_INPUT = 4; + + /** + * Show soft input when navigated forward to the window (with + * {@link LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION}} which the focused view is text + * editor and system will auto-show the IME when the window can resize or running on a large + * screen. + */ + int SHOW_AUTO_EDITOR_FORWARD_NAV = 5; + + /** + * Show soft input when navigated forward to the window with + * {@link LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION} and + * {@link LayoutParams#SOFT_INPUT_STATE_VISIBLE}. + */ + int SHOW_STATE_VISIBLE_FORWARD_NAV = 6; + + /** + * Show soft input when the window with {@link LayoutParams#SOFT_INPUT_STATE_ALWAYS_VISIBLE}. + */ + int SHOW_STATE_ALWAYS_VISIBLE = 7; + + /** + * Show soft input during {@code InputMethodManagerService} receive changes from + * {@code SettingsProvider}. + */ + int SHOW_SETTINGS_ON_CHANGE = 8; + + /** Hide soft input during switching user. */ + int HIDE_SWITCH_USER = 9; + + /** Hide soft input when the user is invalid. */ + int HIDE_INVALID_USER = 10; + + /** + * Hide soft input when the window with {@link LayoutParams#SOFT_INPUT_STATE_UNSPECIFIED} which + * the focused view is not text editor. + */ + int HIDE_UNSPECIFIED_WINDOW = 11; + + /** + * Hide soft input when navigated forward to the window with + * {@link LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION} and + * {@link LayoutParams#SOFT_INPUT_STATE_HIDDEN}. + */ + int HIDE_STATE_HIDDEN_FORWARD_NAV = 12; + + /** + * Hide soft input when the window with {@link LayoutParams#SOFT_INPUT_STATE_ALWAYS_HIDDEN}. + */ + int HIDE_ALWAYS_HIDDEN_STATE = 13; + + /** Hide soft input when "adb shell ime <command>" called. */ + int HIDE_RESET_SHELL_COMMAND = 14; + + /** + * Hide soft input during {@code InputMethodManagerService} receive changes from + * {@code SettingsProvider}. + */ + int HIDE_SETTINGS_ON_CHANGE = 15; + + /** + * Hide soft input from {@link com.android.server.policy.PhoneWindowManager} when setting + * {@link com.android.internal.R.integer#config_shortPressOnPowerBehavior} in config.xml as + * dismiss IME. + */ + int HIDE_POWER_BUTTON_GO_HOME = 16; + + /** Hide soft input when attaching docked stack. */ + int HIDE_DOCKED_STACK_ATTACHED = 17; + + /** + * Hide soft input when {@link com.android.server.wm.RecentsAnimationController} starts + * intercept touch from app window. + */ + int HIDE_RECENTS_ANIMATION = 18; +} diff --git a/core/java/com/android/internal/logging/InstanceIdSequence.java b/core/java/com/android/internal/logging/InstanceIdSequence.java index aa507e538944..34643105b965 100644 --- a/core/java/com/android/internal/logging/InstanceIdSequence.java +++ b/core/java/com/android/internal/logging/InstanceIdSequence.java @@ -25,7 +25,7 @@ import java.security.SecureRandom; import java.util.Random; /** - * Generates random InstanceIds in range [0, instanceIdMax) for passing to + * Generates random InstanceIds in range [1, instanceIdMax] for passing to * UiEventLogger.logWithInstanceId(). Holds a SecureRandom, which self-seeds on * first use; try to give it a long lifetime. Safe for concurrent use. */ @@ -34,12 +34,12 @@ public class InstanceIdSequence { private final Random mRandom = new SecureRandom(); /** - * Constructs a sequence with identifiers [0, instanceIdMax). Capped at INSTANCE_ID_MAX. + * Constructs a sequence with identifiers [1, instanceIdMax]. Capped at INSTANCE_ID_MAX. * @param instanceIdMax Limiting value of identifiers. Normally positive: otherwise you get - * an all-zero sequence. + * an all-1 sequence. */ public InstanceIdSequence(int instanceIdMax) { - mInstanceIdMax = min(max(0, instanceIdMax), InstanceId.INSTANCE_ID_MAX); + mInstanceIdMax = min(max(1, instanceIdMax), InstanceId.INSTANCE_ID_MAX); } /** @@ -47,7 +47,7 @@ public class InstanceIdSequence { * @return new InstanceId */ public InstanceId newInstanceId() { - return newInstanceIdInternal(mRandom.nextInt(mInstanceIdMax)); + return newInstanceIdInternal(1 + mRandom.nextInt(mInstanceIdMax)); } /** diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp index 84acf9ac6989..0a2b1d4a661f 100644 --- a/core/jni/android_view_InputEventSender.cpp +++ b/core/jni/android_view_InputEventSender.cpp @@ -114,9 +114,9 @@ status_t NativeInputEventSender::sendKeyEvent(uint32_t seq, const KeyEvent* even uint32_t publishedSeq = mNextPublishedSeq++; status_t status = - mInputPublisher.publishKeyEvent(publishedSeq, event->getDeviceId(), event->getSource(), - event->getDisplayId(), event->getHmac(), - event->getAction(), event->getFlags(), + mInputPublisher.publishKeyEvent(publishedSeq, event->getId(), event->getDeviceId(), + event->getSource(), event->getDisplayId(), + event->getHmac(), event->getAction(), event->getFlags(), event->getKeyCode(), event->getScanCode(), event->getMetaState(), event->getRepeatCount(), event->getDownTime(), event->getEventTime()); @@ -138,12 +138,12 @@ status_t NativeInputEventSender::sendMotionEvent(uint32_t seq, const MotionEvent for (size_t i = 0; i <= event->getHistorySize(); i++) { publishedSeq = mNextPublishedSeq++; status_t status = - mInputPublisher.publishMotionEvent(publishedSeq, event->getDeviceId(), - event->getSource(), event->getDisplayId(), - event->getHmac(), event->getAction(), - event->getActionButton(), event->getFlags(), - event->getEdgeFlags(), event->getMetaState(), - event->getButtonState(), + mInputPublisher.publishMotionEvent(publishedSeq, event->getId(), + event->getDeviceId(), event->getSource(), + event->getDisplayId(), event->getHmac(), + event->getAction(), event->getActionButton(), + event->getFlags(), event->getEdgeFlags(), + event->getMetaState(), event->getButtonState(), event->getClassification(), event->getXScale(), event->getYScale(), event->getXOffset(), event->getYOffset(), event->getXPrecision(), diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp index bbe563e12a4c..54567e5010c5 100644 --- a/core/jni/android_view_KeyEvent.cpp +++ b/core/jni/android_view_KeyEvent.cpp @@ -75,6 +75,7 @@ static struct { jmethodID obtain; jmethodID recycle; + jfieldID mId; jfieldID mDeviceId; jfieldID mSource; jfieldID mDisplayId; @@ -96,6 +97,7 @@ jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent* event) { ScopedLocalRef<jbyteArray> hmac = toJbyteArray(env, event->getHmac()); jobject eventObj = env->CallStaticObjectMethod(gKeyEventClassInfo.clazz, gKeyEventClassInfo.obtain, + event->getId(), nanoseconds_to_milliseconds(event->getDownTime()), nanoseconds_to_milliseconds(event->getEventTime()), event->getAction(), event->getKeyCode(), @@ -114,6 +116,7 @@ jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent* event) { status_t android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj, KeyEvent* event) { + jint id = env->GetIntField(eventObj, gKeyEventClassInfo.mId); jint deviceId = env->GetIntField(eventObj, gKeyEventClassInfo.mDeviceId); jint source = env->GetIntField(eventObj, gKeyEventClassInfo.mSource); jint displayId = env->GetIntField(eventObj, gKeyEventClassInfo.mDisplayId); @@ -131,7 +134,7 @@ status_t android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj, jlong downTime = env->GetLongField(eventObj, gKeyEventClassInfo.mDownTime); jlong eventTime = env->GetLongField(eventObj, gKeyEventClassInfo.mEventTime); - event->initialize(deviceId, source, displayId, *hmac, action, flags, keyCode, scanCode, + event->initialize(id, deviceId, source, displayId, *hmac, action, flags, keyCode, scanCode, metaState, repeatCount, milliseconds_to_nanoseconds(downTime), milliseconds_to_nanoseconds(eventTime)); return OK; @@ -159,14 +162,18 @@ static jint android_view_KeyEvent_nativeKeyCodeFromString(JNIEnv* env, jobject c return KeyEvent::getKeyCodeFromLabel(keyLabel.c_str()); } +static jint android_view_KeyEvent_nativeNextId() { + return static_cast<jint>(InputEvent::nextId()); +} // ---------------------------------------------------------------------------- static const JNINativeMethod g_methods[] = { - { "nativeKeyCodeToString", "(I)Ljava/lang/String;", - (void*)android_view_KeyEvent_nativeKeyCodeToString}, - { "nativeKeyCodeFromString", "(Ljava/lang/String;)I", - (void*)android_view_KeyEvent_nativeKeyCodeFromString}, + {"nativeKeyCodeToString", "(I)Ljava/lang/String;", + (void*)android_view_KeyEvent_nativeKeyCodeToString}, + {"nativeKeyCodeFromString", "(Ljava/lang/String;)I", + (void*)android_view_KeyEvent_nativeKeyCodeFromString}, + {"nativeNextId", "()I", (void*)android_view_KeyEvent_nativeNextId}, }; int register_android_view_KeyEvent(JNIEnv* env) { @@ -175,10 +182,11 @@ int register_android_view_KeyEvent(JNIEnv* env) { gKeyEventClassInfo.obtain = GetStaticMethodIDOrDie(env, gKeyEventClassInfo.clazz, "obtain", - "(JJIIIIIIIII[BLjava/lang/String;)Landroid/view/KeyEvent;"); + "(IJJIIIIIIIII[BLjava/lang/String;)Landroid/view/KeyEvent;"); gKeyEventClassInfo.recycle = GetMethodIDOrDie(env, gKeyEventClassInfo.clazz, "recycle", "()V"); + gKeyEventClassInfo.mId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mId", "I"); gKeyEventClassInfo.mDeviceId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mDeviceId", "I"); gKeyEventClassInfo.mSource = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mSource", "I"); gKeyEventClassInfo.mDisplayId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mDisplayId", diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp index 3335fb23cfb7..9816d713c6dc 100644 --- a/core/jni/android_view_MotionEvent.cpp +++ b/core/jni/android_view_MotionEvent.cpp @@ -369,9 +369,10 @@ static jlong android_view_MotionEvent_nativeInitialize( env->DeleteLocalRef(pointerCoordsObj); } - event->initialize(deviceId, source, displayId, INVALID_HMAC, action, 0, flags, edgeFlags, - metaState, buttonState, static_cast<MotionClassification>(classification), - 1 /*xScale*/, 1 /*yScale*/, xOffset, yOffset, xPrecision, yPrecision, + event->initialize(InputEvent::nextId(), deviceId, source, displayId, INVALID_HMAC, action, 0, + flags, edgeFlags, metaState, buttonState, + static_cast<MotionClassification>(classification), 1 /*xScale*/, 1 /*yScale*/, + xOffset, yOffset, xPrecision, yPrecision, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, downTimeNanos, eventTimeNanos, pointerCount, pointerProperties, rawPointerCoords); @@ -592,6 +593,11 @@ static jlong android_view_MotionEvent_nativeCopy(jlong destNativePtr, jlong sour return reinterpret_cast<jlong>(destEvent); } +static jint android_view_MotionEvent_nativeGetId(jlong nativePtr) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + return event->getId(); +} + static jint android_view_MotionEvent_nativeGetDeviceId(jlong nativePtr) { MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); return event->getDeviceId(); @@ -790,6 +796,7 @@ static const JNINativeMethod gMotionEventMethods[] = { // --------------- @CriticalNative ------------------ {"nativeCopy", "(JJZ)J", (void*)android_view_MotionEvent_nativeCopy}, + {"nativeGetId", "(J)I", (void*)android_view_MotionEvent_nativeGetId}, {"nativeGetDeviceId", "(J)I", (void*)android_view_MotionEvent_nativeGetDeviceId}, {"nativeGetSource", "(J)I", (void*)android_view_MotionEvent_nativeGetSource}, {"nativeSetSource", "(JI)V", (void*)android_view_MotionEvent_nativeSetSource}, diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 7d1cf5d1190e..d6687f08b542 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -58,6 +58,7 @@ import "frameworks/base/core/proto/android/service/sensor_service.proto"; import "frameworks/base/core/proto/android/service/usb.proto"; import "frameworks/base/core/proto/android/util/event_log_tags.proto"; import "frameworks/base/core/proto/android/util/log.proto"; +import "frameworks/base/core/proto/android/util/textdump.proto"; import "frameworks/base/core/proto/android/privacy.proto"; import "frameworks/base/core/proto/android/section.proto"; import "frameworks/base/proto/src/ipconnectivity.proto"; @@ -510,6 +511,17 @@ message IncidentProto { (section).args = "sensorservice --proto" ]; + // Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999 + optional android.util.TextDumpProto textdump_wifi = 4000 [ + (section).type = SECTION_TEXT_DUMPSYS, + (section).args = "wifi" + ]; + + optional android.util.TextDumpProto textdump_bluetooth = 4001 [ + (section).type = SECTION_TEXT_DUMPSYS, + (section).args = "bluetooth_manager" + ]; + // Reserved for OEMs. extensions 50000 to 100000; } diff --git a/core/proto/android/section.proto b/core/proto/android/section.proto index 5afe22a3095f..299d6f9a1c62 100644 --- a/core/proto/android/section.proto +++ b/core/proto/android/section.proto @@ -46,6 +46,10 @@ enum SectionType { // incidentd calls tombstoned for annotated field SECTION_TOMBSTONE = 6; + + // incidentd calls legacy text dumpsys for annotated field. The section will only be generated + // on userdebug and eng builds. + SECTION_TEXT_DUMPSYS = 7; } message SectionFlags { diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index 08db4544d5e7..e8a0b46e8430 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -167,7 +167,7 @@ message DisplayContentProto { optional WindowContainerProto window_container = 1; optional int32 id = 2; reserved 3; // stacks - optional DockedStackDividerControllerProto docked_stack_divider_controller = 4; + optional DockedStackDividerControllerProto docked_stack_divider_controller = 4 [deprecated=true]; // Will be removed soon. optional PinnedStackControllerProto pinned_stack_controller = 5 [deprecated=true]; /* non app windows */ @@ -229,7 +229,7 @@ message DisplayFramesProto { message DockedStackDividerControllerProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; - optional bool minimized_dock = 1; + optional bool minimized_dock = 1 [deprecated=true]; } /* represents PinnedStackController */ diff --git a/core/proto/android/service/appwidget.proto b/core/proto/android/service/appwidget.proto index cd7173a94a70..97350ef90eec 100644 --- a/core/proto/android/service/appwidget.proto +++ b/core/proto/android/service/appwidget.proto @@ -36,4 +36,5 @@ message WidgetProto { optional int32 minHeight = 7; optional int32 maxWidth = 8; optional int32 maxHeight = 9; + optional bool restoreCompleted = 10; } diff --git a/core/proto/android/util/textdump.proto b/core/proto/android/util/textdump.proto new file mode 100644 index 000000000000..6118487b1387 --- /dev/null +++ b/core/proto/android/util/textdump.proto @@ -0,0 +1,33 @@ +/* + * 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. + */ + +syntax = "proto2"; +package android.util; + +import "frameworks/base/core/proto/android/privacy.proto"; + +option java_multiple_files = true; + +message TextDumpProto { + option (android.msg_privacy).dest = DEST_EXPLICIT; + + // The command that was executed + optional string command = 1; + // The content that was dumped + optional string content = 2; + // The duration of the dump process + optional int64 dump_duration_ns = 3; +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 61d229807d0d..60621022ad26 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1817,6 +1817,9 @@ android:protectionLevel="normal" /> <!-- @SystemApi Allows an internal user to use privileged SecureElement APIs. + Applications holding this permission can access OMAPI reset system API + and bypass OMAPI AccessControlEnforcer. + <p>Not for use by third-party applications. @hide --> <permission android:name="android.permission.SECURE_ELEMENT_PRIVILEGED" android:protectionLevel="signature|privileged" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 4ac51c69b05a..802399093754 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2752,7 +2752,9 @@ <!-- The amount to scale reduced scale snapshots for Overview and snapshot starting windows. Reduced scale snapshots are loaded before full screen snapshots to improve load times and - minimize the chance the user will see an empty task card. --> + minimize the chance the user will see an empty task card. If set to 0, reduced scale + snapshots are disabled, and snapshots will only be stored at config_highResTaskSnapshotScale + --> <item name="config_lowResTaskSnapshotScale" format="float" type="dimen">0.5</item> <!-- Feature flag to store TaskSnapshot in 16 bit pixel format to save memory. --> @@ -3957,14 +3959,14 @@ <!-- Component name for the default module metadata provider on this device --> <string name="config_defaultModuleMetadataProvider" translatable="false">com.android.modulemetadata</string> - <!-- This is the default launcher component to use on secondary displays that support system - decorations. - This launcher activity must support multiple instances and have corresponding launch mode - set in AndroidManifest. + <!-- This is the default launcher package with an activity to use on secondary displays that + support system decorations. + This launcher package must have an activity that supports multiple instances and has + corresponding launch mode set in AndroidManifest. {@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} --> - <string name="config_secondaryHomeComponent" translatable="false">com.android.launcher3/com.android.launcher3.SecondaryDisplayLauncher</string> + <string name="config_secondaryHomePackage" translatable="false">com.android.launcher3</string> - <!-- Force secondary home launcher specified in config_secondaryHomeComponent always. If this is + <!-- Force secondary home launcher specified in config_secondaryHomePackage always. If this is not set, secondary home launcher can be replaced by user. --> <bool name ="config_useSystemProvidedLauncherForSecondary">false</bool> @@ -4404,6 +4406,7 @@ Determines whether the specified key groups can be used to wake up the device. --> <bool name="config_wakeOnDpadKeyPress">true</bool> <bool name="config_wakeOnAssistKeyPress">true</bool> + <bool name="config_wakeOnBackKeyPress">true</bool> <!-- Whether to default to an expanded list of users on the lock screen user switcher. --> <bool name="config_expandLockScreenUserSwitcher">false</bool> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 4f221d0d85fd..e1d94f50f260 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -685,7 +685,7 @@ <!-- The size of the right icon image when on low ram --> <dimen name="notification_right_icon_size_low_ram">@dimen/notification_right_icon_size</dimen> - <dimen name="messaging_avatar_size">@dimen/notification_right_icon_size</dimen> + <dimen name="messaging_avatar_size">52dp</dimen> <dimen name="messaging_group_sending_progress_size">24dp</dimen> diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml index 64768cf4c730..966f495c96e5 100644 --- a/core/res/res/values/styles_device_defaults.xml +++ b/core/res/res/values/styles_device_defaults.xml @@ -146,7 +146,7 @@ easier. <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification</item> </style> <style name="Widget.DeviceDefault.Notification.MessagingName" parent="Widget.Material.Notification.MessagingName"> - <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification.Title</item> + <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification.MessagingName</item> </style> <style name="Widget.DeviceDefault.PreferenceFrameLayout" parent="Widget.Material.PreferenceFrameLayout"/> <style name="Widget.DeviceDefault.ProgressBar.Inverse" parent="Widget.Material.ProgressBar.Inverse"/> @@ -290,6 +290,9 @@ easier. <style name="TextAppearance.DeviceDefault.Notification.Title" parent="TextAppearance.Material.Notification.Title"> <item name="fontFamily">@string/config_headlineFontFamilyMedium</item> </style> + <style name="TextAppearance.DeviceDefault.Notification.MessagingName" parent="TextAppearance.DeviceDefault.Notification.Title"> + <item name="textSize">16sp</item> + </style> <style name="TextAppearance.DeviceDefault.Notification.Reply" parent="TextAppearance.Material.Notification.Reply"> <item name="fontFamily">@string/config_bodyFontFamily</item> </style> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index d5954596535d..3ed3a64e2cb4 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1612,6 +1612,7 @@ <java-symbol type="style" name="TextAppearance.SlidingTabNormal" /> <java-symbol type="style" name="Theme.DeviceDefault.Dialog.NoFrame" /> <java-symbol type="style" name="Theme.IconMenu" /> + <java-symbol type="style" name="Theme.Dream" /> <java-symbol type="style" name="Theme.DeviceDefault.VoiceInteractionSession" /> <java-symbol type="style" name="Pointer" /> <java-symbol type="style" name="LargePointer" /> @@ -3051,6 +3052,7 @@ <!-- Override Wake Key Behavior When Screen is Off --> <java-symbol type="bool" name="config_wakeOnDpadKeyPress" /> <java-symbol type="bool" name="config_wakeOnAssistKeyPress" /> + <java-symbol type="bool" name="config_wakeOnBackKeyPress" /> <!-- Pinner Service --> <java-symbol type="array" name="config_defaultPinnerServiceFiles" /> @@ -3676,7 +3678,7 @@ <java-symbol type="string" name="config_defaultModuleMetadataProvider" /> <!-- For Secondary Launcher --> - <java-symbol type="string" name="config_secondaryHomeComponent" /> + <java-symbol type="string" name="config_secondaryHomePackage" /> <java-symbol type="bool" name="config_useSystemProvidedLauncherForSecondary" /> <java-symbol type="string" name="battery_saver_notification_channel_name" /> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 5d9cb48aa5ed..2ef0c927cc61 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -701,6 +701,11 @@ please see themes_device_defaults.xml. <item name="windowNoDisplay">true</item> </style> + <style name="Theme.Dream"> + <item name="windowBackground">@null</item> + <item name="windowDisablePreview">true</item> + </style> + <!-- Default theme for dialog windows and activities (on API level 10 and lower), which is used by the {@link android.app.Dialog} class. This changes the window to be diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 718ca46a4f18..b42fce02cede 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -138,6 +138,9 @@ <!-- vr test permissions --> <uses-permission android:name="android.permission.RESTRICTED_VR_ACCESS" /> + <!-- WindowMetricsTest permissions --> + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> + <application android:theme="@style/Theme" android:supportsRtl="true"> <uses-library android:name="android.test.runner" /> <uses-library android:name="org.apache.http.legacy" android:required="false" /> diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 372b8c294702..f4fbefe9dde4 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -37,6 +37,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ParceledListSlice; import android.content.pm.ProviderInfo; +import android.content.pm.ProviderInfoList; import android.content.pm.ServiceInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; @@ -397,7 +398,7 @@ public class TransactionParcelTests { @Override public void bindApplication(String s, ApplicationInfo applicationInfo, - List<ProviderInfo> list, ComponentName componentName, ProfilerInfo profilerInfo, + ProviderInfoList list, ComponentName componentName, ProfilerInfo profilerInfo, Bundle bundle, IInstrumentationWatcher iInstrumentationWatcher, IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1, boolean b2, boolean b3, Configuration configuration, diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java index 7c2b98f3e167..d02c6d588585 100644 --- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java +++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java @@ -230,6 +230,16 @@ public class DisplayCutoutTest { } @Test + public void inset_insets_withWaterfallCutout() throws Exception { + DisplayCutout cutout = createCutoutWaterfallOnly(Insets.of(0, 10, 0, 10)).inset(1, 2, 3, 4); + + assertEquals(cutout.getSafeInsetLeft(), 0); + assertEquals(cutout.getSafeInsetTop(), 8); + assertEquals(cutout.getSafeInsetRight(), 0); + assertEquals(cutout.getSafeInsetBottom(), 6); + } + + @Test public void inset_insets_consumeInset() throws Exception { DisplayCutout cutout = mCutoutTop.inset(0, 1000, 0, 0); @@ -457,7 +467,8 @@ public class DisplayCutoutTest { private static DisplayCutout createCutoutWaterfallOnly(Insets waterfallInsets) { return new DisplayCutout( - Insets.of(20, 0, 20, 0), + Insets.of(waterfallInsets.left, waterfallInsets.top, waterfallInsets.right, + waterfallInsets.bottom), ZERO_RECT, ZERO_RECT, ZERO_RECT, diff --git a/core/tests/coretests/src/android/view/KeyEventTest.java b/core/tests/coretests/src/android/view/KeyEventTest.java index 14999c7f5642..623008eafe10 100644 --- a/core/tests/coretests/src/android/view/KeyEventTest.java +++ b/core/tests/coretests/src/android/view/KeyEventTest.java @@ -19,6 +19,7 @@ package android.view; import static android.view.Display.INVALID_DISPLAY; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import android.os.Parcel; @@ -28,10 +29,14 @@ import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.HashSet; +import java.util.Set; + @SmallTest @RunWith(AndroidJUnit4.class) public class KeyEventTest { + private static final int ID = 0xabcdef; private static final int DOWN_TIME = 50; private static final long EVENT_TIME = 100; private static final int ACTION = KeyEvent.ACTION_DOWN; @@ -45,6 +50,8 @@ public class KeyEventTest { private static final byte[] HMAC = null; private static final String CHARACTERS = null; + private static final int ID_SOURCE_MASK = 0x3 << 30; + @Test public void testObtain() { KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT, @@ -75,8 +82,7 @@ public class KeyEventTest { public void testObtainWithDisplayId() { final int displayId = 5; KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT, - METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, displayId, null /* hmac*/, - CHARACTERS); + METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, displayId, CHARACTERS); assertEquals(DOWN_TIME, keyEvent.getDownTime()); assertEquals(EVENT_TIME, keyEvent.getEventTime()); assertEquals(ACTION, keyEvent.getAction()); @@ -91,6 +97,52 @@ public class KeyEventTest { assertEquals(CHARACTERS, keyEvent.getCharacters()); } + /** + * Tests that it can generate 500 consecutive distinct numbers. This is a non-deterministic test + * but with 30 bits randomness the failure rate is roughly 4.52e-5, which is negligible enough. + * Probability formula: N * (N - 1) * ... * (N - n + 1) / N^n, where N = 2^30 and n = 500 for + * this test. + */ + @Test + public void testObtainGeneratesUniqueId() { + Set<Integer> set = new HashSet<>(); + for (int i = 0; i < 500; ++i) { + KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT, + METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, CHARACTERS); + assertFalse("Found duplicate ID in round " + i, + set.contains(keyEvent.getId())); + set.add(keyEvent.getSequenceNumber()); + } + } + + @Test + public void testConstructorGeneratesUniqueId() { + Set<Integer> set = new HashSet<>(); + for (int i = 0; i < 500; ++i) { + KeyEvent keyEvent = new KeyEvent(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT); + assertFalse("Found duplicate sequence number in round " + i, + set.contains(keyEvent.getId())); + set.add(keyEvent.getSequenceNumber()); + } + } + + @Test + public void testObtainGeneratesIdWithRightSource() { + for (int i = 0; i < 500; ++i) { + KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT, + METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, CHARACTERS); + assertEquals(0x3 << 30, ID_SOURCE_MASK & keyEvent.getId()); + } + } + + @Test + public void testConstructorGeneratesIdWithRightSource() { + for (int i = 0; i < 500; ++i) { + KeyEvent keyEvent = new KeyEvent(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT); + assertEquals(0x3 << 30, ID_SOURCE_MASK & keyEvent.getId()); + } + } + @Test public void testParcelUnparcel() { KeyEvent key1 = createKey(); @@ -112,11 +164,12 @@ public class KeyEventTest { } private static KeyEvent createKey() { - return KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT, - METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, INVALID_DISPLAY, HMAC, CHARACTERS); + return KeyEvent.obtain(ID, DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT, METASTATE, + DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, INVALID_DISPLAY, HMAC, CHARACTERS); } private static void compareKeys(KeyEvent key1, KeyEvent key2) { + assertEquals(key1.getId(), key2.getId()); assertEquals(key1.getDownTime(), key2.getDownTime()); assertEquals(key1.getEventTime(), key2.getEventTime()); assertEquals(key1.getAction(), key2.getAction()); diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java index 9d09830c585b..786ae89ac2ae 100644 --- a/core/tests/coretests/src/android/view/MotionEventTest.java +++ b/core/tests/coretests/src/android/view/MotionEventTest.java @@ -23,6 +23,7 @@ import static android.view.MotionEvent.TOOL_TYPE_FINGER; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import android.view.MotionEvent.PointerCoords; @@ -34,9 +35,13 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.HashSet; +import java.util.Set; + @RunWith(AndroidJUnit4.class) @SmallTest public class MotionEventTest { + private static final int ID_SOURCE_MASK = 0x3 << 30; @Test public void testObtainWithDisplayId() { @@ -138,4 +143,30 @@ public class MotionEventTest { assertEquals(30, event.getXCursorPosition(), 0.1); assertEquals(50, event.getYCursorPosition(), 0.1); } + + /** + * Tests that it can generate 500 consecutive distinct numbers. This is a non-deterministic test + * but with 30 bits randomness the failure rate is roughly 4.52e-5, which is negligible enough. + * Probability formula: N * (N - 1) * ... * (N - n + 1) / N^n, where N = 2^30 and n = 500 for + * this test. + */ + @Test + public void testObtainGeneratesUniqueId() { + Set<Integer> set = new HashSet<>(); + for (int i = 0; i < 500; ++i) { + final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */, + ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */); + assertFalse("Found duplicate ID in round " + i, set.contains(event.getId())); + set.add(event.getSequenceNumber()); + } + } + + @Test + public void testObtainGeneratesIdWithRightSource() { + for (int i = 0; i < 500; ++i) { + final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */, + ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */); + assertEquals(0x3 << 30, ID_SOURCE_MASK & event.getId()); + } + } } diff --git a/core/tests/coretests/src/android/view/WindowMetricsTest.java b/core/tests/coretests/src/android/view/WindowMetricsTest.java new file mode 100644 index 000000000000..fa6886075bfd --- /dev/null +++ b/core/tests/coretests/src/android/view/WindowMetricsTest.java @@ -0,0 +1,96 @@ +/* + * 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 android.view; + +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.hardware.display.DisplayManager; +import android.os.Handler; +import android.platform.test.annotations.Presubmit; +import android.util.Size; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for {@link WindowManager#getCurrentWindowMetrics()} and + * {@link WindowManager#getMaximumWindowMetrics()}. + * + * <p>Build/Install/Run: + * atest FrameworksCoreTests:WindowMetricsTest + * + * <p>This test class is a part of Window Manager Service tests and specified in + * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}. + */ +@FlakyTest(bugId = 148789183, detail = "Remove after confirmed it's stable.") +@RunWith(AndroidJUnit4.class) +@SmallTest +@Presubmit +public class WindowMetricsTest { + private Context mWindowContext; + private WindowManager mWm; + + @Before + public void setUp() { + final Context insetContext = InstrumentationRegistry.getInstrumentation() + .getTargetContext(); + final Display display = insetContext.getSystemService(DisplayManager.class) + .getDisplay(DEFAULT_DISPLAY); + mWindowContext = insetContext.createDisplayContext(display) + .createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */); + mWm = mWindowContext.getSystemService(WindowManager.class); + } + + @Test + public void testAddViewANdRemoveView_GetMetrics_DoNotCrash() { + final View view = new View(mWindowContext); + final WindowManager.LayoutParams params = + new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); + Handler.getMain().runWithScissors(() -> { + mWm.addView(view, params); + // Check get metrics do not crash. + WindowMetrics currentMetrics = mWm.getCurrentWindowMetrics(); + WindowMetrics maxMetrics = mWm.getMaximumWindowMetrics(); + verifyMetricsSanity(currentMetrics, maxMetrics); + + mWm.removeViewImmediate(view); + // Check get metrics do not crash. + currentMetrics = mWm.getCurrentWindowMetrics(); + maxMetrics = mWm.getMaximumWindowMetrics(); + verifyMetricsSanity(currentMetrics, maxMetrics); + }, 0); + } + + private static void verifyMetricsSanity(WindowMetrics currentMetrics, + WindowMetrics maxMetrics) { + Size currentSize = currentMetrics.getSize(); + Size maxSize = maxMetrics.getSize(); + + assertTrue(maxSize.getWidth() >= currentSize.getWidth()); + assertTrue(maxSize.getHeight() >= currentSize.getHeight()); + } +} diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java index bbf3b12d9b7d..9af0ed0ab826 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java @@ -36,6 +36,7 @@ import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -85,7 +86,9 @@ import org.mockito.invocation.InvocationOnMock; import java.lang.reflect.Field; import java.util.Collections; +import java.util.HashSet; import java.util.Map; +import java.util.Set; @RunWith(AndroidJUnit4.class) @@ -534,6 +537,36 @@ public class AccessibilityShortcutControllerTest { verify(mRingtone).play(); } + @Test + public void testOnAccessibilityShortcut_showsWarningDialog_ttsLongTimeInit_retrySpoken() + throws Exception { + configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); + configureValidShortcutService(); + configureTtsSpokenPromptEnabled(); + configureHandlerCallbackInvocation(); + AccessibilityShortcutController accessibilityShortcutController = getController(); + Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); + Set<String> features = new HashSet<>(); + features.add(TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED); + doReturn(features, Collections.emptySet()).when(mVoice).getFeatures(); + doReturn(TextToSpeech.LANG_NOT_SUPPORTED, TextToSpeech.LANG_AVAILABLE) + .when(mTextToSpeech).setLanguage(any()); + accessibilityShortcutController.performAccessibilityShortcut(); + + verify(mAlertDialog).show(); + ArgumentCaptor<TextToSpeech.OnInitListener> onInitCap = ArgumentCaptor.forClass( + TextToSpeech.OnInitListener.class); + verify(mFrameworkObjectProvider).getTextToSpeech(any(), onInitCap.capture()); + onInitCap.getValue().onInit(TextToSpeech.SUCCESS); + verify(mTextToSpeech).speak(any(), eq(TextToSpeech.QUEUE_FLUSH), any(), any()); + ArgumentCaptor<DialogInterface.OnDismissListener> onDismissCap = ArgumentCaptor.forClass( + DialogInterface.OnDismissListener.class); + verify(mAlertDialog).setOnDismissListener(onDismissCap.capture()); + onDismissCap.getValue().onDismiss(mAlertDialog); + verify(mTextToSpeech).shutdown(); + verify(mRingtone, times(0)).play(); + } + private void configureNoShortcutService() throws Exception { when(mAccessibilityManagerService .getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY)) diff --git a/data/etc/com.android.documentsui.xml b/data/etc/com.android.documentsui.xml index 4d9860387b8d..0ffaa21d3254 100644 --- a/data/etc/com.android.documentsui.xml +++ b/data/etc/com.android.documentsui.xml @@ -18,5 +18,6 @@ <privapp-permissions package="com.android.documentsui"> <permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/> <permission name="android.permission.INTERACT_ACROSS_USERS"/> + <permission name="android.permission.MODIFY_QUIET_MODE"/> </privapp-permissions> </permissions> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 145fd8b824cf..99605ad64cb8 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -697,6 +697,12 @@ "group": "WM_DEBUG_FOCUS_LIGHT", "at": "com\/android\/server\/wm\/RootWindowContainer.java" }, + "-668956537": { + "message": " THUMBNAIL %s: CREATE", + "level": "INFO", + "group": "WM_SHOW_TRANSACTIONS", + "at": "com\/android\/server\/wm\/SurfaceFreezer.java" + }, "-666510420": { "message": "With display frozen, orientationChangeComplete=%b", "level": "VERBOSE", diff --git a/data/keyboards/Vendor_0079_Product_18d4.kl b/data/keyboards/Vendor_0079_Product_18d4.kl new file mode 100644 index 000000000000..b9a2b67afb41 --- /dev/null +++ b/data/keyboards/Vendor_0079_Product_18d4.kl @@ -0,0 +1,58 @@ +# 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. + +# +# GPD Win 2 X-Box Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_044f_Product_b326.kl b/data/keyboards/Vendor_044f_Product_b326.kl new file mode 100644 index 000000000000..d248d713568f --- /dev/null +++ b/data/keyboards/Vendor_044f_Product_b326.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Thrustmaster Gamepad GP XID +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_045e_Product_028f.kl b/data/keyboards/Vendor_045e_Product_028f.kl new file mode 100644 index 000000000000..cc5b33b9a113 --- /dev/null +++ b/data/keyboards/Vendor_045e_Product_028f.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Microsoft X-Box 360 pad v2 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_046d_Product_c21e.kl b/data/keyboards/Vendor_046d_Product_c21e.kl new file mode 100644 index 000000000000..998074331d4f --- /dev/null +++ b/data/keyboards/Vendor_046d_Product_c21e.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Logitech Gamepad F510 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_046d_Product_c242.kl b/data/keyboards/Vendor_046d_Product_c242.kl new file mode 100644 index 000000000000..51eb44a62187 --- /dev/null +++ b/data/keyboards/Vendor_046d_Product_c242.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Logitech Chillstream Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_056e_Product_2004.kl b/data/keyboards/Vendor_056e_Product_2004.kl new file mode 100644 index 000000000000..9eaa36d740f9 --- /dev/null +++ b/data/keyboards/Vendor_056e_Product_2004.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Elecom JC-U3613M +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_06a3_Product_f51a.kl b/data/keyboards/Vendor_06a3_Product_f51a.kl new file mode 100644 index 000000000000..e52f25724fac --- /dev/null +++ b/data/keyboards/Vendor_06a3_Product_f51a.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Saitek P3600 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0738_Product_4716.kl b/data/keyboards/Vendor_0738_Product_4716.kl new file mode 100644 index 000000000000..5f3d4aa4b276 --- /dev/null +++ b/data/keyboards/Vendor_0738_Product_4716.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Mad Catz Wired Xbox 360 Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0738_Product_4718.kl b/data/keyboards/Vendor_0738_Product_4718.kl new file mode 100644 index 000000000000..756e1e75fb34 --- /dev/null +++ b/data/keyboards/Vendor_0738_Product_4718.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Mad Catz Street Fighter IV FightStick SE +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0738_Product_4726.kl b/data/keyboards/Vendor_0738_Product_4726.kl new file mode 100644 index 000000000000..9d8deb36e46d --- /dev/null +++ b/data/keyboards/Vendor_0738_Product_4726.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Mad Catz Xbox 360 Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0738_Product_4736.kl b/data/keyboards/Vendor_0738_Product_4736.kl new file mode 100644 index 000000000000..c556e25fdeb5 --- /dev/null +++ b/data/keyboards/Vendor_0738_Product_4736.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Mad Catz MicroCon Gamepad +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0738_Product_4740.kl b/data/keyboards/Vendor_0738_Product_4740.kl new file mode 100644 index 000000000000..cdb72683b5dd --- /dev/null +++ b/data/keyboards/Vendor_0738_Product_4740.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Mad Catz Beat Pad +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0738_Product_9871.kl b/data/keyboards/Vendor_0738_Product_9871.kl new file mode 100644 index 000000000000..f404065236d2 --- /dev/null +++ b/data/keyboards/Vendor_0738_Product_9871.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Mad Catz Portable Drum +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0738_Product_b726.kl b/data/keyboards/Vendor_0738_Product_b726.kl new file mode 100644 index 000000000000..05b737f8f919 --- /dev/null +++ b/data/keyboards/Vendor_0738_Product_b726.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Mad Catz Xbox controller - MW2 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0738_Product_beef.kl b/data/keyboards/Vendor_0738_Product_beef.kl new file mode 100644 index 000000000000..f969e73ec5f6 --- /dev/null +++ b/data/keyboards/Vendor_0738_Product_beef.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Mad Catz JOYTECH NEO SE Advanced GamePad +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0738_Product_cb02.kl b/data/keyboards/Vendor_0738_Product_cb02.kl new file mode 100644 index 000000000000..bc2fc35ae172 --- /dev/null +++ b/data/keyboards/Vendor_0738_Product_cb02.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Saitek Cyborg Rumble Pad - PC/Xbox 360 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0738_Product_cb03.kl b/data/keyboards/Vendor_0738_Product_cb03.kl new file mode 100644 index 000000000000..dcbf6b7487b2 --- /dev/null +++ b/data/keyboards/Vendor_0738_Product_cb03.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Saitek P3200 Rumble Pad - PC/Xbox 360 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0738_Product_cb29.kl b/data/keyboards/Vendor_0738_Product_cb29.kl new file mode 100644 index 000000000000..fe81d1c2170b --- /dev/null +++ b/data/keyboards/Vendor_0738_Product_cb29.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Saitek Aviator Stick AV8R02 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0738_Product_f738.kl b/data/keyboards/Vendor_0738_Product_f738.kl new file mode 100644 index 000000000000..2c993807567b --- /dev/null +++ b/data/keyboards/Vendor_0738_Product_f738.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Super SFIV FightStick TE S +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_07ff_Product_ffff.kl b/data/keyboards/Vendor_07ff_Product_ffff.kl new file mode 100644 index 000000000000..637c01bdddfd --- /dev/null +++ b/data/keyboards/Vendor_07ff_Product_ffff.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Mad Catz GamePad +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0113.kl b/data/keyboards/Vendor_0e6f_Product_0113.kl new file mode 100644 index 000000000000..90e1f75d19c0 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0113.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Afterglow AX.1 Gamepad for Xbox 360 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_011f.kl b/data/keyboards/Vendor_0e6f_Product_011f.kl new file mode 100644 index 000000000000..8c63c6bdf0c1 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_011f.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Rock Candy Gamepad Wired Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0131.kl b/data/keyboards/Vendor_0e6f_Product_0131.kl new file mode 100644 index 000000000000..368c37606da3 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0131.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PDP EA Sports Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0133.kl b/data/keyboards/Vendor_0e6f_Product_0133.kl new file mode 100644 index 000000000000..815902edb863 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0133.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Xbox 360 Wired Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0139.kl b/data/keyboards/Vendor_0e6f_Product_0139.kl new file mode 100644 index 000000000000..8e2ae13f906e --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0139.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Afterglow Prismatic Wired Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_013a.kl b/data/keyboards/Vendor_0e6f_Product_013a.kl new file mode 100644 index 000000000000..3f81983bdc34 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_013a.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PDP Xbox One Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0146.kl b/data/keyboards/Vendor_0e6f_Product_0146.kl new file mode 100644 index 000000000000..6ddd056c1597 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0146.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Rock Candy Wired Controller for Xbox One +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0147.kl b/data/keyboards/Vendor_0e6f_Product_0147.kl new file mode 100644 index 000000000000..6745b7c5c294 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0147.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PDP Marvel Xbox One Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0161.kl b/data/keyboards/Vendor_0e6f_Product_0161.kl new file mode 100644 index 000000000000..3f81983bdc34 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0161.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PDP Xbox One Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0162.kl b/data/keyboards/Vendor_0e6f_Product_0162.kl new file mode 100644 index 000000000000..3f81983bdc34 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0162.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PDP Xbox One Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0163.kl b/data/keyboards/Vendor_0e6f_Product_0163.kl new file mode 100644 index 000000000000..3f81983bdc34 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0163.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PDP Xbox One Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0164.kl b/data/keyboards/Vendor_0e6f_Product_0164.kl new file mode 100644 index 000000000000..0fdfd32922cb --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0164.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PDP Battlefield One +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0165.kl b/data/keyboards/Vendor_0e6f_Product_0165.kl new file mode 100644 index 000000000000..f9731e025f4e --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0165.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PDP Titanfall 2 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0201.kl b/data/keyboards/Vendor_0e6f_Product_0201.kl new file mode 100644 index 000000000000..5b4c167d9999 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0201.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Pelican PL-3601 'TSZ' Wired Xbox 360 Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0213.kl b/data/keyboards/Vendor_0e6f_Product_0213.kl new file mode 100644 index 000000000000..9317346faa3e --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0213.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Afterglow Gamepad for Xbox 360 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_021f.kl b/data/keyboards/Vendor_0e6f_Product_021f.kl new file mode 100644 index 000000000000..f8d3f0c08009 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_021f.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Rock Candy Gamepad for Xbox 360 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0246.kl b/data/keyboards/Vendor_0e6f_Product_0246.kl new file mode 100644 index 000000000000..daf8e4525a1e --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0246.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Rock Candy Gamepad for Xbox One 2015 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_02a6.kl b/data/keyboards/Vendor_0e6f_Product_02a6.kl new file mode 100644 index 000000000000..99a59317e724 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_02a6.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PDP Wired Controller for Xbox One - Camo Series +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_02ab.kl b/data/keyboards/Vendor_0e6f_Product_02ab.kl new file mode 100644 index 000000000000..071a56c8c054 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_02ab.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PDP Controller for Xbox One +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0301.kl b/data/keyboards/Vendor_0e6f_Product_0301.kl new file mode 100644 index 000000000000..a3b982d514de --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0301.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Logic3 Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0346.kl b/data/keyboards/Vendor_0e6f_Product_0346.kl new file mode 100644 index 000000000000..6fefbf7ca2a4 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0346.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Rock Candy Gamepad for Xbox One 2016 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0401.kl b/data/keyboards/Vendor_0e6f_Product_0401.kl new file mode 100644 index 000000000000..a3b982d514de --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0401.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Logic3 Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0413.kl b/data/keyboards/Vendor_0e6f_Product_0413.kl new file mode 100644 index 000000000000..90e1f75d19c0 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0413.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Afterglow AX.1 Gamepad for Xbox 360 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_0501.kl b/data/keyboards/Vendor_0e6f_Product_0501.kl new file mode 100644 index 000000000000..35831d148640 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_0501.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PDP Xbox 360 Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0e6f_Product_f900.kl b/data/keyboards/Vendor_0e6f_Product_f900.kl new file mode 100644 index 000000000000..44848ba0cf0d --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_f900.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PDP Afterglow AX.1 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0f0d_Product_000a.kl b/data/keyboards/Vendor_0f0d_Product_000a.kl new file mode 100644 index 000000000000..b3aea049f63d --- /dev/null +++ b/data/keyboards/Vendor_0f0d_Product_000a.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Hori Co. DOA4 FightStick +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0f0d_Product_000c.kl b/data/keyboards/Vendor_0f0d_Product_000c.kl new file mode 100644 index 000000000000..49c3addd785e --- /dev/null +++ b/data/keyboards/Vendor_0f0d_Product_000c.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Hori PadEX Turbo +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_0f0d_Product_0067.kl b/data/keyboards/Vendor_0f0d_Product_0067.kl new file mode 100644 index 000000000000..0dfccebd73c1 --- /dev/null +++ b/data/keyboards/Vendor_0f0d_Product_0067.kl @@ -0,0 +1,58 @@ +# 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. + +# +# HORIPAD ONE +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1038_Product_1430.kl b/data/keyboards/Vendor_1038_Product_1430.kl new file mode 100644 index 000000000000..e635c1d8fac6 --- /dev/null +++ b/data/keyboards/Vendor_1038_Product_1430.kl @@ -0,0 +1,58 @@ +# 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. + +# +# SteelSeries Stratus Duo +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1038_Product_1431.kl b/data/keyboards/Vendor_1038_Product_1431.kl new file mode 100644 index 000000000000..e635c1d8fac6 --- /dev/null +++ b/data/keyboards/Vendor_1038_Product_1431.kl @@ -0,0 +1,58 @@ +# 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. + +# +# SteelSeries Stratus Duo +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_11c9_Product_55f0.kl b/data/keyboards/Vendor_11c9_Product_55f0.kl new file mode 100644 index 000000000000..dbb4a7e6ecdb --- /dev/null +++ b/data/keyboards/Vendor_11c9_Product_55f0.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Nacon GC-100XF +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_12ab_Product_0301.kl b/data/keyboards/Vendor_12ab_Product_0301.kl new file mode 100644 index 000000000000..36956c1386f7 --- /dev/null +++ b/data/keyboards/Vendor_12ab_Product_0301.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PDP AFTERGLOW AX.1 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1430_Product_4748.kl b/data/keyboards/Vendor_1430_Product_4748.kl new file mode 100644 index 000000000000..dbe83087ab68 --- /dev/null +++ b/data/keyboards/Vendor_1430_Product_4748.kl @@ -0,0 +1,58 @@ +# 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. + +# +# RedOctane Guitar Hero X-plorer +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1430_Product_f801.kl b/data/keyboards/Vendor_1430_Product_f801.kl new file mode 100644 index 000000000000..a8f91462030c --- /dev/null +++ b/data/keyboards/Vendor_1430_Product_f801.kl @@ -0,0 +1,58 @@ +# 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. + +# +# RedOctane Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_146b_Product_0601.kl b/data/keyboards/Vendor_146b_Product_0601.kl new file mode 100644 index 000000000000..ea2f2211a18e --- /dev/null +++ b/data/keyboards/Vendor_146b_Product_0601.kl @@ -0,0 +1,58 @@ +# 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. + +# +# BigBen Interactive XBOX 360 Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1532_Product_0037.kl b/data/keyboards/Vendor_1532_Product_0037.kl new file mode 100644 index 000000000000..39d8b2e6c1e2 --- /dev/null +++ b/data/keyboards/Vendor_1532_Product_0037.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Razer Sabertooth +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1532_Product_0a03.kl b/data/keyboards/Vendor_1532_Product_0a03.kl new file mode 100644 index 000000000000..75775e993979 --- /dev/null +++ b/data/keyboards/Vendor_1532_Product_0a03.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Razer Wildcat +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_15e4_Product_3f00.kl b/data/keyboards/Vendor_15e4_Product_3f00.kl new file mode 100644 index 000000000000..0d641cf9f990 --- /dev/null +++ b/data/keyboards/Vendor_15e4_Product_3f00.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Power A Mini Pro Elite +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_15e4_Product_3f0a.kl b/data/keyboards/Vendor_15e4_Product_3f0a.kl new file mode 100644 index 000000000000..9e98aeec7b73 --- /dev/null +++ b/data/keyboards/Vendor_15e4_Product_3f0a.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Xbox Airflo wired controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_15e4_Product_3f10.kl b/data/keyboards/Vendor_15e4_Product_3f10.kl new file mode 100644 index 000000000000..7fb0fea910da --- /dev/null +++ b/data/keyboards/Vendor_15e4_Product_3f10.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Batarang Xbox 360 controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_162e_Product_beef.kl b/data/keyboards/Vendor_162e_Product_beef.kl new file mode 100644 index 000000000000..e7fab5dba349 --- /dev/null +++ b/data/keyboards/Vendor_162e_Product_beef.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Joytech Neo-Se Take2 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1bad_Product_0002.kl b/data/keyboards/Vendor_1bad_Product_0002.kl new file mode 100644 index 000000000000..d8eaaba4a90c --- /dev/null +++ b/data/keyboards/Vendor_1bad_Product_0002.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Harmonix Rock Band Guitar +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1bad_Product_f021.kl b/data/keyboards/Vendor_1bad_Product_f021.kl new file mode 100644 index 000000000000..9fd688b0c663 --- /dev/null +++ b/data/keyboards/Vendor_1bad_Product_f021.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Mad Cats Ghost Recon FS GamePad +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1bad_Product_f025.kl b/data/keyboards/Vendor_1bad_Product_f025.kl new file mode 100644 index 000000000000..03aab446cf81 --- /dev/null +++ b/data/keyboards/Vendor_1bad_Product_f025.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Mad Catz Call Of Duty +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1bad_Product_f028.kl b/data/keyboards/Vendor_1bad_Product_f028.kl new file mode 100644 index 000000000000..51733313d999 --- /dev/null +++ b/data/keyboards/Vendor_1bad_Product_f028.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Street Fighter IV FightPad +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1bad_Product_f038.kl b/data/keyboards/Vendor_1bad_Product_f038.kl new file mode 100644 index 000000000000..79e147d06d69 --- /dev/null +++ b/data/keyboards/Vendor_1bad_Product_f038.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Street Fighter IV FightStick TE +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1bad_Product_f501.kl b/data/keyboards/Vendor_1bad_Product_f501.kl new file mode 100644 index 000000000000..1282532ab5ab --- /dev/null +++ b/data/keyboards/Vendor_1bad_Product_f501.kl @@ -0,0 +1,58 @@ +# 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. + +# +# HoriPad EX2 Turbo +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1bad_Product_f506.kl b/data/keyboards/Vendor_1bad_Product_f506.kl new file mode 100644 index 000000000000..3a9d4620ba21 --- /dev/null +++ b/data/keyboards/Vendor_1bad_Product_f506.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Hori Real Arcade Pro.EX Premium VLX +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1bad_Product_f900.kl b/data/keyboards/Vendor_1bad_Product_f900.kl new file mode 100644 index 000000000000..9cfceb433ad8 --- /dev/null +++ b/data/keyboards/Vendor_1bad_Product_f900.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Harmonix Xbox 360 Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1bad_Product_f901.kl b/data/keyboards/Vendor_1bad_Product_f901.kl new file mode 100644 index 000000000000..86d45e58ad01 --- /dev/null +++ b/data/keyboards/Vendor_1bad_Product_f901.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Gamestop Xbox 360 Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1bad_Product_f903.kl b/data/keyboards/Vendor_1bad_Product_f903.kl new file mode 100644 index 000000000000..f61c05005933 --- /dev/null +++ b/data/keyboards/Vendor_1bad_Product_f903.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Tron Xbox 360 controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1bad_Product_f904.kl b/data/keyboards/Vendor_1bad_Product_f904.kl new file mode 100644 index 000000000000..3e02a24f9e5e --- /dev/null +++ b/data/keyboards/Vendor_1bad_Product_f904.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PDP Versus Fighting Pad +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1bad_Product_fa01.kl b/data/keyboards/Vendor_1bad_Product_fa01.kl new file mode 100644 index 000000000000..517413d2edda --- /dev/null +++ b/data/keyboards/Vendor_1bad_Product_fa01.kl @@ -0,0 +1,58 @@ +# 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. + +# +# MadCatz GamePad +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1bad_Product_fd00.kl b/data/keyboards/Vendor_1bad_Product_fd00.kl new file mode 100644 index 000000000000..fc6a4f85339a --- /dev/null +++ b/data/keyboards/Vendor_1bad_Product_fd00.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Razer Onza TE +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_1bad_Product_fd01.kl b/data/keyboards/Vendor_1bad_Product_fd01.kl new file mode 100644 index 000000000000..8882abf0f7be --- /dev/null +++ b/data/keyboards/Vendor_1bad_Product_fd01.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Razer Onza +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_24c6_Product_5300.kl b/data/keyboards/Vendor_24c6_Product_5300.kl new file mode 100644 index 000000000000..303e906ad721 --- /dev/null +++ b/data/keyboards/Vendor_24c6_Product_5300.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PowerA MINI PROEX Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_24c6_Product_5303.kl b/data/keyboards/Vendor_24c6_Product_5303.kl new file mode 100644 index 000000000000..9e98aeec7b73 --- /dev/null +++ b/data/keyboards/Vendor_24c6_Product_5303.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Xbox Airflo wired controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_24c6_Product_530a.kl b/data/keyboards/Vendor_24c6_Product_530a.kl new file mode 100644 index 000000000000..aa88515c0826 --- /dev/null +++ b/data/keyboards/Vendor_24c6_Product_530a.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Xbox 360 Pro EX Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_24c6_Product_531a.kl b/data/keyboards/Vendor_24c6_Product_531a.kl new file mode 100644 index 000000000000..09a5c6a85c4f --- /dev/null +++ b/data/keyboards/Vendor_24c6_Product_531a.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PowerA Pro Ex +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_24c6_Product_5397.kl b/data/keyboards/Vendor_24c6_Product_5397.kl new file mode 100644 index 000000000000..66b896a36d5f --- /dev/null +++ b/data/keyboards/Vendor_24c6_Product_5397.kl @@ -0,0 +1,58 @@ +# 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. + +# +# FUS1ON Tournament Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_24c6_Product_541a.kl b/data/keyboards/Vendor_24c6_Product_541a.kl new file mode 100644 index 000000000000..24271fbbba4b --- /dev/null +++ b/data/keyboards/Vendor_24c6_Product_541a.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PowerA Xbox One Mini Wired Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_24c6_Product_542a.kl b/data/keyboards/Vendor_24c6_Product_542a.kl new file mode 100644 index 000000000000..623bd1375b11 --- /dev/null +++ b/data/keyboards/Vendor_24c6_Product_542a.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Xbox ONE spectra +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_24c6_Product_543a.kl b/data/keyboards/Vendor_24c6_Product_543a.kl new file mode 100644 index 000000000000..59769c4c0585 --- /dev/null +++ b/data/keyboards/Vendor_24c6_Product_543a.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PowerA Xbox One wired controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_24c6_Product_5500.kl b/data/keyboards/Vendor_24c6_Product_5500.kl new file mode 100644 index 000000000000..d76d7d08267b --- /dev/null +++ b/data/keyboards/Vendor_24c6_Product_5500.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Hori XBOX 360 EX 2 with Turbo +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_24c6_Product_5501.kl b/data/keyboards/Vendor_24c6_Product_5501.kl new file mode 100644 index 000000000000..64d901af132b --- /dev/null +++ b/data/keyboards/Vendor_24c6_Product_5501.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Hori Real Arcade Pro VX-SA +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_24c6_Product_5506.kl b/data/keyboards/Vendor_24c6_Product_5506.kl new file mode 100644 index 000000000000..bfb23c309029 --- /dev/null +++ b/data/keyboards/Vendor_24c6_Product_5506.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Hori SOULCALIBUR V Stick +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_24c6_Product_550d.kl b/data/keyboards/Vendor_24c6_Product_550d.kl new file mode 100644 index 000000000000..24852b0bd867 --- /dev/null +++ b/data/keyboards/Vendor_24c6_Product_550d.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Hori GEM Xbox controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_24c6_Product_551a.kl b/data/keyboards/Vendor_24c6_Product_551a.kl new file mode 100644 index 000000000000..5e338a5c22f9 --- /dev/null +++ b/data/keyboards/Vendor_24c6_Product_551a.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PowerA FUSION Pro Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_24c6_Product_561a.kl b/data/keyboards/Vendor_24c6_Product_561a.kl new file mode 100644 index 000000000000..57b7ddcca207 --- /dev/null +++ b/data/keyboards/Vendor_24c6_Product_561a.kl @@ -0,0 +1,58 @@ +# 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. + +# +# PowerA FUSION Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_24c6_Product_5b02.kl b/data/keyboards/Vendor_24c6_Product_5b02.kl new file mode 100644 index 000000000000..bcf354d0e418 --- /dev/null +++ b/data/keyboards/Vendor_24c6_Product_5b02.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Thrustmaster, Inc. GPX Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_24c6_Product_5d04.kl b/data/keyboards/Vendor_24c6_Product_5d04.kl new file mode 100644 index 000000000000..39d8b2e6c1e2 --- /dev/null +++ b/data/keyboards/Vendor_24c6_Product_5d04.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Razer Sabertooth +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/data/keyboards/Vendor_24c6_Product_fafe.kl b/data/keyboards/Vendor_24c6_Product_fafe.kl new file mode 100644 index 000000000000..f8d3f0c08009 --- /dev/null +++ b/data/keyboards/Vendor_24c6_Product_fafe.kl @@ -0,0 +1,58 @@ +# 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. + +# +# Rock Candy Gamepad for Xbox 360 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index f2b4db1afdac..f800f9ee91c9 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -2531,7 +2531,7 @@ final public class MediaCodec { int offset, int size, long presentationTimeUs, int flags) throws CryptoException { synchronized(mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } invalidateByteBuffer(mCachedInputBuffers, index); @@ -2783,7 +2783,7 @@ final public class MediaCodec { long presentationTimeUs, int flags) throws CryptoException { synchronized(mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } invalidateByteBuffer(mCachedInputBuffers, index); @@ -2818,7 +2818,7 @@ final public class MediaCodec { */ public final int dequeueInputBuffer(long timeoutUs) { synchronized (mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } } @@ -3534,7 +3534,7 @@ final public class MediaCodec { public final int dequeueOutputBuffer( @NonNull BufferInfo info, long timeoutUs) { synchronized (mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } } @@ -3916,7 +3916,7 @@ final public class MediaCodec { @NonNull public ByteBuffer[] getInputBuffers() { synchronized (mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } if (mCachedInputBuffers == null) { @@ -3952,7 +3952,7 @@ final public class MediaCodec { @NonNull public ByteBuffer[] getOutputBuffers() { synchronized (mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } if (mCachedOutputBuffers == null) { @@ -3984,7 +3984,7 @@ final public class MediaCodec { @Nullable public ByteBuffer getInputBuffer(int index) { synchronized (mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } } @@ -4018,7 +4018,7 @@ final public class MediaCodec { @Nullable public Image getInputImage(int index) { synchronized (mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } } @@ -4052,7 +4052,7 @@ final public class MediaCodec { @Nullable public ByteBuffer getOutputBuffer(int index) { synchronized (mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } } @@ -4085,7 +4085,7 @@ final public class MediaCodec { @Nullable public Image getOutputImage(int index) { synchronized (mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } } diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index b579144b45e8..95199cc8e045 100755 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -240,7 +240,7 @@ public abstract class TvInputService extends Service { * @param inputId The ID of the TV input associated with the session. */ @Nullable - public abstract Session onCreateSession(String inputId); + public abstract Session onCreateSession(@NonNull String inputId); /** * Returns a concrete implementation of {@link RecordingSession}. @@ -251,7 +251,7 @@ public abstract class TvInputService extends Service { * @param inputId The ID of the TV input associated with the recording session. */ @Nullable - public RecordingSession onCreateRecordingSession(String inputId) { + public RecordingSession onCreateRecordingSession(@NonNull String inputId) { return null; } diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 73769be3d6ad..7a684b36f460 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -18,11 +18,13 @@ package android.media.tv.tuner; import android.annotation.BytesLong; import android.annotation.CallbackExecutor; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; +import android.hardware.tv.tuner.V1_0.Constants; import android.media.tv.TvInputService; import android.media.tv.tuner.TunerConstants.Result; import android.media.tv.tuner.dvr.DvrPlayback; @@ -45,6 +47,8 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; @@ -67,6 +71,22 @@ public class Tuner implements AutoCloseable { private static final int MSG_ON_FILTER_STATUS = 3; private static final int MSG_ON_LNB_EVENT = 4; + /** @hide */ + @IntDef(prefix = "DVR_TYPE_", value = {DVR_TYPE_RECORD, DVR_TYPE_PLAYBACK}) + @Retention(RetentionPolicy.SOURCE) + public @interface DvrType {} + + /** + * DVR for recording. + * @hide + */ + public static final int DVR_TYPE_RECORD = Constants.DvrType.RECORD; + /** + * DVR for playback of recorded programs. + * @hide + */ + public static final int DVR_TYPE_PLAYBACK = Constants.DvrType.PLAYBACK; + static { System.loadLibrary("media_tv_tuner"); nativeInit(); diff --git a/media/java/android/media/tv/tuner/dvr/Dvr.java b/media/java/android/media/tv/tuner/dvr/Dvr.java deleted file mode 100644 index 4183e3bbe640..000000000000 --- a/media/java/android/media/tv/tuner/dvr/Dvr.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 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 android.media.tv.tuner.dvr; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.hardware.tv.tuner.V1_0.Constants; -import android.media.tv.tuner.TunerConstants.Result; -import android.media.tv.tuner.filter.Filter; -import android.os.ParcelFileDescriptor; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Digital Video Record (DVR) interface provides record control on Demux's output buffer and - * playback control on Demux's input buffer. - * - * @hide - */ -@SystemApi -public class Dvr implements AutoCloseable { - - /** @hide */ - @IntDef(prefix = "TYPE_", value = {TYPE_RECORD, TYPE_PLAYBACK}) - @Retention(RetentionPolicy.SOURCE) - public @interface Type {} - - /** - * DVR for recording. - */ - public static final int TYPE_RECORD = Constants.DvrType.RECORD; - /** - * DVR for playback of recorded programs. - */ - public static final int TYPE_PLAYBACK = Constants.DvrType.PLAYBACK; - - - final int mType; - long mNativeContext; - - private native int nativeAttachFilter(Filter filter); - private native int nativeDetachFilter(Filter filter); - private native int nativeConfigureDvr(DvrSettings settings); - private native int nativeStartDvr(); - private native int nativeStopDvr(); - private native int nativeFlushDvr(); - private native int nativeClose(); - private native void nativeSetFileDescriptor(int fd); - - protected Dvr(int type) { - mType = type; - } - - /** - * Attaches a filter to DVR interface for recording. - * - * @param filter the filter to be attached. - * @return result status of the operation. - */ - @Result - public int attachFilter(@NonNull Filter filter) { - return nativeAttachFilter(filter); - } - - /** - * Detaches a filter from DVR interface. - * - * @param filter the filter to be detached. - * @return result status of the operation. - */ - @Result - public int detachFilter(@NonNull Filter filter) { - return nativeDetachFilter(filter); - } - - /** - * Configures the DVR. - * - * @param settings the settings of the DVR interface. - * @return result status of the operation. - */ - @Result - public int configure(@NonNull DvrSettings settings) { - return nativeConfigureDvr(settings); - } - - /** - * Starts DVR. - * - * <p>Starts consuming playback data or producing data for recording. - * - * @return result status of the operation. - */ - @Result - public int start() { - return nativeStartDvr(); - } - - /** - * Stops DVR. - * - * <p>Stops consuming playback data or producing data for recording. - * - * @return result status of the operation. - */ - @Result - public int stop() { - return nativeStopDvr(); - } - - /** - * Flushed DVR data. - * - * <p>The data in DVR buffer is cleared. - * - * @return result status of the operation. - */ - @Result - public int flush() { - return nativeFlushDvr(); - } - - /** - * Closes the DVR instance to release resources. - */ - public void close() { - nativeClose(); - } - - /** - * Sets file descriptor to read/write data. - * - * @param fd the file descriptor to read/write data. - */ - public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) { - nativeSetFileDescriptor(fd.getFd()); - } - - @Type - int getType() { - return mType; - } -} diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java index eb3157434a95..7c15bb74a94b 100644 --- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java +++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java @@ -21,6 +21,9 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; import android.hardware.tv.tuner.V1_0.Constants; +import android.media.tv.tuner.TunerConstants.Result; +import android.media.tv.tuner.filter.Filter; +import android.os.ParcelFileDescriptor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -33,7 +36,7 @@ import java.lang.annotation.RetentionPolicy; * @hide */ @SystemApi -public class DvrPlayback extends Dvr { +public class DvrPlayback implements AutoCloseable { /** @hide */ @@ -66,17 +69,110 @@ public class DvrPlayback extends Dvr { */ public static final int PLAYBACK_STATUS_FULL = Constants.PlaybackStatus.SPACE_FULL; + long mNativeContext; - + private native int nativeAttachFilter(Filter filter); + private native int nativeDetachFilter(Filter filter); + private native int nativeConfigureDvr(DvrSettings settings); + private native int nativeStartDvr(); + private native int nativeStopDvr(); + private native int nativeFlushDvr(); + private native int nativeClose(); + private native void nativeSetFileDescriptor(int fd); private native long nativeRead(long size); private native long nativeRead(byte[] bytes, long offset, long size); private DvrPlayback() { - super(Dvr.TYPE_PLAYBACK); } /** + * Attaches a filter to DVR interface for recording. + * + * @param filter the filter to be attached. + * @return result status of the operation. + */ + @Result + public int attachFilter(@NonNull Filter filter) { + return nativeAttachFilter(filter); + } + + /** + * Detaches a filter from DVR interface. + * + * @param filter the filter to be detached. + * @return result status of the operation. + */ + @Result + public int detachFilter(@NonNull Filter filter) { + return nativeDetachFilter(filter); + } + + /** + * Configures the DVR. + * + * @param settings the settings of the DVR interface. + * @return result status of the operation. + */ + @Result + public int configure(@NonNull DvrSettings settings) { + return nativeConfigureDvr(settings); + } + + /** + * Starts DVR. + * + * <p>Starts consuming playback data or producing data for recording. + * + * @return result status of the operation. + */ + @Result + public int start() { + return nativeStartDvr(); + } + + /** + * Stops DVR. + * + * <p>Stops consuming playback data or producing data for recording. + * + * @return result status of the operation. + */ + @Result + public int stop() { + return nativeStopDvr(); + } + + /** + * Flushed DVR data. + * + * <p>The data in DVR buffer is cleared. + * + * @return result status of the operation. + */ + @Result + public int flush() { + return nativeFlushDvr(); + } + + /** + * Closes the DVR instance to release resources. + */ + @Override + public void close() { + nativeClose(); + } + + /** + * Sets file descriptor to read/write data. + * + * @param fd the file descriptor to read/write data. + */ + public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) { + nativeSetFileDescriptor(fd.getFd()); + } + + /** * Reads data from the file for DVR playback. * * @param size the maximum number of bytes to read. diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java index 3128ca5da641..52ef5e63389f 100644 --- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java +++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java @@ -19,6 +19,9 @@ package android.media.tv.tuner.dvr; import android.annotation.BytesLong; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.media.tv.tuner.TunerConstants.Result; +import android.media.tv.tuner.filter.Filter; +import android.os.ParcelFileDescriptor; /** * Digital Video Record (DVR) recorder class which provides record control on Demux's output buffer. @@ -26,12 +29,108 @@ import android.annotation.SystemApi; * @hide */ @SystemApi -public class DvrRecorder extends Dvr { +public class DvrRecorder implements AutoCloseable { + long mNativeContext; + + private native int nativeAttachFilter(Filter filter); + private native int nativeDetachFilter(Filter filter); + private native int nativeConfigureDvr(DvrSettings settings); + private native int nativeStartDvr(); + private native int nativeStopDvr(); + private native int nativeFlushDvr(); + private native int nativeClose(); + private native void nativeSetFileDescriptor(int fd); private native long nativeWrite(long size); private native long nativeWrite(byte[] bytes, long offset, long size); private DvrRecorder() { - super(Dvr.TYPE_RECORD); + } + + + /** + * Attaches a filter to DVR interface for recording. + * + * @param filter the filter to be attached. + * @return result status of the operation. + */ + @Result + public int attachFilter(@NonNull Filter filter) { + return nativeAttachFilter(filter); + } + + /** + * Detaches a filter from DVR interface. + * + * @param filter the filter to be detached. + * @return result status of the operation. + */ + @Result + public int detachFilter(@NonNull Filter filter) { + return nativeDetachFilter(filter); + } + + /** + * Configures the DVR. + * + * @param settings the settings of the DVR interface. + * @return result status of the operation. + */ + @Result + public int configure(@NonNull DvrSettings settings) { + return nativeConfigureDvr(settings); + } + + /** + * Starts DVR. + * + * <p>Starts consuming playback data or producing data for recording. + * + * @return result status of the operation. + */ + @Result + public int start() { + return nativeStartDvr(); + } + + /** + * Stops DVR. + * + * <p>Stops consuming playback data or producing data for recording. + * + * @return result status of the operation. + */ + @Result + public int stop() { + return nativeStopDvr(); + } + + /** + * Flushed DVR data. + * + * <p>The data in DVR buffer is cleared. + * + * @return result status of the operation. + */ + @Result + public int flush() { + return nativeFlushDvr(); + } + + /** + * Closes the DVR instance to release resources. + */ + @Override + public void close() { + nativeClose(); + } + + /** + * Sets file descriptor to read/write data. + * + * @param fd the file descriptor to read/write data. + */ + public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) { + nativeSetFileDescriptor(fd.getFd()); } /** diff --git a/media/java/android/media/tv/tuner/dvr/DvrSettings.java b/media/java/android/media/tv/tuner/dvr/DvrSettings.java index f9dc6821039a..362b108e41fd 100644 --- a/media/java/android/media/tv/tuner/dvr/DvrSettings.java +++ b/media/java/android/media/tv/tuner/dvr/DvrSettings.java @@ -30,7 +30,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * DVR settings used to configure {@link Dvr}. + * DVR settings used to configure {@link DvrPlayback} and {@link DvrRecorder}. * * @hide */ diff --git a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java b/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java index 68372444cb83..598ff8f3f075 100644 --- a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java +++ b/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java @@ -78,7 +78,8 @@ public final class ResourceClientProfile implements Parcelable { * {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE} * {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD}. * New [use case : priority value] pair can be defined in the manifest by the - * OEM. Any undefined use case would cause IllegalArgumentException. + * OEM. The id of the useCaseVendor should be passed through this parameter. Any + * undefined use case would cause IllegalArgumentException. */ public ResourceClientProfile(@NonNull String tvInputSessionId, int useCase) { diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index a4eada4ebe0b..f39f9124efbb 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -122,6 +122,7 @@ import com.android.systemui.statusbar.phone.LockscreenLockIconController; import com.android.systemui.statusbar.phone.LockscreenWallpaper; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationShadeWindowController; +import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; @@ -324,6 +325,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt KeyguardDismissUtil keyguardDismissUtil, ExtensionController extensionController, UserInfoControllerImpl userInfoControllerImpl, + PhoneStatusBarPolicy phoneStatusBarPolicy, DismissCallbackRegistry dismissCallbackRegistry, StatusBarTouchableRegionManager statusBarTouchableRegionManager, /* Car Settings injected components. */ @@ -407,6 +409,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt keyguardDismissUtil, extensionController, userInfoControllerImpl, + phoneStatusBarPolicy, dismissCallbackRegistry, statusBarTouchableRegionManager); mUserSwitcherController = userSwitcherController; diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java index 729496580bf6..843e7c55852a 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java @@ -82,6 +82,7 @@ import com.android.systemui.statusbar.phone.LockscreenLockIconController; import com.android.systemui.statusbar.phone.LockscreenWallpaper; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationShadeWindowController; +import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -197,6 +198,7 @@ public class CarStatusBarModule { KeyguardDismissUtil keyguardDismissUtil, ExtensionController extensionController, UserInfoControllerImpl userInfoControllerImpl, + PhoneStatusBarPolicy phoneStatusBarPolicy, DismissCallbackRegistry dismissCallbackRegistry, StatusBarTouchableRegionManager statusBarTouchableRegionManager, CarServiceProvider carServiceProvider, @@ -278,6 +280,7 @@ public class CarStatusBarModule { keyguardDismissUtil, extensionController, userInfoControllerImpl, + phoneStatusBarPolicy, dismissCallbackRegistry, statusBarTouchableRegionManager, carServiceProvider, diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java index 37a77be52983..f36f97ddf610 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java @@ -82,6 +82,9 @@ public class DynamicSystemInstallationService extends Service static final String DEFAULT_DSU_SLOT = "dsu"; static final String KEY_PUBKEY = "KEY_PUBKEY"; + // Default userdata partition size is 2GiB. + private static final long DEFAULT_USERDATA_SIZE = 2L << 30; + /* * Intent actions */ @@ -270,6 +273,10 @@ public class DynamicSystemInstallationService extends Service String dsuSlot = intent.getStringExtra(KEY_DSU_SLOT); String publicKey = intent.getStringExtra(KEY_PUBKEY); + if (userdataSize == 0) { + userdataSize = DEFAULT_USERDATA_SIZE; + } + if (TextUtils.isEmpty(dsuSlot)) { dsuSlot = DEFAULT_DSU_SLOT; } diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 6d11461d6cfa..2297ddf5272b 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -892,9 +892,9 @@ <!-- UI debug setting: enable gpu debug layers summary [CHAR LIMIT=50] --> <string name="enable_gpu_debug_layers_summary">Allow loading GPU debug layers for debug apps</string> - <!-- UI debug setting: enable verbose vendor logging [CHAR LIMIT=30] --> + <!-- UI debug setting: enable verbose vendor logging [CHAR LIMIT=60] --> <string name="enable_verbose_vendor_logging">Enable verbose vendor logging</string> - <!-- UI debug setting: enable verbose vendor logging summary [CHAR LIMIT=100] --> + <!-- UI debug setting: enable verbose vendor logging summary [CHAR LIMIT=NONE] --> <string name="enable_verbose_vendor_logging_summary">Allow additional vendor logs to be included in bug reports, may contain private information</string> <!-- UI debug setting: scaling factor for window animations [CHAR LIMIT=25] --> diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java index 3c0f6fe8ccbb..57e680849fec 100644 --- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java +++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java @@ -136,7 +136,7 @@ public class DreamBackend { if (mDreamManager == null) return null; try { - return mDreamManager.getDefaultDreamComponent(); + return mDreamManager.getDefaultDreamComponentForUser(mContext.getUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed to get default dream", e); return null; @@ -269,7 +269,7 @@ public class DreamBackend { if (mDreamManager == null || dreamInfo == null || dreamInfo.componentName == null) return; try { - mDreamManager.testDream(dreamInfo.componentName); + mDreamManager.testDream(mContext.getUserId(), dreamInfo.componentName); } catch (RemoteException e) { Log.w(TAG, "Failed to preview " + dreamInfo, e); } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java index 3f55ceaad29a..8bf48e59165e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java @@ -68,6 +68,7 @@ public class WifiStatusTracker extends ConnectivityManager.NetworkCallback { private WifiInfo mWifiInfo; public boolean enabled; + public boolean isCaptivePortal; public int state; public boolean connected; public String ssid; @@ -155,9 +156,11 @@ public class WifiStatusTracker extends ConnectivityManager.NetworkCallback { private void updateStatusLabel() { final NetworkCapabilities networkCapabilities = mConnectivityManager.getNetworkCapabilities(mWifiManager.getCurrentNetwork()); + isCaptivePortal = false; if (networkCapabilities != null) { if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) { statusLabel = mContext.getString(R.string.wifi_status_sign_in_required); + isCaptivePortal = true; return; } else if (networkCapabilities.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY)) { statusLabel = mContext.getString(R.string.wifi_limited_connection); diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 0f2ee6ac8bbd..610165a44626 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -268,6 +268,7 @@ public class SettingsBackupTest { Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS, Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS, + Settings.Global.ENHANCED_CONNECTIVITY_ENABLED, Settings.Global.ENHANCED_4G_MODE_ENABLED, Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES, Settings.Global.ERROR_LOGCAT_PREFIX, diff --git a/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml b/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml index 7964609f62e0..015e9f99212d 100644 --- a/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml +++ b/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml @@ -16,6 +16,6 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="true" - android:color="@color/notification_guts_priority_button_bg_stroke_color_selected" /> + android:color="?android:attr/colorAccent" /> <item android:color="@color/notification_guts_priority_button_bg_stroke_color" /> </selector> diff --git a/packages/SystemUI/res/color/notification_guts_priority_contents.xml b/packages/SystemUI/res/color/notification_guts_priority_contents.xml index 56c43f0e5642..42f01896d7a1 100644 --- a/packages/SystemUI/res/color/notification_guts_priority_contents.xml +++ b/packages/SystemUI/res/color/notification_guts_priority_contents.xml @@ -16,6 +16,6 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="true" - android:color="@color/notification_guts_priority_button_content_color_selected" /> + android:color="?android:attr/colorAccent" /> <item android:color="@color/notification_guts_priority_button_content_color" /> </selector> diff --git a/packages/SystemUI/res/drawable/notification_guts_bg.xml b/packages/SystemUI/res/drawable/notification_guts_bg.xml index 1730dcefe480..2fe6c7b2d1a2 100644 --- a/packages/SystemUI/res/drawable/notification_guts_bg.xml +++ b/packages/SystemUI/res/drawable/notification_guts_bg.xml @@ -16,7 +16,7 @@ --> <shape xmlns:android="http://schemas.android.com/apk/res/android"> - <solid android:color="@color/notification_guts_bg_color" /> + <solid android:color="@color/notification_material_background_color" /> <!--The radius is 1dp smaller than the notification one, to avoid aliasing bugs on the corners --> <corners android:radius="1dp" /> </shape> diff --git a/packages/SystemUI/res/drawable/rounded_user_switcher_bg.xml b/packages/SystemUI/res/drawable/rounded_user_switcher_bg.xml new file mode 100644 index 000000000000..e3d010ee7674 --- /dev/null +++ b/packages/SystemUI/res/drawable/rounded_user_switcher_bg.xml @@ -0,0 +1,32 @@ +<?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. + --> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:attr/colorControlHighlight"> + <item> + <shape> + <solid android:color="@color/qs_user_detail_avatar_frame" /> + + <padding + android:left="1dp" + android:right="1dp" + android:bottom="1dp" + android:top="1dp" /> + + <corners android:radius="48dp" /> + </shape> + </item> +</ripple> diff --git a/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml new file mode 100644 index 000000000000..1f38b1ea1da5 --- /dev/null +++ b/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml @@ -0,0 +1,44 @@ +<?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. + --> + +<!-- LinearLayout --> +<com.android.systemui.statusbar.policy.KeyguardUserDetailItemView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:sysui="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="8dp" + android:layout_marginEnd="8dp" + android:gravity="end|center_vertical" + android:clickable="true" + android:background="@drawable/rounded_user_switcher_bg" + sysui:regularTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher" + sysui:activatedTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.Activated"> + <TextView android:id="@+id/user_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="13dp" + android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher" + /> + <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/user_picture" + android:layout_width="@dimen/framed_avatar_size" + android:layout_height="@dimen/framed_avatar_size" + android:contentDescription="@null" + sysui:badgeDiameter="18dp" + sysui:badgeMargin="1dp" /> +</com.android.systemui.statusbar.policy.KeyguardUserDetailItemView> diff --git a/packages/SystemUI/res/layout/app_ops_info.xml b/packages/SystemUI/res/layout/app_ops_info.xml index 82a0115098f2..bfa252c5a14b 100644 --- a/packages/SystemUI/res/layout/app_ops_info.xml +++ b/packages/SystemUI/res/layout/app_ops_info.xml @@ -26,7 +26,7 @@ android:orientation="vertical" android:paddingStart="@*android:dimen/notification_content_margin_start" android:paddingEnd="@*android:dimen/notification_content_margin_end" - android:background="@color/notification_guts_bg_color" + android:background="@color/notification_material_background_color" android:theme="@*android:style/Theme.DeviceDefault.Light"> <!-- Package Info --> diff --git a/packages/SystemUI/res/layout/controls_management.xml b/packages/SystemUI/res/layout/controls_management.xml index a7379bedebef..6533c18a41a9 100644 --- a/packages/SystemUI/res/layout/controls_management.xml +++ b/packages/SystemUI/res/layout/controls_management.xml @@ -70,12 +70,14 @@ android:layout_height="match_parent" android:padding="4dp"> - <TextView + <Button + android:id="@+id/other_apps" + android:visibility="gone" android:layout_width="wrap_content" android:layout_height="match_parent" + android:gravity="center_vertical" android:text="See other apps" - android:textAppearance="@style/TextAppearance.Control.Title" - android:textColor="?android:attr/colorPrimary" + style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent"/> @@ -85,6 +87,7 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:text="Done" + style="@*android:style/Widget.DeviceDefault.Button.Colored" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent"/> diff --git a/packages/SystemUI/res/layout/controls_management_favorites.xml b/packages/SystemUI/res/layout/controls_management_favorites.xml index 62056e60372d..aab32f45e77a 100644 --- a/packages/SystemUI/res/layout/controls_management_favorites.xml +++ b/packages/SystemUI/res/layout/controls_management_favorites.xml @@ -14,97 +14,26 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<androidx.constraintlayout.widget.ConstraintLayout +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView - android:id="@+id/error_message" + android:id="@+id/status_message" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/controls_management_list_margin" - android:text="@string/controls_favorite_load_error" android:textAppearance="?android:attr/textAppearanceSmall" - android:visibility="gone" android:gravity="center_horizontal" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="parent" - app:layout_constraintBottom_toTopOf="@id/text_favorites" /> - <TextView - android:id="@+id/text_favorites" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/controls_management_list_margin" - android:text="@string/controls_favorite_header_favorites" - android:textAppearance="?android:attr/textAppearanceSmall" - android:textAllCaps="true" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintBottom_toTopOf="@id/divider1" - app:layout_constraintTop_toBottomOf="@id/error_message" - /> - - <View - android:id="@+id/divider1" - android:layout_width="match_parent" - android:layout_height="@dimen/controls_app_divider_height" - android:layout_gravity="center_horizontal|top" - android:background="?android:attr/listDivider" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintBottom_toTopOf="@id/listFavorites" - app:layout_constraintTop_toBottomOf="@id/text_favorites" - /> - - <androidx.recyclerview.widget.RecyclerView - android:id="@+id/listFavorites" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_marginTop="@dimen/controls_management_list_margin" - android:nestedScrollingEnabled="false" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintBottom_toTopOf="@id/text_all" - app:layout_constraintTop_toBottomOf="@id/divider1"/> - - <TextView - android:id="@+id/text_all" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/controls_management_list_margin" - android:text="@string/controls_favorite_header_all" - android:textAppearance="?android:attr/textAppearanceSmall" - android:textAllCaps="true" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintBottom_toTopOf="@id/divider2" - app:layout_constraintTop_toBottomOf="@id/listFavorites" - /> - - <View - android:id="@+id/divider2" - android:layout_width="match_parent" - android:layout_height="@dimen/controls_app_divider_height" - android:layout_gravity="center_horizontal|top" - android:background="?android:attr/listDivider" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintBottom_toTopOf="@id/listAll" - app:layout_constraintTop_toBottomOf="@id/text_all" - /> - <androidx.recyclerview.widget.RecyclerView android:id="@+id/listAll" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="@dimen/controls_management_list_margin" - android:nestedScrollingEnabled="false" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@id/divider2"/> + android:nestedScrollingEnabled="false"/> -</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file +</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml index fb38e1c6dc6b..b1e51659817e 100644 --- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml +++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml @@ -27,8 +27,6 @@ android:gravity="center_vertical" android:clickable="true" android:background="@drawable/ripple_drawable" - android:clipChildren="false" - android:clipToPadding="false" sysui:regularTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher" sysui:activatedTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.Activated"> <TextView android:id="@+id/user_name" diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index 87de9d4d3b51..5d03eee11f75 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -24,8 +24,7 @@ android:clipChildren="false" android:clipToPadding="true" android:orientation="vertical" - android:paddingStart="@*android:dimen/notification_content_margin_start" - android:background="@color/notification_guts_bg_color"> + android:paddingStart="@*android:dimen/notification_content_margin_start"> <!-- Package Info --> <RelativeLayout diff --git a/packages/SystemUI/res/layout/notification_snooze.xml b/packages/SystemUI/res/layout/notification_snooze.xml index ffe2eee80447..c350ed22b765 100644 --- a/packages/SystemUI/res/layout/notification_snooze.xml +++ b/packages/SystemUI/res/layout/notification_snooze.xml @@ -21,7 +21,7 @@ android:layout_height="wrap_content" android:orientation="vertical" android:clickable="true" - android:background="@color/notification_guts_bg_color" + android:background="@color/notification_material_background_color" android:theme="@style/Theme.SystemUI"> <RelativeLayout diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml index 4fae3c500a45..f8db97dbf800 100644 --- a/packages/SystemUI/res/layout/status_bar.xml +++ b/packages/SystemUI/res/layout/status_bar.xml @@ -24,7 +24,6 @@ android:layout_width="match_parent" android:layout_height="@dimen/status_bar_height" android:id="@+id/status_bar" - android:background="@drawable/system_bar_background" android:orientation="vertical" android:focusable="false" android:descendantFocusability="afterDescendants" diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml index 8fee2cf9597e..7142929bab64 100644 --- a/packages/SystemUI/res/layout/super_status_bar.xml +++ b/packages/SystemUI/res/layout/super_status_bar.xml @@ -28,5 +28,6 @@ <FrameLayout android:id="@+id/status_bar_container" android:layout_width="match_parent" - android:layout_height="wrap_content" /> + android:layout_height="wrap_content" + android:background="@drawable/system_bar_background" /> </com.android.systemui.statusbar.phone.StatusBarWindowView> diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index 9a66e8bc8791..9b0fe465deee 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -39,23 +39,16 @@ <!-- The color of the ripples on the untinted notifications --> <color name="notification_ripple_untinted_color">#30ffffff</color> - <!-- The "inside" of a notification, reached via longpress --> - <color name="notification_guts_bg_color">@color/GM2_grey_900</color> - <!-- The color of the text inside a notification --> <color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color> <color name="notification_guts_link_icon_tint">@color/GM2_grey_500</color> <color name="notification_guts_sub_text_color">@color/GM2_grey_300</color> <color name="notification_guts_header_text_color">@color/GM2_grey_200</color> - <color name="notification_guts_info_button_color">@color/GM2_blue_300</color> <color name="notification_guts_priority_button_content_color">@color/GM2_grey_500</color> - <color name="notification_guts_priority_button_content_color_selected">@color/GM2_blue_300</color> <color name="notification_guts_priority_button_bg_fill_color">@color/transparent</color> <color name="notification_guts_priority_button_bg_fill_color_selected">@color/GM2_grey_800</color> <color name="notification_guts_priority_button_bg_stroke_color">@color/GM2_grey_700</color> - <color name="notification_guts_priority_button_bg_stroke_color_selected">@color/GM2_blue_300</color> - <color name="notification_section_header_label_color">@color/GM2_grey_200</color> <color name="notification_section_clear_all_btn_color">@color/GM2_grey_500</color> <color name="notification_channel_dialog_separator">@color/GM2_grey_700</color> diff --git a/packages/SystemUI/res/values-sw600dp/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml index 23b64e34a939..b375364fdab9 100644 --- a/packages/SystemUI/res/values-sw600dp/styles.xml +++ b/packages/SystemUI/res/values-sw600dp/styles.xml @@ -22,4 +22,28 @@ <style name="UserDetailView"> <item name="numColumns">4</item> </style> + + <style name="TextAppearance.StatusBar.Expanded.UserSwitcher"> + <item name="android:textSize">@dimen/kg_user_switcher_text_size</item> + <item name="android:textStyle">normal</item> + <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> + <item name="android:textColor">?attr/wallpaperTextColor</item> + </style> + + <style name="TextAppearance.StatusBar.Expanded.UserSwitcher.Activated"> + <item name="android:fontWeight">700</item> + <item name="android:textStyle">bold</item> + <item name="android:textColor">?android:attr/textColorPrimaryInverse</item> + </style> + + <style name="TextAppearance.QS.UserSwitcher"> + <item name="android:textSize">@dimen/qs_detail_item_primary_text_size</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> + <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> + </style> + + <style name="TextAppearance.QS.UserSwitcher.Activated"> + <item name="android:fontWeight">700</item> + <item name="android:textStyle">bold</item> + </style> </resources> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 43ceb4ecaee8..73e49cee6a8b 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -88,21 +88,16 @@ <!-- The color of the text inside a notification --> <color name="notification_primary_text_color">@*android:color/notification_primary_text_color_light</color> - <!-- The "inside" of a notification, reached via longpress --> - <color name="notification_guts_bg_color">@color/GM2_grey_50</color> <color name="notification_guts_link_icon_tint">@color/GM2_grey_700</color> <color name="notification_guts_sub_text_color">@color/GM2_grey_700</color> <color name="notification_guts_header_text_color">@color/GM2_grey_900</color> <color name="notification_silence_color">#FF32c1de</color> <color name="notification_alert_color">#FFF87B2B</color> - <color name="notification_guts_info_button_color">@color/GM2_blue_700</color> <color name="notification_guts_priority_button_content_color">@color/GM2_grey_700</color> - <color name="notification_guts_priority_button_content_color_selected">@color/GM2_blue_700</color> <color name="notification_guts_priority_button_bg_fill_color">@color/transparent</color> <color name="notification_guts_priority_button_bg_fill_color_selected">#FFFFFF</color> <color name="notification_guts_priority_button_bg_stroke_color">@color/GM2_grey_300</color> - <color name="notification_guts_priority_button_bg_stroke_color_selected">@color/GM2_blue_600</color> <color name="notification_section_header_label_color">@color/GM2_grey_900</color> <color name="notification_section_clear_all_btn_color">@color/GM2_grey_700</color> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 8de2df592eda..8cb0c0225fd9 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -201,6 +201,13 @@ always-on display) --> <string name="doze_brightness_sensor_type" translatable="false"></string> + <!-- Override value to use for proximity sensor. --> + <string name="proximity_sensor_type" translatable="false"></string> + + <!-- If using proximity_sensor_type, specifies a threshold value to distinguish near and + far break points. A sensor value less than this is considered "near". --> + <item name="proximity_sensor_threshold" translatable="false" format="float" type="dimen"></item> + <!-- Doze: pulse parameter - how long does it take to fade in? --> <integer name="doze_pulse_duration_in">130</integer> @@ -464,6 +471,9 @@ <!-- Allow dragging the PIP to a location to close it --> <bool name="config_pipEnableDismissDragToEdge">true</bool> + <!-- Alow PIP to resize to a slightly bigger state upon touch/showing the menu --> + <bool name="config_pipEnableResizeForMenu">true</bool> + <!-- SystemUI Plugins that can be loaded on user builds. --> <string-array name="config_pluginWhitelist" translatable="false"> <item>com.android.systemui</item> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 1c2404f1921e..9caaa9f8565d 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -124,6 +124,9 @@ <!-- Increased height of a collapsed media notification in the status bar --> <dimen name="notification_min_height_media">160dp</dimen> + <!-- Increased height of a collapsed messaging notification in the status bar --> + <dimen name="notification_min_height_messaging">118dp</dimen> + <!-- Height of a small notification in the status bar which was used before android N --> <dimen name="notification_min_height_legacy">64dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 4aafec886a37..ff28b4d289e8 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2614,8 +2614,8 @@ <string name="controls_providers_subtitle">Choose an app from which to add controls</string> <!-- Number of favorites for controls management screen [CHAR LIMIT=NONE]--> <plurals name="controls_number_of_favorites"> - <item quantity="one"><xliff:g id="number" example="1">%s</xliff:g> current favorite.</item> - <item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> current favorites.</item> + <item quantity="one"><xliff:g id="number" example="1">%s</xliff:g> control added.</item> + <item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> controls added.</item> </plurals> <!-- Controls management controls screen default title [CHAR LIMIT=30] --> @@ -2626,6 +2626,10 @@ <string name="controls_favorite_header_favorites">Favorites</string> <!-- Controls management controls screen all header [CHAR LIMIT=50] --> <string name="controls_favorite_header_all">All</string> - <!-- Controls management controls screen error on load message [CHAR LIMIT=50] --> + <!-- Controls management controls screen error on load message [CHAR LIMIT=60] --> <string name="controls_favorite_load_error">The list of all controls could not be loaded.</string> + <!-- Controls management controls screen header for Other zone [CHAR LIMIT=60] --> + <string name="controls_favorite_other_zone_header">Other</string> + + </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 36c4526fb521..d9b1452b6bb1 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -514,7 +514,7 @@ <style name="TextAppearance.NotificationInfo.Button"> <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item> <item name="android:textSize">14sp</item> - <item name="android:textColor">@color/notification_guts_info_button_color</item> + <item name="android:textColor">?android:attr/colorAccent</item> <item name="android:background">@drawable/btn_borderless_rect</item> <item name="android:gravity">center_vertical</item> <item name="android:focusable">true</item> @@ -550,7 +550,7 @@ <style name="TextAppearance.NotificationImportanceButton"> <item name="android:textSize">@dimen/notification_importance_button_text</item> <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item> - <item name="android:textColor">@color/notification_guts_priority_contents</item> + <item name="android:textColor">?android:attr/colorAccent</item> <item name="android:gravity">center</item> </style> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl new file mode 100644 index 000000000000..97aa512ea7df --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl @@ -0,0 +1,28 @@ +/* + * 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.systemui.shared.recents; + +/** + * Listener interface that Launcher attaches to SystemUI to get + * pinned stack animation callbacks. + */ +oneway interface IPinnedStackAnimationListener { + /** + * Notifies the pinned stack animation is started. + */ + void onPinnedStackAnimationStarted(); +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index b1d39f59f789..80fd826f28c6 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -22,9 +22,11 @@ import android.graphics.Rect; import android.os.Bundle; import android.view.MotionEvent; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; + /** * Temporary callbacks into SystemUI. - * Next id = 22 + * Next id = 25 */ interface ISystemUiProxy { @@ -121,11 +123,21 @@ interface ISystemUiProxy { /** * Handle the provided image as if it was a screenshot. */ - void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen, + void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen, in Insets visibleInsets, int taskId) = 21; /** * Sets the split-screen divider minimized state */ void setSplitScreenMinimized(boolean minimized) = 22; + + /* + * Notifies that the swipe-to-home (recents animation) is finished. + */ + void notifySwipeToHomeFinished() = 23; + + /** + * Sets listener to get pinned stack animation callbacks. + */ + void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) = 24; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java index 4474a49e55b8..eca6ebf7f8e5 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java @@ -62,7 +62,9 @@ public class ThumbnailData { orientation = snapshot.getOrientation(); rotation = snapshot.getRotation(); reducedResolution = snapshot.isLowResolution(); - scale = snapshot.getScale(); + // TODO(b/149579527): Pass task size instead of computing scale. + // Assume width and height were scaled the same; compute scale only for width + scale = (float) thumbnail.getWidth() / snapshot.getTaskSize().x; isRealSnapshot = snapshot.isRealSnapshot(); isTranslucent = snapshot.isTranslucent(); windowingMode = snapshot.getWindowingMode(); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index a130092c77d1..49e3e5724988 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -370,8 +370,7 @@ public class ActivityManagerWrapper { Rect initialBounds) { try { return ActivityTaskManager.getService().setTaskWindowingModeSplitScreenPrimary(taskId, - createMode, true /* onTop */, false /* animate */, initialBounds, - true /* showRecents */); + true /* onTop */); } catch (RemoteException e) { return false; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java index 8c0ffb82f0ed..360244c9af52 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java @@ -53,11 +53,9 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { } @Override - public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment) { + public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) { for (PinnedStackListener listener : mListeners) { - listener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment, - fromShelfAdjustment); + listener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment); } } @@ -69,13 +67,6 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { } @Override - public void onMinimizedStateChanged(boolean isMinimized) { - for (PinnedStackListener listener : mListeners) { - listener.onMinimizedStateChanged(isMinimized); - } - } - - @Override public void onActionsChanged(ParceledListSlice actions) { for (PinnedStackListener listener : mListeners) { listener.onActionsChanged(actions); @@ -117,13 +108,6 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { } } - @Override - public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) { - for (PinnedStackListener listener : mListeners) { - listener.onPrepareAnimation(sourceRectHint, aspectRatio, bounds); - } - } - /** * A counterpart of {@link IPinnedStackListener} with empty implementations. * Subclasses can ignore those methods they do not intend to take action upon. @@ -131,13 +115,10 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { public static class PinnedStackListener { public void onListenerRegistered(IPinnedStackController controller) {} - public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment) {} + public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) {} public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {} - public void onMinimizedStateChanged(boolean isMinimized) {} - public void onActionsChanged(ParceledListSlice actions) {} public void onSaveReentryBounds(ComponentName componentName, Rect bounds) {} @@ -149,7 +130,5 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { public void onConfigurationChanged() {} public void onAspectRatioChanged(float aspectRatio) {} - - public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {} } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java index 748f356c25b9..bbb83c73446c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -61,14 +61,6 @@ public class RecentsAnimationControllerCompat { } } - public void setSplitScreenMinimized(boolean minimized) { - try { - mAnimationController.setSplitScreenMinimized(minimized); - } catch (RemoteException e) { - Log.e(TAG, "Failed to set minimize dock", e); - } - } - public void hideCurrentInputMethod() { try { mAnimationController.hideCurrentInputMethod(); @@ -91,15 +83,6 @@ public class RecentsAnimationControllerCompat { } } - @Deprecated - public void setCancelWithDeferredScreenshot(boolean screenshot) { - try { - mAnimationController.setCancelWithDeferredScreenshot(screenshot); - } catch (RemoteException e) { - Log.e(TAG, "Failed to set cancel with deferred screenshot", e); - } - } - public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { try { mAnimationController.setDeferCancelUntilNextTransition(defer, screenshot); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java index 5f92b2811807..1c6223b847de 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java @@ -20,8 +20,6 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.ITaskStackListener; import android.content.ComponentName; import android.os.IBinder; -import android.os.UserHandle; -import android.util.Log; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -40,8 +38,6 @@ public abstract class TaskStackChangeListener { public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { } public void onActivityUnpinned() { } public void onPinnedActivityRestartAttempt(boolean clearedTask) { } - public void onPinnedStackAnimationStarted() { } - public void onPinnedStackAnimationEnded() { } public void onActivityForcedResizable(String packageName, int taskId, int reason) { } public void onActivityDismissingDockedStack() { } public void onActivityLaunchOnSecondaryDisplayFailed() { } @@ -117,22 +113,4 @@ public abstract class TaskStackChangeListener { /** @see ITaskStackListener#onRecentTaskListFrozenChanged(boolean) */ public void onRecentTaskListFrozenChanged(boolean frozen) { } - - /** - * Checks that the current user matches the process. Since - * {@link android.app.ITaskStackListener} is not multi-user aware, handlers of - * {@link TaskStackChangeListener} should make this call to verify that we don't act on events - * originating from another user's interactions. - */ - protected final boolean checkCurrentUserId(int currentUserId, boolean debug) { - int processUserId = UserHandle.myUserId(); - if (processUserId != currentUserId) { - if (debug) { - Log.d("TaskStackChangeListener", "UID mismatch. Process is uid=" + processUserId - + " and the current user is uid=" + currentUserId); - } - return false; - } - return true; - } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java index acce41cc942e..cbdd3f8191f4 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java @@ -128,18 +128,6 @@ public class TaskStackChangeListeners extends TaskStackListener { } @Override - public void onPinnedStackAnimationStarted() throws RemoteException { - mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_STARTED); - mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_STARTED); - } - - @Override - public void onPinnedStackAnimationEnded() throws RemoteException { - mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_ENDED); - mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_ENDED); - } - - @Override public void onActivityForcedResizable(String packageName, int taskId, int reason) throws RemoteException { mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName) @@ -249,11 +237,9 @@ public class TaskStackChangeListeners extends TaskStackListener { private static final int ON_TASK_SNAPSHOT_CHANGED = 2; private static final int ON_ACTIVITY_PINNED = 3; private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 4; - private static final int ON_PINNED_STACK_ANIMATION_ENDED = 5; private static final int ON_ACTIVITY_FORCED_RESIZABLE = 6; private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 7; private static final int ON_TASK_PROFILE_LOCKED = 8; - private static final int ON_PINNED_STACK_ANIMATION_STARTED = 9; private static final int ON_ACTIVITY_UNPINNED = 10; private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED = 11; private static final int ON_TASK_CREATED = 12; @@ -317,18 +303,6 @@ public class TaskStackChangeListeners extends TaskStackListener { } break; } - case ON_PINNED_STACK_ANIMATION_STARTED: { - for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onPinnedStackAnimationStarted(); - } - break; - } - case ON_PINNED_STACK_ANIMATION_ENDED: { - for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onPinnedStackAnimationEnded(); - } - break; - } case ON_ACTIVITY_FORCED_RESIZABLE: { for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { mTaskStackListeners.get(i).onActivityForcedResizable( diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java index e28b1e2806e9..d64bf77ad9d5 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java @@ -191,18 +191,6 @@ public class WindowManagerWrapper { } /** - * Registers a docked stack listener with the system. - */ - public void registerDockedStackListener(DockedStackListenerCompat listener) { - try { - WindowManagerGlobal.getWindowManagerService().registerDockedStackListener( - listener.mListener); - } catch (RemoteException e) { - Log.w(TAG, "Failed to register docked stack listener"); - } - } - - /** * Adds a pinned stack listener, which will receive updates from the window manager service * along with any other pinned stack listeners that were added via this method. */ diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index 4473b010cbda..dbcdead8de9f 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -20,7 +20,9 @@ import android.app.ActivityManager; import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; +import android.os.Handler; import android.os.HandlerThread; +import android.os.SystemClock; import android.os.Trace; import android.service.wallpaper.WallpaperService; import android.util.Log; @@ -94,18 +96,28 @@ public class ImageWallpaper extends WallpaperService { private EglHelper mEglHelper; private StatusBarStateController mController; private final Runnable mFinishRenderingTask = this::finishRendering; - private final boolean mNeedTransition; private boolean mShouldStopTransition; - @VisibleForTesting - final boolean mIsHighEndGfx; - private final boolean mDisplayNeedsBlanking; private final DisplayInfo mDisplayInfo = new DisplayInfo(); private final Object mMonitor = new Object(); + @VisibleForTesting + boolean mIsHighEndGfx; + private boolean mDisplayNeedsBlanking; + private boolean mNeedTransition; private boolean mNeedRedraw; // This variable can only be accessed in synchronized block. private boolean mWaitingForRendering; GLEngine(Context context, DozeParameters dozeParameters) { + init(dozeParameters); + } + + @VisibleForTesting + GLEngine(DozeParameters dozeParameters, Handler handler) { + super(SystemClock::elapsedRealtime, handler); + init(dozeParameters); + } + + private void init(DozeParameters dozeParameters) { mIsHighEndGfx = ActivityManager.isHighEndGfx(); mDisplayNeedsBlanking = dozeParameters.getDisplayNeedsBlanking(); mNeedTransition = mIsHighEndGfx && !mDisplayNeedsBlanking; diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java index 10ae3434daaa..f0a82c519d48 100644 --- a/packages/SystemUI/src/com/android/systemui/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/Prefs.java @@ -164,7 +164,7 @@ public final class Prefs { get(context).unregisterOnSharedPreferenceChangeListener(listener); } - private static SharedPreferences get(Context context) { + public static SharedPreferences get(Context context) { return context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE); } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index 0ac1c1215a28..79b691bb3e37 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -39,6 +39,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.FalsingPlugin; import com.android.systemui.plugins.PluginListener; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.sensors.ProximitySensor; @@ -69,6 +70,7 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable { private final DockManager mDockManager; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private Executor mUiBgExecutor; + private final StatusBarStateController mStatusBarStateController; @Inject FalsingManagerProxy(Context context, PluginManager pluginManager, @Main Executor executor, @@ -76,12 +78,14 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable { DeviceConfigProxy deviceConfig, DockManager dockManager, KeyguardUpdateMonitor keyguardUpdateMonitor, DumpManager dumpManager, - @UiBackground Executor uiBgExecutor) { + @UiBackground Executor uiBgExecutor, + StatusBarStateController statusBarStateController) { mDisplayMetrics = displayMetrics; mProximitySensor = proximitySensor; mDockManager = dockManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mUiBgExecutor = uiBgExecutor; + mStatusBarStateController = statusBarStateController; mProximitySensor.setTag(PROXIMITY_SENSOR_TAG); mProximitySensor.setSensorDelay(SensorManager.SENSOR_DELAY_GAME); mDeviceConfig = deviceConfig; @@ -143,7 +147,8 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable { mKeyguardUpdateMonitor, mProximitySensor, mDeviceConfig, - mDockManager + mDockManager, + mStatusBarStateController ); } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java index a084ae6ed50f..ec81b9f5a291 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java @@ -32,6 +32,8 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.classifier.Classifier; import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.sensors.ProximitySensor; @@ -59,6 +61,7 @@ public class BrightLineFalsingManager implements FalsingManager { private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final ProximitySensor mProximitySensor; private final DockManager mDockManager; + private final StatusBarStateController mStatusBarStateController; private boolean mSessionStarted; private MetricsLogger mMetricsLogger; private int mIsFalseTouchCalls; @@ -88,15 +91,29 @@ public class BrightLineFalsingManager implements FalsingManager { }; private boolean mPreviousResult = false; + private StatusBarStateController.StateListener mStatusBarStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + logDebug("StatusBarState=" + StatusBarState.toShortString(newState)); + mState = newState; + updateSessionActive(); + } + }; + private int mState; + public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider, KeyguardUpdateMonitor keyguardUpdateMonitor, ProximitySensor proximitySensor, DeviceConfigProxy deviceConfigProxy, - DockManager dockManager) { + DockManager dockManager, StatusBarStateController statusBarStateController) { mKeyguardUpdateMonitor = keyguardUpdateMonitor; mDataProvider = falsingDataProvider; mProximitySensor = proximitySensor; mDockManager = dockManager; + mStatusBarStateController = statusBarStateController; mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback); + mStatusBarStateController.addCallback(mStatusBarStateListener); + mState = mStatusBarStateController.getState(); mMetricsLogger = new MetricsLogger(); mClassifiers = new ArrayList<>(); @@ -116,13 +133,12 @@ public class BrightLineFalsingManager implements FalsingManager { mProximitySensor.register(mSensorEventListener); } - private void unregisterSensors() { mProximitySensor.unregister(mSensorEventListener); } private void sessionStart() { - if (!mSessionStarted && !mShowingAod && mScreenOn) { + if (!mSessionStarted && shouldSessionBeActive()) { logDebug("Starting Session"); mSessionStarted = true; mJustUnlockedWithFace = false; @@ -145,6 +161,19 @@ public class BrightLineFalsingManager implements FalsingManager { } } + + private void updateSessionActive() { + if (shouldSessionBeActive()) { + sessionStart(); + } else { + sessionEnd(); + } + } + + private boolean shouldSessionBeActive() { + return mScreenOn && (mState == StatusBarState.KEYGUARD) && !mShowingAod; + } + private void updateInteractionType(@Classifier.InteractionType int type) { logDebug("InteractionType: " + type); mDataProvider.setInteractionType(type); @@ -232,11 +261,7 @@ public class BrightLineFalsingManager implements FalsingManager { @Override public void setShowingAod(boolean showingAod) { mShowingAod = showingAod; - if (showingAod) { - sessionEnd(); - } else { - sessionStart(); - } + updateSessionActive(); } @Override @@ -343,13 +368,13 @@ public class BrightLineFalsingManager implements FalsingManager { @Override public void onScreenTurningOn() { mScreenOn = true; - sessionStart(); + updateSessionActive(); } @Override public void onScreenOff() { mScreenOn = false; - sessionEnd(); + updateSessionActive(); } @@ -421,6 +446,7 @@ public class BrightLineFalsingManager implements FalsingManager { public void cleanup() { unregisterSensors(); mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback); + mStatusBarStateController.removeCallback(mStatusBarStateListener); } static void logDebug(String msg) { diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt index 6ff1bbce672f..a67f6bd88ad8 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt @@ -16,6 +16,7 @@ package com.android.systemui.controls.controller +import android.app.ActivityManager import android.content.ComponentName import android.content.Context import android.os.IBinder @@ -50,7 +51,7 @@ open class ControlsBindingControllerImpl @Inject constructor( private val refreshing = AtomicBoolean(false) - private var currentUser = context.user + private var currentUser = UserHandle.of(ActivityManager.getCurrentUser()) override val currentUserId: Int get() = currentUser.identifier diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index a2a08502a58f..3b06ebef9443 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -16,6 +16,7 @@ package com.android.systemui.controls.controller +import android.app.ActivityManager import android.app.PendingIntent import android.content.BroadcastReceiver import android.content.ComponentName @@ -67,6 +68,7 @@ class ControlsControllerImpl @Inject constructor ( internal const val CONTROLS_AVAILABLE = "systemui.controls_available" internal val URI = Settings.Secure.getUriFor(CONTROLS_AVAILABLE) private const val USER_CHANGE_RETRY_DELAY = 500L // ms + private const val DEFAULT_ENABLED = 1 } // Map of map: ComponentName -> (String -> ControlInfo). @@ -77,15 +79,16 @@ class ControlsControllerImpl @Inject constructor ( private var userChanging: Boolean = true + private var currentUser = UserHandle.of(ActivityManager.getCurrentUser()) + override val currentUserId + get() = currentUser.identifier + private val contentResolver: ContentResolver get() = context.contentResolver - override var available = Settings.Secure.getInt(contentResolver, CONTROLS_AVAILABLE, 0) != 0 + override var available = Settings.Secure.getIntForUser( + contentResolver, CONTROLS_AVAILABLE, DEFAULT_ENABLED, currentUserId) != 0 private set - private var currentUser = context.user - override val currentUserId - get() = currentUser.identifier - private val persistenceWrapper = optionalWrapper.orElseGet { ControlsFavoritePersistenceWrapper( Environment.buildPath( @@ -104,7 +107,7 @@ class ControlsControllerImpl @Inject constructor ( userContext.filesDir, ControlsFavoritePersistenceWrapper.FILE_NAME) persistenceWrapper.changeFile(fileName) available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE, - /* default */ 0, newUser.identifier) != 0 + DEFAULT_ENABLED, newUser.identifier) != 0 synchronized(currentFavorites) { currentFavorites.clear() } @@ -140,7 +143,7 @@ class ControlsControllerImpl @Inject constructor ( return } available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE, - /* default */ 0, currentUserId) != 0 + DEFAULT_ENABLED, currentUserId) != 0 synchronized(currentFavorites) { currentFavorites.clear() } diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt new file mode 100644 index 000000000000..c05351795aed --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt @@ -0,0 +1,114 @@ +/* + * 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.controls.management + +import android.text.TextUtils +import android.util.ArrayMap +import com.android.systemui.controls.ControlStatus +import com.android.systemui.controls.controller.ControlInfo + +/** + * This model is used to show all controls separated by zones. + * + * The model will sort the controls and zones in the following manner: + * * The zones will be sorted in a first seen basis + * * The controls in each zone will be sorted in a first seen basis. + * + * @property controls List of all controls as returned by loading + * @property initialFavoriteIds sorted ids of favorite controls + * @property noZoneString text to use as header for all controls that have blank or `null` zone. + */ +class AllModel( + private val controls: List<ControlStatus>, + initialFavoriteIds: List<String>, + private val emptyZoneString: CharSequence +) : ControlsModel { + + override val favorites: List<ControlInfo.Builder> + get() = favoriteIds.mapNotNull { id -> + val control = controls.firstOrNull { it.control.controlId == id }?.control + control?.let { + ControlInfo.Builder().apply { + controlId = it.controlId + controlTitle = it.title + deviceType = it.deviceType + } + } + } + + private val favoriteIds = initialFavoriteIds.toMutableList() + + override val elements: List<ElementWrapper> = createWrappers(controls) + + override fun changeFavoriteStatus(controlId: String, favorite: Boolean) { + if (favorite) { + favoriteIds.add(controlId) + } else { + favoriteIds.remove(controlId) + } + } + + private fun createWrappers(list: List<ControlStatus>): List<ElementWrapper> { + val map = list.groupByTo(OrderedMap(ArrayMap<CharSequence, MutableList<ControlStatus>>())) { + it.control.zone ?: "" + } + val output = mutableListOf<ElementWrapper>() + var emptyZoneValues: Sequence<ControlWrapper>? = null + for (zoneName in map.orderedKeys) { + val values = map.getValue(zoneName).asSequence().map { ControlWrapper(it) } + if (TextUtils.isEmpty(zoneName)) { + emptyZoneValues = values + } else { + output.add(ZoneNameWrapper(zoneName)) + output.addAll(values) + } + } + // Add controls with empty zone at the end + if (emptyZoneValues != null) { + if (map.size != 1) { + output.add(ZoneNameWrapper(emptyZoneString)) + } + output.addAll(emptyZoneValues) + } + return output + } + + private class OrderedMap<K, V>(private val map: MutableMap<K, V>) : MutableMap<K, V> by map { + + val orderedKeys = mutableListOf<K>() + + override fun put(key: K, value: V): V? { + if (key !in map) { + orderedKeys.add(key) + } + return map.put(key, value) + } + + override fun clear() { + orderedKeys.clear() + map.clear() + } + + override fun remove(key: K): V? { + val removed = map.remove(key) + if (removed != null) { + orderedKeys.remove(key) + } + return removed + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt index ac5e0893b526..25ebc65357ee 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt @@ -116,6 +116,10 @@ class FavoritesRenderer( fun renderFavoritesForComponent(component: ComponentName): String { val qty = favoriteFunction(component) - return resources.getQuantityString(R.plurals.controls_number_of_favorites, qty, qty) + if (qty != 0) { + return resources.getQuantityString(R.plurals.controls_number_of_favorites, qty, qty) + } else { + return "" + } } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt index d3cabe67790e..0870a4d179c9 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt @@ -42,8 +42,7 @@ private typealias ModelFavoriteChanger = (String, Boolean) -> Unit * @param onlyFavorites set to true to only display favorites instead of all controls */ class ControlAdapter( - private val layoutInflater: LayoutInflater, - private val onlyFavorites: Boolean = false + private val layoutInflater: LayoutInflater ) : RecyclerView.Adapter<Holder>() { companion object { @@ -57,22 +56,21 @@ class ControlAdapter( } } - var modelList: List<ElementWrapper> = emptyList() - private var favoritesModel: FavoriteModel? = null + private var model: ControlsModel? = null override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { return when (viewType) { TYPE_CONTROL -> { ControlHolder( - layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply { - layoutParams.apply { - width = ViewGroup.LayoutParams.MATCH_PARENT - } - elevation = 15f - }, - { id, favorite -> - favoritesModel?.changeFavoriteStatus(id, favorite) - }) + layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply { + layoutParams.apply { + width = ViewGroup.LayoutParams.MATCH_PARENT + } + elevation = 15f + } + ) { id, favorite -> + model?.changeFavoriteStatus(id, favorite) + } } TYPE_ZONE -> { ZoneHolder(layoutInflater.inflate(R.layout.controls_zone_header, parent, false)) @@ -81,27 +79,26 @@ class ControlAdapter( } } - fun changeFavoritesModel(favoritesModel: FavoriteModel) { - this.favoritesModel = favoritesModel - if (onlyFavorites) { - modelList = favoritesModel.favorites - } else { - modelList = favoritesModel.all - } + fun changeModel(model: ControlsModel) { + this.model = model notifyDataSetChanged() } - override fun getItemCount() = modelList.size + override fun getItemCount() = model?.elements?.size ?: 0 override fun onBindViewHolder(holder: Holder, index: Int) { - holder.bindData(modelList[index]) + model?.let { + holder.bindData(it.elements[index]) + } } override fun getItemViewType(position: Int): Int { - return when (modelList[position]) { - is ZoneNameWrapper -> TYPE_ZONE - is ControlWrapper -> TYPE_CONTROL - } + model?.let { + return when (it.elements.get(position)) { + is ZoneNameWrapper -> TYPE_ZONE + is ControlWrapper -> TYPE_CONTROL + } + } ?: throw IllegalStateException("Getting item type for null model") } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt index af4a977022ae..2c014498fdc2 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt @@ -26,11 +26,9 @@ import android.view.ViewStub import android.widget.Button import android.widget.TextView import androidx.recyclerview.widget.GridLayoutManager -import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import com.android.systemui.R import com.android.systemui.broadcast.BroadcastDispatcher -import com.android.systemui.controls.controller.ControlInfo import com.android.systemui.controls.controller.ControlsControllerImpl import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.settings.CurrentUserTracker @@ -51,33 +49,10 @@ class ControlsFavoritingActivity @Inject constructor( private lateinit var recyclerViewAll: RecyclerView private lateinit var adapterAll: ControlAdapter - private lateinit var recyclerViewFavorites: RecyclerView - private lateinit var adapterFavorites: ControlAdapter - private lateinit var errorText: TextView + private lateinit var statusText: TextView + private var model: ControlsModel? = null private var component: ComponentName? = null - private var currentModel: FavoriteModel? = null - private var itemTouchHelperCallback = object : ItemTouchHelper.SimpleCallback( - /* dragDirs */ ItemTouchHelper.UP - or ItemTouchHelper.DOWN - or ItemTouchHelper.LEFT - or ItemTouchHelper.RIGHT, - /* swipeDirs */0 - ) { - override fun onMove( - recyclerView: RecyclerView, - viewHolder: RecyclerView.ViewHolder, - target: RecyclerView.ViewHolder - ): Boolean { - return currentModel?.onMoveItem( - viewHolder.layoutPosition, target.layoutPosition) != null - } - - override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {} - - override fun isItemViewSwipeEnabled() = false - } - private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) { private val startingUser = controller.currentUserId @@ -89,6 +64,10 @@ class ControlsFavoritingActivity @Inject constructor( } } + override fun onBackPressed() { + finish() + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.controls_management) @@ -99,21 +78,27 @@ class ControlsFavoritingActivity @Inject constructor( val app = intent.getCharSequenceExtra(EXTRA_APP) component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME) - errorText = requireViewById(R.id.error_message) + statusText = requireViewById(R.id.status_message) - setUpRecyclerViews() + setUpRecyclerView() requireViewById<TextView>(R.id.title).text = app?.let { it } ?: resources.getText(R.string.controls_favorite_default_title) requireViewById<TextView>(R.id.subtitle).text = resources.getText(R.string.controls_favorite_subtitle) + requireViewById<Button>(R.id.other_apps).apply { + visibility = View.VISIBLE + setOnClickListener { + this@ControlsFavoritingActivity.onBackPressed() + } + } + requireViewById<Button>(R.id.done).setOnClickListener { if (component == null) return@setOnClickListener - val favoritesForStorage = currentModel?.favorites?.map { - with(it.controlStatus.control) { - ControlInfo(component!!, controlId, title, deviceType) - } + val favoritesForStorage = model?.favorites?.map { + it.componentName = component!! + it.build() } if (favoritesForStorage != null) { controller.replaceFavoritesForComponent(component!!, favoritesForStorage) @@ -122,20 +107,22 @@ class ControlsFavoritingActivity @Inject constructor( } component?.let { + statusText.text = resources.getText(com.android.internal.R.string.loading) controller.loadForComponent(it, Consumer { data -> val allControls = data.allControls val favoriteKeys = data.favoritesIds val error = data.errorOnLoad executor.execute { - val favoriteModel = FavoriteModel( - allControls, - favoriteKeys, - allAdapter = adapterAll, - favoritesAdapter = adapterFavorites) - adapterAll.changeFavoritesModel(favoriteModel) - adapterFavorites.changeFavoritesModel(favoriteModel) - currentModel = favoriteModel - errorText.visibility = if (error) View.VISIBLE else View.GONE + val emptyZoneString = resources.getText( + R.string.controls_favorite_other_zone_header) + val model = AllModel(allControls, favoriteKeys, emptyZoneString) + adapterAll.changeModel(model) + this.model = model + if (error) { + statusText.text = resources.getText(R.string.controls_favorite_load_error) + } else { + statusText.visibility = View.GONE + } } }) } @@ -143,7 +130,7 @@ class ControlsFavoritingActivity @Inject constructor( currentUserTracker.startTracking() } - private fun setUpRecyclerViews() { + private fun setUpRecyclerView() { val margin = resources.getDimensionPixelSize(R.dimen.controls_card_margin) val itemDecorator = MarginItemDecorator(margin, margin) val layoutInflater = LayoutInflater.from(applicationContext) @@ -156,14 +143,6 @@ class ControlsFavoritingActivity @Inject constructor( } addItemDecoration(itemDecorator) } - - adapterFavorites = ControlAdapter(layoutInflater, true) - recyclerViewFavorites = requireViewById<RecyclerView>(R.id.listFavorites).apply { - layoutManager = GridLayoutManager(applicationContext, 2) - adapter = adapterFavorites - addItemDecoration(itemDecorator) - } - ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerViewFavorites) } override fun onDestroy() { diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt index 882382cc4ade..53f301939435 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt @@ -16,6 +16,7 @@ package com.android.systemui.controls.management +import android.app.ActivityManager import android.content.ComponentName import android.content.Context import android.content.pm.ServiceInfo @@ -72,7 +73,7 @@ class ControlsListingControllerImpl @VisibleForTesting constructor( private var availableServices = emptyList<ServiceInfo>() - override var currentUserId = context.userId + override var currentUserId = ActivityManager.getCurrentUser() private set private val serviceListingCallback = ServiceListing.Callback { diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt new file mode 100644 index 000000000000..a995a2ebfd25 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt @@ -0,0 +1,59 @@ +/* + * 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.controls.management + +import com.android.systemui.controls.ControlStatus +import com.android.systemui.controls.controller.ControlInfo + +/** + * Model for using with [ControlAdapter]. + * + * Implementations of this interface provide different views of the controls to show. + */ +interface ControlsModel { + + /** + * List of favorites (builders) in order. + * + * This should be obtained prior to storing the favorites using + * [ControlsController.replaceFavoritesForComponent]. + */ + val favorites: List<ControlInfo.Builder> + + /** + * List of all the elements to display by the corresponding [RecyclerView]. + */ + val elements: List<ElementWrapper> + + /** + * Change the favorite status of a particular control. + */ + fun changeFavoriteStatus(controlId: String, favorite: Boolean) {} + + /** + * Move an item (in elements) from one position to another. + */ + fun onMoveItem(from: Int, to: Int) {} +} + +/** + * Wrapper classes for the different types of elements shown in the [RecyclerView]s in + * [ControlAdapter]. + */ +sealed class ElementWrapper +data class ZoneNameWrapper(val zoneName: CharSequence) : ElementWrapper() +data class ControlWrapper(val controlStatus: ControlStatus) : ElementWrapper()
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt index ad4bdefdff3e..098caf61a873 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt @@ -21,6 +21,7 @@ import android.content.Intent import android.os.Bundle import android.view.LayoutInflater import android.view.ViewStub +import android.widget.Button import android.widget.TextView import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -86,6 +87,10 @@ class ControlsProviderSelectorActivity @Inject constructor( requireViewById<TextView>(R.id.subtitle).text = resources.getText(R.string.controls_providers_subtitle) + requireViewById<Button>(R.id.done).setOnClickListener { + this@ControlsProviderSelectorActivity.finishAffinity() + } + currentUserTracker.startTracking() } diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt index 6bade0aeb998..5c51e3dbe4ac 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt @@ -142,12 +142,4 @@ class CharSequenceComparator : Comparator<CharSequence> { else if (p0 != null && p1 == null) return 1 return p0.toString().compareTo(p1.toString()) } -} - -/** - * Wrapper classes for the different types of elements shown in the [RecyclerView]s in - * [ControlsFavoritingActivity]. - */ -sealed class ElementWrapper -data class ZoneNameWrapper(val zoneName: CharSequence) : ElementWrapper() -data class ControlWrapper(val controlStatus: ControlStatus) : ElementWrapper()
\ No newline at end of file +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 7c0033c1d4ec..024378d56edd 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -20,6 +20,7 @@ import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME; import android.app.INotificationManager; import android.content.Context; +import android.content.SharedPreferences; import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.display.NightDisplayListener; import android.os.Handler; @@ -34,6 +35,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.util.NotificationMessagingUtil; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.ViewMediatorCallback; +import com.android.systemui.Prefs; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.AlwaysOnDisplayPolicy; @@ -79,6 +81,13 @@ public class DependencyProvider { /** */ @Provides + @Main + public SharedPreferences provideSharePreferences(Context context) { + return Prefs.get(context); + } + + /** */ + @Provides public AmbientDisplayConfiguration provideAmbientDispalyConfiguration(Context context) { return new AmbientDisplayConfiguration(context); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java index 3aa14a31a5d9..352ee3304964 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java @@ -209,6 +209,7 @@ public class SystemServicesModule { @Provides @Singleton + @Nullable static TelecomManager provideTelecomManager(Context context) { return context.getSystemService(TelecomManager.class); } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 9203e6df61e3..24f505d5a395 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -25,7 +25,6 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.Dialog; import android.app.IActivityManager; -import android.app.KeyguardManager; import android.app.PendingIntent; import android.app.StatusBarManager; import android.app.WallpaperManager; @@ -93,6 +92,7 @@ import com.android.systemui.MultiListLayout; import com.android.systemui.MultiListLayout.MultiListAdapter; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.controls.management.ControlsListingController; import com.android.systemui.controls.ui.ControlsUiController; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -152,7 +152,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final IDreamManager mDreamManager; private final DevicePolicyManager mDevicePolicyManager; private final LockPatternUtils mLockPatternUtils; - private final KeyguardManager mKeyguardManager; + private final KeyguardStateController mKeyguardStateController; private final BroadcastDispatcher mBroadcastDispatcher; private final ContentResolver mContentResolver; private final Resources mResources; @@ -190,6 +190,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private ControlsUiController mControlsUiController; private final IWindowManager mIWindowManager; private final Executor mBackgroundExecutor; + private final ControlsListingController mControlsListingController; + private boolean mAnyControlsProviders = false; /** * @param context everything needs a context :( @@ -198,25 +200,26 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, public GlobalActionsDialog(Context context, GlobalActionsManager windowManagerFuncs, AudioManager audioManager, IDreamManager iDreamManager, DevicePolicyManager devicePolicyManager, LockPatternUtils lockPatternUtils, - KeyguardManager keyguardManager, BroadcastDispatcher broadcastDispatcher, + BroadcastDispatcher broadcastDispatcher, ConnectivityManager connectivityManager, TelephonyManager telephonyManager, ContentResolver contentResolver, @Nullable Vibrator vibrator, @Main Resources resources, ConfigurationController configurationController, ActivityStarter activityStarter, KeyguardStateController keyguardStateController, UserManager userManager, TrustManager trustManager, IActivityManager iActivityManager, - TelecomManager telecomManager, MetricsLogger metricsLogger, + @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger, BlurUtils blurUtils, SysuiColorExtractor colorExtractor, IStatusBarService statusBarService, NotificationShadeWindowController notificationShadeWindowController, ControlsUiController controlsUiController, IWindowManager iWindowManager, - @Background Executor backgroundExecutor) { + @Background Executor backgroundExecutor, + ControlsListingController controlsListingController) { mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme); mWindowManagerFuncs = windowManagerFuncs; mAudioManager = audioManager; mDreamManager = iDreamManager; mDevicePolicyManager = devicePolicyManager; mLockPatternUtils = lockPatternUtils; - mKeyguardManager = keyguardManager; + mKeyguardStateController = keyguardStateController; mBroadcastDispatcher = broadcastDispatcher; mContentResolver = contentResolver; mResources = resources; @@ -233,6 +236,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mControlsUiController = controlsUiController; mIWindowManager = iWindowManager; mBackgroundExecutor = backgroundExecutor; + mControlsListingController = controlsListingController; // receive broadcasts IntentFilter filter = new IntentFilter(); @@ -269,6 +273,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } } }); + + mControlsListingController.addCallback(list -> mAnyControlsProviders = !list.isEmpty()); } /** @@ -427,7 +433,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, .startPendingIntentDismissingKeyguard(intent); } }, - mKeyguardManager.isDeviceLocked()) + !mKeyguardStateController.isUnlocked()) : null; ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController, @@ -444,12 +450,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } private boolean shouldDisplayLockdown() { - int userId = getCurrentUser().id; // Lockdown is meaningless without a place to go. - if (!mKeyguardManager.isDeviceSecure(userId)) { + if (!mKeyguardStateController.isMethodSecure()) { return false; } + int userId = getCurrentUser().id; // Only show the lockdown button if the device isn't locked down (for whatever reason). int state = mLockPatternUtils.getStrongAuthForUser(userId); return (state == STRONG_AUTH_NOT_REQUIRED @@ -562,13 +568,16 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, @Override public void onPress() { mMetricsLogger.action(MetricsEvent.ACTION_EMERGENCY_DIALER_FROM_POWER_MENU); - Intent intent = mTelecomManager.createLaunchEmergencyDialerIntent(null /* number */); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS - | Intent.FLAG_ACTIVITY_CLEAR_TOP); - intent.putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE, - EmergencyDialerConstants.ENTRY_TYPE_POWER_MENU); - mContext.startActivityAsUser(intent, UserHandle.CURRENT); + if (mTelecomManager != null) { + Intent intent = mTelecomManager.createLaunchEmergencyDialerIntent( + null /* number */); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + | Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE, + EmergencyDialerConstants.ENTRY_TYPE_POWER_MENU); + mContext.startActivityAsUser(intent, UserHandle.CURRENT); + } } } @@ -1914,7 +1923,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } private boolean shouldShowControls() { - return !mKeyguardManager.isDeviceLocked() - && mControlsUiController.getAvailable(); + return mKeyguardStateController.isUnlocked() + && mControlsUiController.getAvailable() + && mAnyControlsProviders; } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java index adee7f23e709..38744fe1b670 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java @@ -18,6 +18,8 @@ package com.android.systemui.pip; import android.content.res.Configuration; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; + import java.io.PrintWriter; @@ -27,5 +29,7 @@ public interface BasePipManager { default void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {} void onConfigurationChanged(Configuration newConfig); default void setShelfHeight(boolean visible, int height) {} + default void setPinnedStackAnimationType(int animationType) {} + default void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {} default void dump(PrintWriter pw) {} } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java new file mode 100644 index 000000000000..b5fd406de368 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java @@ -0,0 +1,317 @@ +/* + * 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.systemui.pip; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.annotation.IntDef; +import android.annotation.MainThread; +import android.content.Context; +import android.graphics.Rect; +import android.os.RemoteException; +import android.view.IWindowContainer; +import android.view.SurfaceControl; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; + +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Controller class of PiP animations (both from and to PiP mode). + */ +public class PipAnimationController { + private static final float FRACTION_START = 0f; + private static final float FRACTION_END = 1f; + + public static final int DURATION_NONE = 0; + public static final int DURATION_DEFAULT_MS = 425; + public static final int ANIM_TYPE_BOUNDS = 0; + public static final int ANIM_TYPE_ALPHA = 1; + + @IntDef(prefix = { "ANIM_TYPE_" }, value = { + ANIM_TYPE_BOUNDS, + ANIM_TYPE_ALPHA + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AnimationType {} + + private final Interpolator mFastOutSlowInInterpolator; + + private PipTransitionAnimator mCurrentAnimator; + + PipAnimationController(Context context) { + mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.fast_out_slow_in); + } + + @MainThread + PipTransitionAnimator getAnimator(IWindowContainer wc, boolean scheduleFinishPip, + Rect destinationBounds, float alphaStart, float alphaEnd) { + if (mCurrentAnimator == null) { + mCurrentAnimator = setupPipTransitionAnimator( + PipTransitionAnimator.ofAlpha(wc, scheduleFinishPip, + destinationBounds, alphaStart, alphaEnd)); + } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA + && mCurrentAnimator.isRunning()) { + mCurrentAnimator.updateEndValue(alphaEnd); + } else { + mCurrentAnimator.cancel(); + mCurrentAnimator = setupPipTransitionAnimator( + PipTransitionAnimator.ofAlpha(wc, scheduleFinishPip, + destinationBounds, alphaStart, alphaEnd)); + } + return mCurrentAnimator; + } + + @MainThread + PipTransitionAnimator getAnimator(IWindowContainer wc, boolean scheduleFinishPip, + Rect startBounds, Rect endBounds) { + if (mCurrentAnimator == null) { + mCurrentAnimator = setupPipTransitionAnimator( + PipTransitionAnimator.ofBounds(wc, scheduleFinishPip, startBounds, endBounds)); + } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS + && mCurrentAnimator.isRunning()) { + mCurrentAnimator.setDestinationBounds(endBounds); + // construct new Rect instances in case they are recycled + mCurrentAnimator.updateEndValue(new Rect(endBounds)); + } else { + mCurrentAnimator.cancel(); + mCurrentAnimator = setupPipTransitionAnimator( + PipTransitionAnimator.ofBounds(wc, scheduleFinishPip, startBounds, endBounds)); + } + return mCurrentAnimator; + } + + private PipTransitionAnimator setupPipTransitionAnimator(PipTransitionAnimator animator) { + animator.setInterpolator(mFastOutSlowInInterpolator); + animator.setFloatValues(FRACTION_START, FRACTION_END); + return animator; + } + + /** + * Additional callback interface for PiP animation + */ + public static class PipAnimationCallback { + /** + * Called when PiP animation is started. + */ + public void onPipAnimationStart(IWindowContainer wc, PipTransitionAnimator animator) {} + + /** + * Called when PiP animation is ended. + */ + public void onPipAnimationEnd(IWindowContainer wc, SurfaceControl.Transaction tx, + PipTransitionAnimator animator) {} + + /** + * Called when PiP animation is cancelled. + */ + public void onPipAnimationCancel(IWindowContainer wc, PipTransitionAnimator animator) {} + } + + /** + * Animator for PiP transition animation which supports both alpha and bounds animation. + * @param <T> Type of property to animate, either alpha (float) or bounds (Rect) + */ + public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements + ValueAnimator.AnimatorUpdateListener, + ValueAnimator.AnimatorListener { + private final IWindowContainer mWindowContainer; + private final boolean mScheduleFinishPip; + private final SurfaceControl mLeash; + private final @AnimationType int mAnimationType; + private final Rect mDestinationBounds = new Rect(); + + private T mStartValue; + private T mEndValue; + private T mCurrentValue; + private PipAnimationCallback mPipAnimationCallback; + private SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; + + private PipTransitionAnimator(IWindowContainer wc, boolean scheduleFinishPip, + @AnimationType int animationType, Rect destinationBounds, + T startValue, T endValue) { + mWindowContainer = wc; + mScheduleFinishPip = scheduleFinishPip; + try { + mLeash = wc.getLeash(); + mAnimationType = animationType; + mDestinationBounds.set(destinationBounds); + mStartValue = startValue; + mEndValue = endValue; + addListener(this); + addUpdateListener(this); + mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + @Override + public void onAnimationStart(Animator animation) { + mCurrentValue = mStartValue; + applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(), FRACTION_START); + if (mPipAnimationCallback != null) { + mPipAnimationCallback.onPipAnimationStart(mWindowContainer, this); + } + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(), + animation.getAnimatedFraction()); + } + + @Override + public void onAnimationEnd(Animator animation) { + mCurrentValue = mEndValue; + final SurfaceControl.Transaction tx = newSurfaceControlTransaction(); + applySurfaceControlTransaction(mLeash, tx, FRACTION_END); + if (mPipAnimationCallback != null) { + mPipAnimationCallback.onPipAnimationEnd(mWindowContainer, tx, this); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + if (mPipAnimationCallback != null) { + mPipAnimationCallback.onPipAnimationCancel(mWindowContainer, this); + } + } + + @Override public void onAnimationRepeat(Animator animation) {} + + @AnimationType int getAnimationType() { + return mAnimationType; + } + + PipTransitionAnimator<T> setPipAnimationCallback(PipAnimationCallback callback) { + mPipAnimationCallback = callback; + return this; + } + + boolean shouldScheduleFinishPip() { + return mScheduleFinishPip; + } + + T getStartValue() { + return mStartValue; + } + + T getEndValue() { + return mEndValue; + } + + Rect getDestinationBounds() { + return mDestinationBounds; + } + + void setDestinationBounds(Rect destinationBounds) { + mDestinationBounds.set(destinationBounds); + } + + void setCurrentValue(T value) { + mCurrentValue = value; + } + + /** + * Updates the {@link #mEndValue}. + * + * NOTE: Do not forget to call {@link #setDestinationBounds(Rect)} for bounds animation. + * This is typically used when we receive a shelf height adjustment during the bounds + * animation. In which case we can update the end bounds and keep the existing animation + * running instead of cancelling it. + */ + void updateEndValue(T endValue) { + mEndValue = endValue; + mStartValue = mCurrentValue; + } + + SurfaceControl.Transaction newSurfaceControlTransaction() { + return mSurfaceControlTransactionFactory.getTransaction(); + } + + @VisibleForTesting + void setSurfaceControlTransactionFactory(SurfaceControlTransactionFactory factory) { + mSurfaceControlTransactionFactory = factory; + } + + abstract void applySurfaceControlTransaction(SurfaceControl leash, + SurfaceControl.Transaction tx, float fraction); + + static PipTransitionAnimator<Float> ofAlpha(IWindowContainer wc, boolean scheduleFinishPip, + Rect destinationBounds, float startValue, float endValue) { + return new PipTransitionAnimator<Float>(wc, scheduleFinishPip, ANIM_TYPE_ALPHA, + destinationBounds, startValue, endValue) { + @Override + void applySurfaceControlTransaction(SurfaceControl leash, + SurfaceControl.Transaction tx, float fraction) { + final float alpha = getStartValue() * (1 - fraction) + getEndValue() * fraction; + setCurrentValue(alpha); + tx.setAlpha(leash, alpha); + if (Float.compare(fraction, FRACTION_START) == 0) { + // Ensure the start condition + final Rect bounds = getDestinationBounds(); + tx.setPosition(leash, bounds.left, bounds.top) + .setWindowCrop(leash, bounds.width(), bounds.height()); + } + tx.apply(); + } + }; + } + + static PipTransitionAnimator<Rect> ofBounds(IWindowContainer wc, boolean scheduleFinishPip, + Rect startValue, Rect endValue) { + // construct new Rect instances in case they are recycled + return new PipTransitionAnimator<Rect>(wc, scheduleFinishPip, ANIM_TYPE_BOUNDS, + endValue, new Rect(startValue), new Rect(endValue)) { + private final Rect mTmpRect = new Rect(); + + private int getCastedFractionValue(float start, float end, float fraction) { + return (int) (start * (1 - fraction) + end * fraction + .5f); + } + + @Override + void applySurfaceControlTransaction(SurfaceControl leash, + SurfaceControl.Transaction tx, float fraction) { + final Rect start = getStartValue(); + final Rect end = getEndValue(); + mTmpRect.set( + getCastedFractionValue(start.left, end.left, fraction), + getCastedFractionValue(start.top, end.top, fraction), + getCastedFractionValue(start.right, end.right, fraction), + getCastedFractionValue(start.bottom, end.bottom, fraction)); + setCurrentValue(mTmpRect); + tx.setPosition(leash, mTmpRect.left, mTmpRect.top) + .setWindowCrop(leash, mTmpRect.width(), mTmpRect.height()); + if (Float.compare(fraction, FRACTION_START) == 0) { + // Ensure the start condition + tx.setAlpha(leash, 1f); + } + tx.apply(); + } + }; + } + } + + interface SurfaceControlTransactionFactory { + SurfaceControl.Transaction getTransaction(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java index 41b31306a931..8c3ccaab8249 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java @@ -36,7 +36,6 @@ import android.util.Size; import android.util.TypedValue; import android.view.DisplayInfo; import android.view.Gravity; -import android.view.IPinnedStackController; import android.view.IWindowManager; import android.view.WindowContainerTransaction; import android.view.WindowManagerGlobal; @@ -56,9 +55,7 @@ public class PipBoundsHandler { private final IWindowManager mWindowManager; private final PipSnapAlgorithm mSnapAlgorithm; private final DisplayInfo mDisplayInfo = new DisplayInfo(); - private final Rect mStableInsets = new Rect(); private final Rect mTmpInsets = new Rect(); - private final Point mTmpDisplaySize = new Point(); /** * Tracks the destination bounds, used for any following @@ -66,7 +63,6 @@ public class PipBoundsHandler { */ private final Rect mLastDestinationBounds = new Rect(); - private IPinnedStackController mPinnedStackController; private ComponentName mLastPipComponentName; private float mReentrySnapFraction = INVALID_SNAP_FRACTION; private Size mReentrySize = null; @@ -80,7 +76,6 @@ public class PipBoundsHandler { private Point mScreenEdgeInsets; private int mCurrentMinSize; - private boolean mIsMinimized; private boolean mIsImeShowing; private int mImeHeight; private boolean mIsShelfShowing; @@ -123,10 +118,6 @@ public class PipBoundsHandler { com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio); } - public void setPinnedStackController(IPinnedStackController controller) { - mPinnedStackController = controller; - } - public void setMinEdgeSize(int minEdgeSize) { mCurrentMinSize = minEdgeSize; } @@ -155,14 +146,6 @@ public class PipBoundsHandler { } /** - * Responds to IPinnedStackListener on minimized state change. - */ - public void onMinimizedStateChanged(boolean minimized) { - mIsMinimized = minimized; - mSnapAlgorithm.setMinimized(minimized); - } - - /** * Responds to IPinnedStackListener on movement bounds change. * Note that both inset and normal bounds will be calculated here rather than in the caller. */ @@ -238,9 +221,9 @@ public class PipBoundsHandler { } /** - * Responds to IPinnedStackListener on preparing the pinned stack animation. + * @return {@link Rect} of the destination PiP window bounds. */ - public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) { + Rect getDestinationBounds(float aspectRatio, Rect bounds) { final Rect destinationBounds; final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize); if (bounds == null) { @@ -253,17 +236,16 @@ public class PipBoundsHandler { false /* useCurrentMinEdgeSize */); } if (destinationBounds.equals(bounds)) { - return; + return bounds; } mAspectRatio = aspectRatio; onResetReentryBoundsUnchecked(); - try { - mPinnedStackController.startAnimation(destinationBounds, sourceRectHint, - -1 /* animationDuration */); - mLastDestinationBounds.set(destinationBounds); - } catch (RemoteException e) { - Log.e(TAG, "Failed to start PiP animation from SysUI", e); - } + mLastDestinationBounds.set(destinationBounds); + return destinationBounds; + } + + float getDefaultAspectRatio() { + return mDefaultAspectRatio; } /** @@ -307,18 +289,10 @@ public class PipBoundsHandler { false /* adjustForIme */); mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds, snapFraction); - if (mIsMinimized) { - applyMinimizedOffset(postChangeStackBounds, postChangeMovementBounds); - } - try { - outBounds.set(postChangeStackBounds); - mLastDestinationBounds.set(outBounds); - mPinnedStackController.resetBoundsAnimation(outBounds); - t.setBounds(pinnedStackInfo.stackToken, outBounds); - } catch (RemoteException e) { - Log.e(TAG, "Failed to resize PiP on display rotation", e); - } + outBounds.set(postChangeStackBounds); + mLastDestinationBounds.set(outBounds); + t.setBounds(pinnedStackInfo.stackToken, outBounds); return true; } @@ -370,9 +344,6 @@ public class PipBoundsHandler { final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f); stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight()); mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction); - if (mIsMinimized) { - applyMinimizedOffset(stackBounds, getMovementBounds(stackBounds)); - } } /** @@ -436,20 +407,6 @@ public class PipBoundsHandler { } /** - * Applies the minimized offsets to the given stack bounds. - */ - private void applyMinimizedOffset(Rect stackBounds, Rect movementBounds) { - mTmpDisplaySize.set(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); - try { - mWindowManager.getStableInsets(mContext.getDisplayId(), mStableInsets); - mSnapAlgorithm.applyMinimizedOffset(stackBounds, movementBounds, mTmpDisplaySize, - mStableInsets); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get stable insets from WM", e); - } - } - - /** * @return the default snap fraction to apply instead of the default gravity when calculating * the default stack bounds when first entering PiP. */ @@ -486,7 +443,6 @@ public class PipBoundsHandler { pw.println(innerPrefix + "mMaxAspectRatio=" + mMaxAspectRatio); pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio); pw.println(innerPrefix + "mDefaultStackGravity=" + mDefaultStackGravity); - pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized); pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing); pw.println(innerPrefix + "mImeHeight=" + mImeHeight); pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing); diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java b/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java index f3e707c6408c..6b89718acee8 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java @@ -63,14 +63,9 @@ public class PipSnapAlgorithm { private int mOrientation = Configuration.ORIENTATION_UNDEFINED; - private final int mMinimizedVisibleSize; - private boolean mIsMinimized; - public PipSnapAlgorithm(Context context) { Resources res = context.getResources(); mContext = context; - mMinimizedVisibleSize = res.getDimensionPixelSize( - com.android.internal.R.dimen.pip_minimized_visible_size); mDefaultSizePercent = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent); mMaxAspectRatioForMinSize = res.getFloat( @@ -92,13 +87,6 @@ public class PipSnapAlgorithm { } /** - * Sets the PIP's minimized state. - */ - public void setMinimized(boolean isMinimized) { - mIsMinimized = isMinimized; - } - - /** * @return the closest absolute snap stack bounds for the given {@param stackBounds} moving at * the given {@param velocityX} and {@param velocityY}. The {@param movementBounds} should be * those for the given {@param stackBounds}. @@ -235,20 +223,6 @@ public class PipSnapAlgorithm { } /** - * Applies the offset to the {@param stackBounds} to adjust it to a minimized state. - */ - public void applyMinimizedOffset(Rect stackBounds, Rect movementBounds, Point displaySize, - Rect stableInsets) { - if (stackBounds.left <= movementBounds.centerX()) { - stackBounds.offsetTo(stableInsets.left + mMinimizedVisibleSize - stackBounds.width(), - stackBounds.top); - } else { - stackBounds.offsetTo(displaySize.x - stableInsets.right - mMinimizedVisibleSize, - stackBounds.top); - } - } - - /** * @return returns a fraction that describes where along the {@param movementBounds} the * {@param stackBounds} are. If the {@param stackBounds} are not currently on the * {@param movementBounds} exactly, then they will be snapped to the movement bounds. @@ -402,16 +376,11 @@ public class PipSnapAlgorithm { * the new bounds out to {@param boundsOut}. */ private void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut) { - // If the stackBounds are minimized, then it should only be snapped back horizontally final int boundedLeft = Math.max(movementBounds.left, Math.min(movementBounds.right, stackBounds.left)); final int boundedTop = Math.max(movementBounds.top, Math.min(movementBounds.bottom, stackBounds.top)); boundsOut.set(stackBounds); - if (mIsMinimized) { - boundsOut.offsetTo(boundedLeft, boundedTop); - return; - } // Otherwise, just find the closest edge final int fromLeft = Math.abs(stackBounds.left - movementBounds.left); @@ -479,7 +448,5 @@ public class PipSnapAlgorithm { pw.println(prefix + PipSnapAlgorithm.class.getSimpleName()); pw.println(innerPrefix + "mSnapMode=" + mSnapMode); pw.println(innerPrefix + "mOrientation=" + mOrientation); - pw.println(innerPrefix + "mMinimizedVisibleSize=" + mMinimizedVisibleSize); - pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java new file mode 100644 index 000000000000..1555153e8d4f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -0,0 +1,291 @@ +/* + * 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.systemui.pip; + +import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA; +import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS; +import static com.android.systemui.pip.PipAnimationController.DURATION_NONE; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.ActivityTaskManager; +import android.app.ITaskOrganizerController; +import android.app.PictureInPictureParams; +import android.content.Context; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Looper; +import android.os.RemoteException; +import android.util.Log; +import android.view.DisplayInfo; +import android.view.ITaskOrganizer; +import android.view.IWindowContainer; +import android.view.SurfaceControl; +import android.view.WindowContainerTransaction; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Manages PiP tasks such as resize and offset. + * + * This class listens on {@link ITaskOrganizer} callbacks for windowing mode change + * both to and from PiP and issues corresponding animation if applicable. + * Normally, we apply series of {@link SurfaceControl.Transaction} when the animator is running + * and files a final {@link WindowContainerTransaction} at the end of the transition. + * + * This class is also responsible for general resize/offset PiP operations within SysUI component, + * see also {@link com.android.systemui.pip.phone.PipMotionHelper}. + */ +public class PipTaskOrganizer extends ITaskOrganizer.Stub { + private static final String TAG = PipTaskOrganizer.class.getSimpleName(); + + private final Handler mMainHandler; + private final ITaskOrganizerController mTaskOrganizerController; + private final PipBoundsHandler mPipBoundsHandler; + private final PipAnimationController mPipAnimationController; + private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); + private final Rect mDisplayBounds = new Rect(); + private final Rect mLastReportedBounds = new Rect(); + + private final PipAnimationController.PipAnimationCallback mPipAnimationCallback = + new PipAnimationController.PipAnimationCallback() { + @Override + public void onPipAnimationStart(IWindowContainer wc, + PipAnimationController.PipTransitionAnimator animator) { + mMainHandler.post(() -> { + for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { + final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); + callback.onPipTransitionStarted(); + } + }); + } + + @Override + public void onPipAnimationEnd(IWindowContainer wc, SurfaceControl.Transaction tx, + PipAnimationController.PipTransitionAnimator animator) { + mMainHandler.post(() -> { + for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { + final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); + callback.onPipTransitionFinished(); + } + }); + final Rect destinationBounds = animator.getDestinationBounds(); + mLastReportedBounds.set(destinationBounds); + try { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + if (animator.shouldScheduleFinishPip()) { + wct.scheduleFinishEnterPip(wc, destinationBounds); + } else { + wct.setBounds(wc, destinationBounds); + } + wct.setBoundsChangeTransaction(wc, tx); + mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */); + } catch (RemoteException e) { + Log.e(TAG, "Failed to apply container transaction", e); + } + } + + @Override + public void onPipAnimationCancel(IWindowContainer wc, + PipAnimationController.PipTransitionAnimator animator) { + mMainHandler.post(() -> { + for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { + final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); + callback.onPipTransitionCanceled(); + } + }); + } + }; + + private ActivityManager.RunningTaskInfo mTaskInfo; + private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; + + public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler) { + mMainHandler = new Handler(Looper.getMainLooper()); + mTaskOrganizerController = ActivityTaskManager.getTaskOrganizerController(); + mPipBoundsHandler = boundsHandler; + mPipAnimationController = new PipAnimationController(context); + } + + /** + * Resize the PiP window, animate if the given duration is not {@link #DURATION_NONE} + */ + public void resizePinnedStack(Rect destinationBounds, int durationMs) { + Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer"); + resizePinnedStackInternal(mTaskInfo.token, false /* scheduleFinishPip */, + mLastReportedBounds, destinationBounds, durationMs); + } + + /** + * Offset the PiP window, animate if the given duration is not {@link #DURATION_NONE} + */ + public void offsetPinnedStack(Rect originalBounds, int xOffset, int yOffset, int durationMs) { + if (mTaskInfo == null) { + Log.w(TAG, "mTaskInfo is not set"); + return; + } + final Rect destinationBounds = new Rect(originalBounds); + destinationBounds.offset(xOffset, yOffset); + resizePinnedStackInternal(mTaskInfo.token, false /* scheduleFinishPip*/, + originalBounds, destinationBounds, durationMs); + } + + /** + * Registers {@link PipTransitionCallback} to receive transition callbacks. + */ + public void registerPipTransitionCallback(PipTransitionCallback callback) { + mPipTransitionCallbacks.add(callback); + } + + /** + * Sets the preferred animation type for one time. + * This is typically used to set the animation type to {@link #ANIM_TYPE_ALPHA}. + */ + public void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) { + mOneShotAnimationType = animationType; + } + + /** + * Updates the display dimension with given {@link DisplayInfo} + */ + public void onDisplayInfoChanged(DisplayInfo displayInfo) { + mDisplayBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight); + } + + /** + * Callback to issue the final {@link WindowContainerTransaction} on end of movements. + * @param destinationBounds the final bounds. + */ + public void onMotionMovementEnd(Rect destinationBounds) { + try { + mLastReportedBounds.set(destinationBounds); + final WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setBounds(mTaskInfo.token, destinationBounds); + mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */); + } catch (RemoteException e) { + Log.w(TAG, "Failed to apply window container transaction", e); + } + } + + @Override + public void taskAppeared(ActivityManager.RunningTaskInfo info) { + Objects.requireNonNull(info, "Requires RunningTaskInfo"); + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( + getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */); + Objects.requireNonNull(destinationBounds, "Missing destination bounds"); + mTaskInfo = info; + if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { + final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); + resizePinnedStackInternal(mTaskInfo.token, true /* scheduleFinishPip */, + currentBounds, destinationBounds, + PipAnimationController.DURATION_DEFAULT_MS); + } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { + mMainHandler.post(() -> mPipAnimationController + .getAnimator(mTaskInfo.token, true /* scheduleFinishPip */, + destinationBounds, 0f, 1f) + .setPipAnimationCallback(mPipAnimationCallback) + .setDuration(PipAnimationController.DURATION_DEFAULT_MS) + .start()); + mOneShotAnimationType = ANIM_TYPE_BOUNDS; + } else { + throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType); + } + } + + @Override + public void taskVanished(IWindowContainer token) { + Objects.requireNonNull(token, "Requires valid IWindowContainer"); + if (token.asBinder() != mTaskInfo.token.asBinder()) { + Log.wtf(TAG, "Unrecognized token: " + token); + return; + } + resizePinnedStackInternal(token, false /* scheduleFinishPip */, + mLastReportedBounds, mDisplayBounds, + PipAnimationController.DURATION_DEFAULT_MS); + } + + @Override + public void transactionReady(int id, SurfaceControl.Transaction t) { + } + + @Override + public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( + getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */); + Objects.requireNonNull(destinationBounds, "Missing destination bounds"); + resizePinnedStack(destinationBounds, PipAnimationController.DURATION_DEFAULT_MS); + } + + private void resizePinnedStackInternal(IWindowContainer wc, boolean scheduleFinishPip, + Rect currentBounds, Rect destinationBounds, int animationDurationMs) { + try { + // Could happen when dismissPip + if (wc == null || wc.getLeash() == null) { + Log.w(TAG, "Abort animation, invalid leash"); + return; + } + final SurfaceControl leash = wc.getLeash(); + if (animationDurationMs == DURATION_NONE) { + // Directly resize if no animation duration is set. When fling, wait for final + // callback to issue the proper WindowContainerTransaction with destination bounds. + new SurfaceControl.Transaction() + .setPosition(leash, destinationBounds.left, destinationBounds.top) + .setWindowCrop(leash, destinationBounds.width(), destinationBounds.height()) + .apply(); + } else { + mMainHandler.post(() -> mPipAnimationController + .getAnimator(wc, scheduleFinishPip, currentBounds, destinationBounds) + .setPipAnimationCallback(mPipAnimationCallback) + .setDuration(animationDurationMs) + .start()); + } + } catch (RemoteException e) { + Log.w(TAG, "Abort animation, invalid window container", e); + } catch (Exception e) { + Log.e(TAG, "Should not reach here, terrible thing happened", e); + } + } + + private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) { + return params == null + ? mPipBoundsHandler.getDefaultAspectRatio() + : params.getAspectRatio(); + } + + /** + * Callback interface for PiP transitions (both from and to PiP mode) + */ + public interface PipTransitionCallback { + /** + * Callback when the pip transition is started. + */ + void onPipTransitionStarted(); + + /** + * Callback when the pip transition is finished. + */ + void onPipTransitionFinished(); + + /** + * Callback when the pip transition is cancelled. + */ + void onPipTransitionCanceled(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java index 599c845ba39a..4fb675e31f0e 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java @@ -25,6 +25,7 @@ import android.os.UserHandle; import android.os.UserManager; import com.android.systemui.SystemUI; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.statusbar.CommandQueue; import java.io.FileDescriptor; @@ -98,6 +99,18 @@ public class PipUI extends SystemUI implements CommandQueue.Callbacks { mPipManager.setShelfHeight(visible, height); } + public void setPinnedStackAnimationType(int animationType) { + if (mPipManager != null) { + mPipManager.setPinnedStackAnimationType(animationType); + } + } + + public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) { + if (mPipManager != null) { + mPipManager.setPinnedStackAnimationListener(listener); + } + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mPipManager == null) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index cb94e28e3467..e98dec0835bf 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -41,6 +41,8 @@ import com.android.systemui.UiOffloadThread; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.pip.BasePipManager; import com.android.systemui.pip.PipBoundsHandler; +import com.android.systemui.pip.PipTaskOrganizer; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener; @@ -59,12 +61,11 @@ import javax.inject.Singleton; * Manages the picture-in-picture (PIP) UI and states for Phones. */ @Singleton -public class PipManager implements BasePipManager { +public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitionCallback { private static final String TAG = "PipManager"; private Context mContext; private IActivityManager mActivityManager; - private IActivityTaskManager mActivityTaskManager; private Handler mHandler = new Handler(); private final PinnedStackListener mPinnedStackListener = new PipManagerPinnedStackListener(); @@ -78,7 +79,9 @@ public class PipManager implements BasePipManager { private PipMenuActivityController mMenuController; private PipMediaController mMediaController; private PipTouchHandler mTouchHandler; + private PipTaskOrganizer mPipTaskOrganizer; private PipAppOpsListener mAppOpsListener; + private IPinnedStackAnimationListener mPinnedStackAnimationRecentsListener; /** * Handler for display rotation changes. @@ -124,20 +127,6 @@ public class PipManager implements BasePipManager { } @Override - public void onPinnedStackAnimationStarted() { - // Disable touches while the animation is running - mTouchHandler.setTouchEnabled(false); - } - - @Override - public void onPinnedStackAnimationEnded() { - // Re-enable touches after the animation completes - mTouchHandler.setTouchEnabled(true); - mTouchHandler.onPinnedStackAnimationEnded(); - mMenuController.onPinnedStackAnimationEnded(); - } - - @Override public void onPinnedActivityRestartAttempt(boolean clearedTask) { mTouchHandler.getMotionHelper().expandPip(clearedTask /* skipAnimation */); } @@ -149,10 +138,7 @@ public class PipManager implements BasePipManager { private class PipManagerPinnedStackListener extends PinnedStackListener { @Override public void onListenerRegistered(IPinnedStackController controller) { - mHandler.post(() -> { - mPipBoundsHandler.setPinnedStackController(controller); - mTouchHandler.setPinnedStackController(controller); - }); + mHandler.post(() -> mTouchHandler.setPinnedStackController(controller)); } @Override @@ -164,18 +150,9 @@ public class PipManager implements BasePipManager { } @Override - public void onMinimizedStateChanged(boolean isMinimized) { - mHandler.post(() -> { - mPipBoundsHandler.onMinimizedStateChanged(isMinimized); - mTouchHandler.setMinimizedState(isMinimized, true /* fromController */); - }); - } - - @Override - public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment) { + public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) { mHandler.post(() -> updateMovementBounds(animatingBounds, fromImeAdjustment, - fromShelfAdjustment)); + false /* fromShelfAdjustment */)); } @Override @@ -207,7 +184,10 @@ public class PipManager implements BasePipManager { @Override public void onDisplayInfoChanged(DisplayInfo displayInfo) { - mHandler.post(() -> mPipBoundsHandler.onDisplayInfoChanged(displayInfo)); + mHandler.post(() -> { + mPipBoundsHandler.onDisplayInfoChanged(displayInfo); + mPipTaskOrganizer.onDisplayInfoChanged(displayInfo); + }); } @Override @@ -219,13 +199,6 @@ public class PipManager implements BasePipManager { public void onAspectRatioChanged(float aspectRatio) { mHandler.post(() -> mPipBoundsHandler.onAspectRatioChanged(aspectRatio)); } - - @Override - public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) { - mHandler.post(() -> { - mPipBoundsHandler.onPrepareAnimation(sourceRectHint, aspectRatio, bounds); - }); - } } @Inject @@ -234,7 +207,6 @@ public class PipManager implements BasePipManager { FloatingContentCoordinator floatingContentCoordinator) { mContext = context; mActivityManager = ActivityManager.getService(); - mActivityTaskManager = ActivityTaskManager.getService(); try { WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener); @@ -243,24 +215,29 @@ public class PipManager implements BasePipManager { } ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); + final IActivityTaskManager activityTaskManager = ActivityTaskManager.getService(); mPipBoundsHandler = new PipBoundsHandler(context); + mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler); + mPipTaskOrganizer.registerPipTransitionCallback(this); mInputConsumerController = InputConsumerController.getPipInputConsumer(); mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher); - mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController, + mMenuController = new PipMenuActivityController(context, mMediaController, mInputConsumerController); - mTouchHandler = new PipTouchHandler(context, mActivityManager, mActivityTaskManager, - mMenuController, mInputConsumerController, mPipBoundsHandler, + mTouchHandler = new PipTouchHandler(context, mActivityManager, activityTaskManager, + mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer, floatingContentCoordinator); mAppOpsListener = new PipAppOpsListener(context, mActivityManager, mTouchHandler.getMotionHelper()); displayController.addDisplayChangingController(mRotationController); - // If SystemUI restart, and it already existed a pinned stack, - // register the pip input consumer to ensure touch can send to it. try { - ActivityManager.StackInfo stackInfo = mActivityTaskManager.getStackInfo( + ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer( + mPipTaskOrganizer, WINDOWING_MODE_PINNED); + ActivityManager.StackInfo stackInfo = activityTaskManager.getStackInfo( WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); if (stackInfo != null) { + // If SystemUI restart, and it already existed a pinned stack, + // register the pip input consumer to ensure touch can send to it. mInputConsumerController.registerInputConsumer(); } } catch (RemoteException e) { @@ -320,6 +297,46 @@ public class PipManager implements BasePipManager { }); } + @Override + public void setPinnedStackAnimationType(int animationType) { + mHandler.post(() -> mPipTaskOrganizer.setOneShotAnimationType(animationType)); + } + + @Override + public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) { + mHandler.post(() -> mPinnedStackAnimationRecentsListener = listener); + } + + @Override + public void onPipTransitionStarted() { + // Disable touches while the animation is running + mTouchHandler.setTouchEnabled(false); + if (mPinnedStackAnimationRecentsListener != null) { + try { + mPinnedStackAnimationRecentsListener.onPinnedStackAnimationStarted(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to callback recents", e); + } + } + } + + @Override + public void onPipTransitionFinished() { + onPipTransitionFinishedOrCanceled(); + } + + @Override + public void onPipTransitionCanceled() { + onPipTransitionFinishedOrCanceled(); + } + + private void onPipTransitionFinishedOrCanceled() { + // Re-enable touches after the animation completes + mTouchHandler.setTouchEnabled(true); + mTouchHandler.onPinnedStackAnimationEnded(); + mMenuController.onPinnedStackAnimationEnded(); + } + private void updateMovementBounds(Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment) { // Populate inset / normal bounds and DisplayInfo from mPipBoundsHandler first. diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java index c7bfc06829b3..d660b670446b 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -22,7 +22,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import android.app.ActivityManager.StackInfo; import android.app.ActivityOptions; import android.app.ActivityTaskManager; -import android.app.IActivityManager; import android.app.RemoteAction; import android.content.Context; import android.content.Intent; @@ -68,11 +67,8 @@ public class PipMenuActivityController { public static final int MESSAGE_MENU_STATE_CHANGED = 100; public static final int MESSAGE_EXPAND_PIP = 101; - public static final int MESSAGE_MINIMIZE_PIP = 102; public static final int MESSAGE_DISMISS_PIP = 103; public static final int MESSAGE_UPDATE_ACTIVITY_CALLBACK = 104; - public static final int MESSAGE_REGISTER_INPUT_CONSUMER = 105; - public static final int MESSAGE_UNREGISTER_INPUT_CONSUMER = 106; public static final int MESSAGE_SHOW_MENU = 107; public static final int MENU_STATE_NONE = 0; @@ -100,11 +96,6 @@ public class PipMenuActivityController { void onPipExpand(); /** - * Called when the PIP requested to be minimized. - */ - void onPipMinimize(); - - /** * Called when the PIP requested to be dismissed. */ void onPipDismiss(); @@ -116,7 +107,6 @@ public class PipMenuActivityController { } private Context mContext; - private IActivityManager mActivityManager; private PipMediaController mMediaController; private InputConsumerController mInputConsumerController; @@ -146,10 +136,6 @@ public class PipMenuActivityController { mListeners.forEach(l -> l.onPipExpand()); break; } - case MESSAGE_MINIMIZE_PIP: { - mListeners.forEach(l -> l.onPipMinimize()); - break; - } case MESSAGE_DISMISS_PIP: { mListeners.forEach(l -> l.onPipDismiss()); break; @@ -194,10 +180,9 @@ public class PipMenuActivityController { } }; - public PipMenuActivityController(Context context, IActivityManager activityManager, + public PipMenuActivityController(Context context, PipMediaController mediaController, InputConsumerController inputConsumerController) { mContext = context; - mActivityManager = activityManager; mMediaController = mediaController; mInputConsumerController = inputConsumerController; } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index c6e28522ccbd..91f539c3a13d 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -22,7 +22,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager.StackInfo; -import android.app.IActivityManager; import android.app.IActivityTaskManager; import android.content.Context; import android.graphics.Point; @@ -39,7 +38,9 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.internal.os.SomeArgs; +import com.android.systemui.pip.PipAnimationController; import com.android.systemui.pip.PipSnapAlgorithm; +import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.util.FloatingContentCoordinator; @@ -65,8 +66,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call /** Friction to use for PIP when it moves via physics fling animations. */ private static final float DEFAULT_FRICTION = 2f; - // The fraction of the stack width that the user has to drag offscreen to minimize the PiP - private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.3f; // The fraction of the stack height that the user has to drag offscreen to dismiss the PiP private static final float DISMISS_OFFSCREEN_FRACTION = 0.3f; @@ -74,10 +73,10 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call private static final int MSG_RESIZE_ANIMATE = 2; private static final int MSG_OFFSET_ANIMATE = 3; - private Context mContext; - private IActivityManager mActivityManager; - private IActivityTaskManager mActivityTaskManager; - private Handler mHandler; + private final Context mContext; + private final IActivityTaskManager mActivityTaskManager; + private final PipTaskOrganizer mPipTaskOrganizer; + private final Handler mHandler; private PipMenuActivityController mMenuController; private PipSnapAlgorithm mSnapAlgorithm; @@ -139,14 +138,14 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call new PhysicsAnimator.SpringConfig( SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY); - public PipMotionHelper(Context context, IActivityManager activityManager, - IActivityTaskManager activityTaskManager, PipMenuActivityController menuController, + public PipMotionHelper(Context context, IActivityTaskManager activityTaskManager, + PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController, PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils, FloatingContentCoordinator floatingContentCoordinator) { mContext = context; mHandler = new Handler(ForegroundThread.get().getLooper(), this); - mActivityManager = activityManager; mActivityTaskManager = activityTaskManager; + mPipTaskOrganizer = pipTaskOrganizer; mMenuController = menuController; mSnapAlgorithm = snapAlgorithm; mFlingAnimationUtils = flingAnimationUtils; @@ -285,35 +284,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call } /** - * @return the closest minimized PiP bounds. - */ - Rect getClosestMinimizedBounds(Rect stackBounds) { - Point displaySize = new Point(); - mContext.getDisplay().getRealSize(displaySize); - Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mMovementBounds, stackBounds); - mSnapAlgorithm.applyMinimizedOffset(toBounds, mMovementBounds, displaySize, mStableInsets); - return toBounds; - } - - /** - * @return whether the PiP at the current bounds should be minimized. - */ - boolean shouldMinimizePip() { - Point displaySize = new Point(); - mContext.getDisplay().getRealSize(displaySize); - if (mBounds.left < 0) { - float offscreenFraction = (float) -mBounds.left / mBounds.width(); - return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION; - } else if (mBounds.right > displaySize.x) { - float offscreenFraction = (float) (mBounds.right - displaySize.x) / - mBounds.width(); - return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION; - } else { - return false; - } - } - - /** * @return whether the PiP at the current bounds should be dismissed. */ boolean shouldDismissPip() { @@ -328,25 +298,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call } /** - * Animates the PiP to the minimized state, slightly offscreen. - */ - void animateToClosestMinimizedState(@Nullable Runnable updateAction) { - final Rect toBounds = getClosestMinimizedBounds(mBounds); - - mAnimatedBounds.set(mBounds); - mAnimatedBoundsPhysicsAnimator - .spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig) - .spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig); - - if (updateAction != null) { - mAnimatedBoundsPhysicsAnimator.addUpdateListener( - (target, values) -> updateAction.run()); - } - - startBoundsAnimation(); - } - - /** * Flings the PiP to the closest snap target. */ void flingToSnapTarget( @@ -436,8 +387,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call * Animates the PiP from the expanded state to the normal state after the menu is hidden. */ void animateToUnexpandedState(Rect normalBounds, float savedSnapFraction, - Rect normalMovementBounds, Rect currentMovementBounds, boolean minimized, - boolean immediate) { + Rect normalMovementBounds, Rect currentMovementBounds, boolean immediate) { if (savedSnapFraction < 0f) { // If there are no saved snap fractions, then just use the current bounds savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds), @@ -445,10 +395,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call } mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction); - if (minimized) { - normalBounds = getClosestMinimizedBounds(normalBounds); - } - if (immediate) { movePip(normalBounds); } else { @@ -503,6 +449,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call cancelAnimations(); mAnimatedBoundsPhysicsAnimator + .withEndActions(() -> mPipTaskOrganizer.onMotionMovementEnd(mAnimatedBounds)) .addUpdateListener(mResizePipVsyncUpdateListener) .start(); } @@ -594,13 +541,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call } /** - * @return the distance between points {@param p1} and {@param p2}. - */ - private float distanceBetweenRectOffsets(Rect r1, Rect r2) { - return PointF.length(r1.left - r2.left, r1.top - r2.top); - } - - /** * Handles messages to be processed on the background thread. */ public boolean handleMessage(Message msg) { @@ -608,13 +548,8 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call case MSG_RESIZE_IMMEDIATE: { SomeArgs args = (SomeArgs) msg.obj; Rect toBounds = (Rect) args.arg1; - try { - mActivityTaskManager.resizePinnedStack( - toBounds, null /* tempPinnedTaskBounds */); - mBounds.set(toBounds); - } catch (RemoteException e) { - Log.e(TAG, "Could not resize pinned stack to bounds: " + toBounds, e); - } + mPipTaskOrganizer.resizePinnedStack(toBounds, PipAnimationController.DURATION_NONE); + mBounds.set(toBounds); return true; } @@ -631,8 +566,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call return true; } - mActivityTaskManager.animateResizePinnedStack(stackInfo.stackId, toBounds, - duration); + mPipTaskOrganizer.resizePinnedStack(toBounds, duration); mBounds.set(toBounds); } catch (RemoteException e) { Log.e(TAG, "Could not animate resize pinned stack to bounds: " + toBounds, e); @@ -654,8 +588,8 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call return true; } - mActivityTaskManager.offsetPinnedStackBounds(stackInfo.stackId, originalBounds, - 0/* xOffset */, offset, duration); + mPipTaskOrganizer.offsetPinnedStack(originalBounds, + 0 /* xOffset */, offset, duration); Rect toBounds = new Rect(originalBounds); toBounds.offset(0, offset); mBounds.set(toBounds); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 8e588e67861c..3b855db6e328 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -45,6 +45,7 @@ import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.systemui.R; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSnapAlgorithm; +import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.util.FloatingContentCoordinator; @@ -58,8 +59,6 @@ import java.io.PrintWriter; public class PipTouchHandler { private static final String TAG = "PipTouchHandler"; - // Allow the PIP to be dragged to the edge of the screen to be minimized. - private static final boolean ENABLE_MINIMIZE = false; // Allow the PIP to be flung from anywhere on the screen to the bottom to be dismissed. private static final boolean ENABLE_FLING_DISMISS = false; @@ -67,12 +66,11 @@ public class PipTouchHandler { private static final int BOTTOM_OFFSET_BUFFER_DP = 1; // Allow dragging the PIP to a location to close it - private final boolean mEnableDimissDragToEdge; + private final boolean mEnableDismissDragToEdge; + // Allow PIP to resize to a slightly bigger state upon touch + private final boolean mEnableResize; private final Context mContext; private final IActivityManager mActivityManager; - private final IActivityTaskManager mActivityTaskManager; - private final ViewConfiguration mViewConfig; - private final PipMenuListener mMenuListener = new PipMenuListener(); private final PipBoundsHandler mPipBoundsHandler; private final PipResizeGestureHandler mPipResizeGestureHandler; private IPinnedStackController mPinnedStackController; @@ -104,7 +102,7 @@ public class PipTouchHandler { private Runnable mShowDismissAffordance = new Runnable() { @Override public void run() { - if (mEnableDimissDragToEdge) { + if (mEnableDismissDragToEdge) { mDismissViewController.showDismissTarget(); } } @@ -112,7 +110,6 @@ public class PipTouchHandler { // Behaviour states private int mMenuState = MENU_STATE_NONE; - private boolean mIsMinimized; private boolean mIsImeShowing; private int mImeHeight; private int mImeOffset; @@ -121,7 +118,6 @@ public class PipTouchHandler { private int mMovementBoundsExtraOffsets; private float mSavedSnapFraction = -1f; private boolean mSendingHoverAccessibilityEvents; - private boolean mMovementWithinMinimize; private boolean mMovementWithinDismiss; private PipAccessibilityInteractionConnection mConnection; @@ -146,15 +142,7 @@ public class PipTouchHandler { @Override public void onPipExpand() { - if (!mIsMinimized) { - mMotionHelper.expandPip(); - } - } - - @Override - public void onPipMinimize() { - setMinimizedStateInternal(true); - mMotionHelper.animateToClosestMinimizedState(null /* updateAction */); + mMotionHelper.expandPip(); } @Override @@ -175,26 +163,24 @@ public class PipTouchHandler { IActivityTaskManager activityTaskManager, PipMenuActivityController menuController, InputConsumerController inputConsumerController, PipBoundsHandler pipBoundsHandler, + PipTaskOrganizer pipTaskOrganizer, FloatingContentCoordinator floatingContentCoordinator) { - // Initialize the Pip input consumer mContext = context; mActivityManager = activityManager; - mActivityTaskManager = activityTaskManager; mAccessibilityManager = context.getSystemService(AccessibilityManager.class); - mViewConfig = ViewConfiguration.get(context); mMenuController = menuController; - mMenuController.addListener(mMenuListener); + mMenuController.addListener(new PipMenuListener()); mDismissViewController = new PipDismissViewController(context); mSnapAlgorithm = new PipSnapAlgorithm(mContext); mFlingAnimationUtils = new FlingAnimationUtils(context.getResources().getDisplayMetrics(), 2.5f); mGesture = new DefaultPipTouchGesture(); - mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mActivityTaskManager, + mMotionHelper = new PipMotionHelper(mContext, activityTaskManager, pipTaskOrganizer, mMenuController, mSnapAlgorithm, mFlingAnimationUtils, floatingContentCoordinator); mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper); - mTouchState = new PipTouchState(mViewConfig, mHandler, + mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler, () -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(), mMovementBounds, true /* allowMenuTimeout */, willResizeMenu())); @@ -203,7 +189,8 @@ public class PipTouchHandler { R.dimen.pip_expanded_shortest_edge_size); mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset); - mEnableDimissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge); + mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge); + mEnableResize = res.getBoolean(R.bool.config_pipEnableResizeForMenu); // Register the listener for input consumer touch events inputConsumerController.setInputListener(this::handleTouchEvent); @@ -313,7 +300,7 @@ public class PipTouchHandler { } else { final float offsetBufferPx = BOTTOM_OFFSET_BUFFER_DP * mContext.getResources().getDisplayMetrics().density; - final Rect toMovementBounds = mMenuState == MENU_STATE_FULL + final Rect toMovementBounds = mMenuState == MENU_STATE_FULL && willResizeMenu() ? new Rect(expandedMovementBounds) : new Rect(normalMovementBounds); final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets; @@ -339,8 +326,7 @@ public class PipTouchHandler { // If we have a deferred resize, apply it now if (mDeferResizeToNormalBoundsUntilRotation == displayRotation) { mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction, - mNormalMovementBounds, mMovementBounds, mIsMinimized, - true /* immediate */); + mNormalMovementBounds, mMovementBounds, true /* immediate */); mSavedSnapFraction = -1f; mDeferResizeToNormalBoundsUntilRotation = -1; } @@ -482,44 +468,6 @@ public class PipTouchHandler { } /** - * Sets the minimized state. - */ - private void setMinimizedStateInternal(boolean isMinimized) { - if (!ENABLE_MINIMIZE) { - return; - } - setMinimizedState(isMinimized, false /* fromController */); - } - - /** - * Sets the minimized state. - */ - void setMinimizedState(boolean isMinimized, boolean fromController) { - if (!ENABLE_MINIMIZE) { - return; - } - if (mIsMinimized != isMinimized) { - MetricsLoggerWrapper.logPictureInPictureMinimize(mContext, - isMinimized, PipUtils.getTopPinnedActivity(mContext, mActivityManager)); - } - mIsMinimized = isMinimized; - mSnapAlgorithm.setMinimized(isMinimized); - - if (fromController) { - if (isMinimized) { - // Move the PiP to the new bounds immediately if minimized - mMotionHelper.movePip(mMotionHelper.getClosestMinimizedBounds(mNormalBounds)); - } - } else if (mPinnedStackController != null) { - try { - mPinnedStackController.setIsMinimized(isMinimized); - } catch (RemoteException e) { - Log.e(TAG, "Could not set minimized state", e); - } - } - } - - /** * Sets the menu visibility. */ private void setMenuState(int menuState, boolean resize) { @@ -562,8 +510,7 @@ public class PipTouchHandler { if (mDeferResizeToNormalBoundsUntilRotation == -1) { Rect normalBounds = new Rect(mNormalBounds); mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction, - mNormalMovementBounds, mMovementBounds, mIsMinimized, - false /* immediate */); + mNormalMovementBounds, mMovementBounds, false /* immediate */); mSavedSnapFraction = -1f; } } else { @@ -601,8 +548,6 @@ public class PipTouchHandler { * Gesture controlling normal movement of the PIP. */ private class DefaultPipTouchGesture extends PipTouchGesture { - // Whether the PiP was on the left side of the screen at the start of the gesture - private boolean mStartedOnLeft; private final Point mStartPosition = new Point(); private final PointF mDelta = new PointF(); @@ -615,17 +560,15 @@ public class PipTouchHandler { Rect bounds = mMotionHelper.getBounds(); mDelta.set(0f, 0f); mStartPosition.set(bounds.left, bounds.top); - mStartedOnLeft = bounds.left < mMovementBounds.centerX(); - mMovementWithinMinimize = true; mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom; - // If the menu is still visible, and we aren't minimized, then just poke the menu + // If the menu is still visible then just poke the menu // so that it will timeout after the user stops touching it - if (mMenuState != MENU_STATE_NONE && !mIsMinimized) { + if (mMenuState != MENU_STATE_NONE) { mMenuController.pokeMenu(); } - if (mEnableDimissDragToEdge) { + if (mEnableDismissDragToEdge) { mDismissViewController.createDismissTarget(); mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY); } @@ -640,7 +583,7 @@ public class PipTouchHandler { if (touchState.startedDragging()) { mSavedSnapFraction = -1f; - if (mEnableDimissDragToEdge) { + if (mEnableDismissDragToEdge) { mHandler.removeCallbacks(mShowDismissAffordance); mDismissViewController.showDismissTarget(); } @@ -662,17 +605,11 @@ public class PipTouchHandler { mTmpBounds.offsetTo((int) left, (int) top); mMotionHelper.movePip(mTmpBounds, true /* isDragging */); - if (mEnableDimissDragToEdge) { + if (mEnableDismissDragToEdge) { updateDismissFraction(); } final PointF curPos = touchState.getLastTouchPosition(); - if (mMovementWithinMinimize) { - // Track if movement remains near starting edge to identify swipes to minimize - mMovementWithinMinimize = mStartedOnLeft - ? curPos.x <= mMovementBounds.left + mTmpBounds.width() - : curPos.x >= mMovementBounds.right; - } if (mMovementWithinDismiss) { // Track if movement remains near the bottom edge to identify swipe to dismiss mMovementWithinDismiss = curPos.y >= mMovementBounds.bottom; @@ -684,7 +621,7 @@ public class PipTouchHandler { @Override public boolean onUp(PipTouchState touchState) { - if (mEnableDimissDragToEdge) { + if (mEnableDismissDragToEdge) { // Clean up the dismiss target regardless of the touch state in case the touch // enabled state changes while the user is interacting cleanUpDismissTarget(); @@ -704,7 +641,7 @@ public class PipTouchHandler { vel.y, isFling); final boolean isFlingToBot = isFling && vel.y > 0 && !isHorizontal && (mMovementWithinDismiss || isUpWithinDimiss); - if (mEnableDimissDragToEdge) { + if (mEnableDismissDragToEdge) { // Check if the user dragged or flung the PiP offscreen to dismiss it if (mMotionHelper.shouldDismissPip() || isFlingToBot) { MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext, @@ -717,33 +654,10 @@ public class PipTouchHandler { } if (touchState.isDragging()) { - final boolean isFlingToEdge = isFling && isHorizontal && mMovementWithinMinimize - && (mStartedOnLeft ? vel.x < 0 : vel.x > 0); - if (ENABLE_MINIMIZE && - !mIsMinimized && (mMotionHelper.shouldMinimizePip() || isFlingToEdge)) { - // Pip should be minimized - setMinimizedStateInternal(true); - if (mMenuState == MENU_STATE_FULL) { - // If the user dragged the expanded PiP to the edge, then hiding the menu - // will trigger the PiP to be scaled back to the normal size with the - // minimize offset adjusted - mMenuController.hideMenu(); - } else { - mMotionHelper.animateToClosestMinimizedState( - PipTouchHandler.this::updateDismissFraction /* updateAction */); - } - return true; - } - if (mIsMinimized) { - // If we're dragging and it wasn't a minimize gesture then we shouldn't be - // minimized. - setMinimizedStateInternal(false); - } - Runnable endAction = null; if (mMenuState != MENU_STATE_NONE) { - // If the menu is still visible, and we aren't minimized, then just poke the - // menu so that it will timeout after the user stops touching it + // If the menu is still visible, then just poke the menu so that + // it will timeout after the user stops touching it mMenuController.showMenu(mMenuState, mMotionHelper.getBounds(), mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()); } else { @@ -759,10 +673,6 @@ public class PipTouchHandler { } else { mMotionHelper.animateToClosestSnapTarget(); } - } else if (mIsMinimized) { - // This was a tap, so no longer minimized - mMotionHelper.animateToClosestSnapTarget(); - setMinimizedStateInternal(false); } else if (mTouchState.isDoubleTap()) { // Expand to fullscreen if this is a double tap mMotionHelper.expandPip(); @@ -784,11 +694,12 @@ public class PipTouchHandler { }; /** - * Updates the current movement bounds based on whether the menu is currently visible. + * Updates the current movement bounds based on whether the menu is currently visible and + * resized. */ private void updateMovementBounds(int menuState) { boolean isMenuExpanded = menuState == MENU_STATE_FULL; - mMovementBounds = isMenuExpanded + mMovementBounds = isMenuExpanded && willResizeMenu() ? mExpandedMovementBounds : mNormalMovementBounds; mPipBoundsHandler.setMinEdgeSize( @@ -808,8 +719,11 @@ public class PipTouchHandler { * @return whether the menu will resize as a part of showing the full menu. */ private boolean willResizeMenu() { - return mExpandedBounds.width() != mNormalBounds.width() || - mExpandedBounds.height() != mNormalBounds.height(); + if (!mEnableResize) { + return false; + } + return mExpandedBounds.width() != mNormalBounds.width() + || mExpandedBounds.height() != mNormalBounds.height(); } public void dump(PrintWriter pw, String prefix) { @@ -821,14 +735,12 @@ public class PipTouchHandler { pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds); pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds); pw.println(innerPrefix + "mMenuState=" + mMenuState); - pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized); pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing); pw.println(innerPrefix + "mImeHeight=" + mImeHeight); pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing); pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight); pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction); - pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDimissDragToEdge); - pw.println(innerPrefix + "mEnableMinimize=" + ENABLE_MINIMIZE); + pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDismissDragToEdge); mSnapAlgorithm.dump(pw, innerPrefix); mTouchState.dump(pw, innerPrefix); mMotionHelper.dump(pw, innerPrefix); diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index 487c2533b0bb..cb1a218af954 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -50,7 +50,9 @@ import com.android.systemui.R; import com.android.systemui.UiOffloadThread; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.pip.BasePipManager; +import com.android.systemui.pip.PipAnimationController; import com.android.systemui.pip.PipBoundsHandler; +import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener; import com.android.systemui.shared.system.TaskStackChangeListener; @@ -66,7 +68,7 @@ import javax.inject.Singleton; * Manages the picture-in-picture (PIP) UI and states. */ @Singleton -public class PipManager implements BasePipManager { +public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitionCallback { private static final String TAG = "PipManager"; static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -91,7 +93,6 @@ public class PipManager implements BasePipManager { private static final int INVALID_RESOURCE_TYPE = -1; public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH = 0x1; - public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH = 0x2; /** * PIPed activity is playing a media and it can be paused. @@ -112,6 +113,7 @@ public class PipManager implements BasePipManager { private Context mContext; private PipBoundsHandler mPipBoundsHandler; + private PipTaskOrganizer mPipTaskOrganizer; private IActivityTaskManager mActivityTaskManager; private MediaSessionManager mMediaSessionManager; private int mState = STATE_NO_PIP; @@ -205,8 +207,7 @@ public class PipManager implements BasePipManager { } @Override - public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment) { + public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) { mHandler.post(() -> { // Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first. mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds, @@ -234,6 +235,8 @@ public class PipManager implements BasePipManager { mInitialized = true; mContext = context; mPipBoundsHandler = new PipBoundsHandler(context); + mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler); + mPipTaskOrganizer.registerPipTransitionCallback(this); mActivityTaskManager = ActivityTaskManager.getService(); ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); IntentFilter intentFilter = new IntentFilter(); @@ -279,9 +282,12 @@ public class PipManager implements BasePipManager { mMediaSessionManager = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE); + mPipTaskOrganizer.registerPipTransitionCallback(this); try { WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener); + ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer( + mPipTaskOrganizer, WINDOWING_MODE_PINNED); } catch (RemoteException e) { Log.e(TAG, "Failed to register pinned stack listener", e); } @@ -422,20 +428,13 @@ public class PipManager implements BasePipManager { case STATE_PIP_MENU: mCurrentPipBounds = mMenuModePipBounds; break; - case STATE_PIP: - mCurrentPipBounds = mPipBounds; - break; + case STATE_PIP: // fallthrough default: mCurrentPipBounds = mPipBounds; break; } - try { - int animationDurationMs = -1; - mActivityTaskManager.animateResizePinnedStack(mPinnedStackId, mCurrentPipBounds, - animationDurationMs); - } catch (RemoteException e) { - Log.e(TAG, "resizeStack failed", e); - } + mPipTaskOrganizer.resizePinnedStack( + mCurrentPipBounds, PipAnimationController.DURATION_DEFAULT_MS); } /** @@ -449,13 +448,6 @@ public class PipManager implements BasePipManager { } /** - * Returns the default PIP bound. - */ - public Rect getPipBounds() { - return mPipBounds; - } - - /** * Shows PIP menu UI by launching {@link PipMenuActivity}. It also locates the pinned * stack to the centered PIP bound {@link R.config_centeredPictureInPictureBounds}. */ @@ -692,18 +684,27 @@ public class PipManager implements BasePipManager { // If PIPed activity is launched again by Launcher or intent, make it fullscreen. movePipToFullscreen(); } + }; - @Override - public void onPinnedStackAnimationEnded() { - if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()"); + @Override + public void onPipTransitionStarted() { } - switch (getState()) { - case STATE_PIP_MENU: - showPipMenu(); - break; - } + @Override + public void onPipTransitionFinished() { + onPipTransitionFinishedOrCanceled(); + } + + @Override + public void onPipTransitionCanceled() { + onPipTransitionFinishedOrCanceled(); + } + + private void onPipTransitionFinishedOrCanceled() { + if (DEBUG) Log.d(TAG, "onPipTransitionFinishedOrCanceled()"); + if (getState() == STATE_PIP_MENU) { + showPipMenu(); } - }; + } /** * A listener interface to receive notification on changes in PIP. diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java index fb89ed264628..bfac85bd4c10 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java @@ -37,8 +37,8 @@ import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.UiEventLoggerImpl; import com.android.systemui.R; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.plugins.qs.QS; @@ -86,6 +86,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene private int mY; private boolean mOpening; private boolean mIsShowingNavBackdrop; + private UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); @Inject public QSCustomizer(Context context, AttributeSet attrs, @@ -187,7 +188,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene int containerLocation[] = findViewById(R.id.customize_container).getLocationOnScreen(); mX = x - containerLocation[0]; mY = y - containerLocation[1]; - MetricsLogger.visible(getContext(), MetricsProto.MetricsEvent.QS_EDIT); + mUiEventLogger.log(QSEditEvent.QS_EDIT_OPEN); isShown = true; mOpening = true; setTileSpecs(); @@ -224,7 +225,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene public void hide() { final boolean animate = mScreenLifecycle.getScreenState() != ScreenLifecycle.SCREEN_OFF; if (isShown) { - MetricsLogger.hidden(getContext(), MetricsProto.MetricsEvent.QS_EDIT); + mUiEventLogger.log(QSEditEvent.QS_EDIT_CLOSED); isShown = false; mToolbar.dismissPopupMenus(); setCustomizing(false); @@ -258,7 +259,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case MENU_RESET: - MetricsLogger.action(getContext(), MetricsProto.MetricsEvent.ACTION_QS_EDIT_RESET); + mUiEventLogger.log(QSEditEvent.QS_EDIT_RESET); reset(); break; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSEditEvent.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/QSEditEvent.kt new file mode 100644 index 000000000000..ff8ddec8397a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSEditEvent.kt @@ -0,0 +1,38 @@ +/* + * 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.systemui.qs.customize + +import com.android.internal.logging.UiEvent +import com.android.internal.logging.UiEventLogger + +enum class QSEditEvent(private val _id: Int) : UiEventLogger.UiEventEnum { + + @UiEvent(doc = "Tile removed from current tiles") + QS_EDIT_REMOVE(210), + @UiEvent(doc = "Tile added to current tiles") + QS_EDIT_ADD(211), + @UiEvent(doc = "Tile moved") + QS_EDIT_MOVE(212), + @UiEvent(doc = "QS customizer open") + QS_EDIT_OPEN(213), + @UiEvent(doc = "QS customizer closed") + QS_EDIT_CLOSED(214), + @UiEvent(doc = "QS tiles reset") + QS_EDIT_RESET(215); + + override fun getId() = _id +}
\ No newline at end of file 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 3afc46045a77..58de95d7ed6d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -40,8 +40,8 @@ import androidx.recyclerview.widget.RecyclerView.ItemDecoration; import androidx.recyclerview.widget.RecyclerView.State; import androidx.recyclerview.widget.RecyclerView.ViewHolder; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.UiEventLoggerImpl; import com.android.systemui.R; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.customize.TileAdapter.Holder; @@ -92,6 +92,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta private int mAccessibilityFromIndex; private CharSequence mAccessibilityFromLabel; private QSTileHost mHost; + private UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); public TileAdapter(Context context) { mContext = context; @@ -436,20 +437,11 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta move(from, to, mTiles); updateDividerLocations(); if (to >= mEditIndex) { - MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_REMOVE_SPEC, - strip(mTiles.get(to))); - MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_REMOVE, - from); + mUiEventLogger.log(QSEditEvent.QS_EDIT_REMOVE, 0, strip(mTiles.get(to))); } else if (from >= mEditIndex) { - MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_ADD_SPEC, - strip(mTiles.get(to))); - MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_ADD, - to); + mUiEventLogger.log(QSEditEvent.QS_EDIT_ADD, 0, strip(mTiles.get(to))); } else { - MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_MOVE_SPEC, - strip(mTiles.get(to))); - MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_MOVE, - to); + mUiEventLogger.log(QSEditEvent.QS_EDIT_MOVE, 0, strip(mTiles.get(to))); } saveSpecs(mHost); return true; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index ebf45a66a23a..9f7b84aa62c1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -53,6 +53,7 @@ import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.SysUIToast; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.plugins.qs.QSTile.BooleanState; @@ -79,6 +80,7 @@ public class DndTile extends QSTileImpl<BooleanState> { private final ZenModeController mController; private final DndDetailAdapter mDetailAdapter; private final ActivityStarter mActivityStarter; + private final SharedPreferences mSharedPreferences; private final BroadcastDispatcher mBroadcastDispatcher; private boolean mListening; @@ -87,10 +89,12 @@ public class DndTile extends QSTileImpl<BooleanState> { @Inject public DndTile(QSHost host, ZenModeController zenModeController, - ActivityStarter activityStarter, BroadcastDispatcher broadcastDispatcher) { + ActivityStarter activityStarter, BroadcastDispatcher broadcastDispatcher, + @Main SharedPreferences sharedPreferences) { super(host); mController = zenModeController; mActivityStarter = activityStarter; + mSharedPreferences = sharedPreferences; mDetailAdapter = new DndDetailAdapter(); mBroadcastDispatcher = broadcastDispatcher; broadcastDispatcher.registerReceiver(mReceiver, new IntentFilter(ACTION_SET_VISIBLE)); @@ -111,16 +115,16 @@ public class DndTile extends QSTileImpl<BooleanState> { Prefs.putBoolean(context, Prefs.Key.DND_TILE_VISIBLE, visible); } - public static boolean isVisible(Context context) { - return Prefs.getBoolean(context, Prefs.Key.DND_TILE_VISIBLE, false /* defaultValue */); + public static boolean isVisible(SharedPreferences prefs) { + return prefs.getBoolean(Prefs.Key.DND_TILE_VISIBLE, false /* defaultValue */); } public static void setCombinedIcon(Context context, boolean combined) { Prefs.putBoolean(context, Prefs.Key.DND_TILE_COMBINED_ICON, combined); } - public static boolean isCombinedIcon(Context context) { - return Prefs.getBoolean(context, Prefs.Key.DND_TILE_COMBINED_ICON, + public static boolean isCombinedIcon(SharedPreferences sharedPreferences) { + return sharedPreferences.getBoolean(Prefs.Key.DND_TILE_COMBINED_ICON, false /* defaultValue */); } @@ -301,7 +305,7 @@ public class DndTile extends QSTileImpl<BooleanState> { @Override public boolean isAvailable() { - return isVisible(mContext); + return isVisible(mSharedPreferences); } private final OnSharedPreferenceChangeListener mPrefListener diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java index 2557226d07bd..5dcb4e3b1fb9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java @@ -16,9 +16,9 @@ package com.android.systemui.qs.tiles; -import android.content.Context; import android.content.Intent; import android.content.res.Configuration; +import android.content.res.Resources; import android.provider.Settings; import android.service.quicksettings.Tile; import android.widget.Switch; @@ -81,11 +81,11 @@ public class RotationLockTile extends QSTileImpl<BooleanState> { } public static boolean isCurrentOrientationLockPortrait(RotationLockController controller, - Context context) { + Resources resources) { int lockOrientation = controller.getRotationLockOrientation(); if (lockOrientation == Configuration.ORIENTATION_UNDEFINED) { // Freely rotating device; use current rotation - return context.getResources().getConfiguration().orientation + return resources.getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE; } else { return lockOrientation != Configuration.ORIENTATION_LANDSCAPE; 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 39bfd5a8b6db..f1695017a883 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -206,7 +206,7 @@ public class WifiTile extends QSTileImpl<SignalState> { state.icon = ResourceIcon.get(cb.wifiSignalIconId); state.label = removeDoubleQuotes(cb.ssid); } else if (wifiNotConnected) { - state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_disconnected); + state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK); state.label = r.getString(R.string.quick_settings_wifi_label); } else { state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK); diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 34cad51e1c9f..1cd63881a700 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -61,9 +61,11 @@ import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.util.ScreenshotHelper; import com.android.systemui.Dumpable; import com.android.systemui.model.SysUiState; +import com.android.systemui.pip.PipAnimationController; import com.android.systemui.pip.PipUI; import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; import com.android.systemui.shared.recents.IOverviewProxy; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; @@ -388,6 +390,32 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } + @Override + public void notifySwipeToHomeFinished() { + if (!verifyCaller("notifySwipeToHomeFinished")) { + return; + } + long token = Binder.clearCallingIdentity(); + try { + mPipUI.setPinnedStackAnimationType(PipAnimationController.ANIM_TYPE_ALPHA); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) { + if (!verifyCaller("setPinnedStackAnimationListener")) { + return; + } + long token = Binder.clearCallingIdentity(); + try { + mPipUI.setPinnedStackAnimationListener(listener); + } finally { + Binder.restoreCallingIdentity(token); + } + } + private boolean verifyCaller(String reason) { final int callerId = Binder.getCallingUserHandle().getIdentifier(); if (callerId != mCurrentBoundedUserId) { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index f06cd54f7756..3afd5dc31382 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -514,12 +514,17 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset 1, cornerScale, mFastOutSlowIn.getInterpolation(t / scalePct)); mScreenshotView.setScaleX(scale); mScreenshotView.setScaleY(scale); + } else { + mScreenshotView.setScaleX(cornerScale); + mScreenshotView.setScaleY(cornerScale); } if (t < xPositionPct) { float xCenter = MathUtils.lerp(startPos.x, finalPos.x, mFastOutSlowIn.getInterpolation(t / xPositionPct)); mScreenshotView.setX(xCenter - width * mScreenshotView.getScaleX() / 2f); + } else { + mScreenshotView.setX(finalPos.x - width * mScreenshotView.getScaleX() / 2f); } float yCenter = MathUtils.lerp(startPos.y, finalPos.y, mFastOutSlowIn.getInterpolation(t)); @@ -544,6 +549,10 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); + mScreenshotView.setScaleX(cornerScale); + mScreenshotView.setScaleY(cornerScale); + mScreenshotView.setX(finalPos.x - height * cornerScale / 2f); + mScreenshotView.setY(finalPos.y - height * cornerScale / 2f); Rect bounds = new Rect(); mScreenshotView.getBoundsOnScreen(bounds); mDismissButton.setX(bounds.right - mDismissButtonSize / 2f); diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index 56cdff43e255..27b799bc02a3 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -401,6 +401,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, return; } mMinimized = minimized; + WindowManagerProxy.applyPrimaryFocusable(mSplits, !mMinimized); mView.setMinimizedDockStack(minimized, getAnimDuration(), mHomeStackResizable); updateTouchable(); }); @@ -504,6 +505,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, final boolean wasMinimized = mMinimized; mMinimized = true; setHomeStackResizable(mSplits.mSecondary.isResizable()); + WindowManagerProxy.applyPrimaryFocusable(mSplits, false /* focusable */); if (!inSplitMode()) { // Wasn't in split-mode yet, so enter now. if (DEBUG) { @@ -521,6 +523,9 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, } void ensureNormalSplit() { + if (mMinimized) { + WindowManagerProxy.applyPrimaryFocusable(mSplits, true /* focusable */); + } if (!inSplitMode()) { // Wasn't in split-mode, so enter now. if (DEBUG) { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java index 76857337f73e..167c33abac6e 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java @@ -292,10 +292,23 @@ public class WindowManagerProxy { for (int i = freeHomeAndRecents.size() - 1; i >= 0; --i) { wct.setBounds(freeHomeAndRecents.get(i).token, null); } + // Reset focusable to true + wct.setFocusable(tiles.mPrimary.token, true /* focusable */); ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct, null /* organizer */); } catch (RemoteException e) { Log.w(TAG, "Failed to remove stack: " + e); } } + + static void applyPrimaryFocusable(SplitScreenTaskOrganizer splits, boolean focusable) { + try { + WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setFocusable(splits.mPrimary.token, focusable); + ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct, + null /* organizer */); + } catch (RemoteException e) { + Log.w(TAG, "Error setting focusability: " + e); + } + } } 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 500813318484..004b56b19418 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 @@ -28,6 +28,7 @@ import android.animation.ObjectAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.Notification; import android.app.NotificationChannel; import android.content.Context; import android.content.pm.PackageInfo; @@ -148,6 +149,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private int mNotificationMinHeight; private int mNotificationMinHeightLarge; private int mNotificationMinHeightMedia; + private int mNotificationMinHeightMessaging; private int mNotificationMaxHeight; private int mIncreasedPaddingBetweenElements; private int mNotificationLaunchHeight; @@ -630,10 +632,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView && expandedView.findViewById(com.android.internal.R.id.media_actions) != null; boolean showCompactMediaSeekbar = mMediaManager.getShowCompactMediaSeekbar(); + Class<? extends Notification.Style> style = + mEntry.getSbn().getNotification().getNotificationStyle(); + boolean isMessagingLayout = Notification.MessagingStyle.class.equals(style); + if (customView && beforeP && !mIsSummaryWithChildren) { minHeight = beforeN ? mNotificationMinHeightBeforeN : mNotificationMinHeightBeforeP; } else if (isMediaLayout && showCompactMediaSeekbar) { minHeight = mNotificationMinHeightMedia; + } else if (isMessagingLayout) { + minHeight = mNotificationMinHeightMessaging; } else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) { minHeight = mNotificationMinHeightLarge; } else { @@ -799,7 +807,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView // TODO: Move inflation logic out of this call if (mIsChildInGroup != isChildInGroup) { mIsChildInGroup = isChildInGroup; - if (mIsLowPriority) { + if (!isRemoved() && mIsLowPriority) { RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry); params.setUseLowPriority(mIsLowPriority); mRowContentBindStage.requestRebind(mEntry, null /* callback */); @@ -1568,13 +1576,15 @@ public class ExpandableNotificationRow extends ActivatableNotificationView // TODO: Move inflation logic out of this call and remove this method if (mNeedsRedaction != needsRedaction) { mNeedsRedaction = needsRedaction; - RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry); - if (needsRedaction) { - params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC); - } else { - params.freeContentViews(FLAG_CONTENT_VIEW_PUBLIC); + if (!isRemoved()) { + RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry); + if (needsRedaction) { + params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC); + } else { + params.freeContentViews(FLAG_CONTENT_VIEW_PUBLIC); + } + mRowContentBindStage.requestRebind(mEntry, null /* callback */); } - mRowContentBindStage.requestRebind(mEntry, null /* callback */); } } @@ -1635,6 +1645,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView R.dimen.notification_min_height_increased); mNotificationMinHeightMedia = NotificationUtils.getFontScaledHeight(mContext, R.dimen.notification_min_height_media); + mNotificationMinHeightMessaging = NotificationUtils.getFontScaledHeight(mContext, + R.dimen.notification_min_height_messaging); mNotificationMaxHeight = NotificationUtils.getFontScaledHeight(mContext, R.dimen.notification_max_height); mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index 60eda06dd3f9..bab7840e57d7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -20,6 +20,7 @@ import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; +import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED; import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC; import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED; import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS; @@ -218,7 +219,7 @@ public class NotificationConversationInfo extends LinearLayout implements // TODO: consider querying this earlier in the notification pipeline and passing it in LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery() .setPackage(mPackageName) - .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED) + .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED) .setShortcutIds(Arrays.asList(mConversationId)); List<ShortcutInfo> shortcuts = mLauncherApps.getShortcuts(query, mSbn.getUser()); if (shortcuts != null && !shortcuts.isEmpty()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java index 8729e04b2dec..f38d416a6f4c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java @@ -44,8 +44,9 @@ import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.NotificationShadeWindowBlurController; +import com.android.systemui.statusbar.PulseExpansionHandler; +import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -91,6 +92,7 @@ public class NotificationShadeWindowViewController { private boolean mExpandAnimationRunning; private NotificationStackScrollLayout mStackScrollLayout; private PhoneStatusBarView mStatusBarView; + private PhoneStatusBarTransitions mBarTransitions; private StatusBar mService; private DragDownHelper mDragDownHelper; private boolean mDoubleTapEnabled; @@ -98,6 +100,7 @@ public class NotificationShadeWindowViewController { private boolean mExpandingBelowNotch; private final DockManager mDockManager; private final NotificationPanelViewController mNotificationPanelViewController; + private final SuperStatusBarViewFactory mStatusBarViewFactory; // Used for determining view / touch intersection private int[] mTempLocation = new int[2]; @@ -124,8 +127,9 @@ public class NotificationShadeWindowViewController { ShadeController shadeController, DockManager dockManager, @Nullable NotificationShadeWindowBlurController blurController, - NotificationShadeWindowView statusBarWindowView, - NotificationPanelViewController notificationPanelViewController) { + NotificationShadeWindowView notificationShadeWindowView, + NotificationPanelViewController notificationPanelViewController, + SuperStatusBarViewFactory statusBarViewFactory) { mInjectionInflationController = injectionInflationController; mCoordinator = coordinator; mPulseExpansionHandler = pulseExpansionHandler; @@ -141,11 +145,12 @@ public class NotificationShadeWindowViewController { mDozeLog = dozeLog; mDozeParameters = dozeParameters; mCommandQueue = commandQueue; - mView = statusBarWindowView; + mView = notificationShadeWindowView; mShadeController = shadeController; mDockManager = dockManager; mNotificationPanelViewController = notificationPanelViewController; mBlurController = blurController; + mStatusBarViewFactory = statusBarViewFactory; // This view is not part of the newly inflated expanded status bar. mBrightnessMirror = mView.findViewById(R.id.brightness_mirror); @@ -440,8 +445,18 @@ public class NotificationShadeWindowViewController { } } + public PhoneStatusBarTransitions getBarTransitions() { + return mBarTransitions; + } + public void setStatusBarView(PhoneStatusBarView statusBarView) { mStatusBarView = statusBarView; + if (statusBarView != null && mStatusBarViewFactory != null) { + mBarTransitions = new PhoneStatusBarTransitions( + statusBarView, + mStatusBarViewFactory.getStatusBarWindowView() + .findViewById(R.id.status_bar_container)); + } } public void setService(StatusBar statusBar) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 1ab36c5e3760..14af466a2424 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -16,15 +16,18 @@ package com.android.systemui.statusbar.phone; -import android.app.ActivityManager; +import android.annotation.Nullable; import android.app.ActivityTaskManager; import android.app.AlarmManager; import android.app.AlarmManager.AlarmClockInfo; +import android.app.IActivityManager; import android.app.SynchronousUserSwitchObserver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.content.res.Resources; import android.media.AudioManager; import android.os.Handler; import android.os.RemoteException; @@ -33,13 +36,13 @@ import android.os.UserManager; import android.provider.Settings.Global; import android.service.notification.ZenModeConfig; import android.telecom.TelecomManager; -import android.telephony.TelephonyManager; import android.text.format.DateFormat; import android.util.Log; -import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.qualifiers.DisplayId; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.qs.tiles.RotationLockTile; @@ -61,10 +64,13 @@ import com.android.systemui.statusbar.policy.RotationLockController.RotationLock import com.android.systemui.statusbar.policy.SensorPrivacyController; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.util.time.DateFormatUtil; import java.util.Locale; import java.util.concurrent.Executor; +import javax.inject.Inject; + /** * This class contains all of the policy about which icons are installed in the status bar at boot * time. It goes through the normal API for icons, even though it probably strictly doesn't need to. @@ -82,7 +88,7 @@ public class PhoneStatusBarPolicy private static final String TAG = "PhoneStatusBarPolicy"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - public static final int LOCATION_STATUS_ICON_ID = + static final int LOCATION_STATUS_ICON_ID = com.android.internal.R.drawable.perm_group_location; private final String mSlotCast; @@ -97,20 +103,26 @@ public class PhoneStatusBarPolicy private final String mSlotHeadset; private final String mSlotDataSaver; private final String mSlotLocation; - private final String mSlotMicrophone; - private final String mSlotCamera; private final String mSlotSensorsOff; private final String mSlotScreenRecord; + private final int mDisplayId; + private final SharedPreferences mSharedPreferences; + private final DateFormatUtil mDateFormatUtil; + private final TelecomManager mTelecomManager; + private final AudioManager mAudioManager; - private final Context mContext; private final Handler mHandler = new Handler(); private final CastController mCast; private final HotspotController mHotspot; private final NextAlarmController mNextAlarmController; private final AlarmManager mAlarmManager; private final UserInfoController mUserInfoController; + private final IActivityManager mIActivityManager; private final UserManager mUserManager; private final StatusBarIconController mIconController; + private final CommandQueue mCommandQueue; + private final BroadcastDispatcher mBroadcastDispatcher; + private final Resources mResources; private final RotationLockController mRotationLockController; private final DataSaverController mDataSaver; private final ZenModeController mZenController; @@ -121,10 +133,6 @@ public class PhoneStatusBarPolicy private final SensorPrivacyController mSensorPrivacyController; private final RecordingController mRecordingController; - // Assume it's all good unless we hear otherwise. We don't always seem - // to get broadcasts that it *is* there. - int mSimState = TelephonyManager.SIM_STATE_READY; - private boolean mZenVisible; private boolean mVolumeVisible; private boolean mCurrentUserSetup; @@ -134,47 +142,70 @@ public class PhoneStatusBarPolicy private BluetoothController mBluetooth; private AlarmManager.AlarmClockInfo mNextAlarm; - public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController, + @Inject + public PhoneStatusBarPolicy(StatusBarIconController iconController, CommandQueue commandQueue, BroadcastDispatcher broadcastDispatcher, - @UiBackground Executor uiBgExecutor) { - mContext = context; + @UiBackground Executor uiBgExecutor, @Main Resources resources, + CastController castController, HotspotController hotspotController, + BluetoothController bluetoothController, NextAlarmController nextAlarmController, + UserInfoController userInfoController, RotationLockController rotationLockController, + DataSaverController dataSaverController, ZenModeController zenModeController, + DeviceProvisionedController deviceProvisionedController, + KeyguardStateController keyguardStateController, + LocationController locationController, + SensorPrivacyController sensorPrivacyController, IActivityManager iActivityManager, + AlarmManager alarmManager, UserManager userManager, AudioManager audioManager, + RecordingController recordingController, + @Nullable TelecomManager telecomManager, @DisplayId int displayId, + @Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil) { mIconController = iconController; - mCast = Dependency.get(CastController.class); - mHotspot = Dependency.get(HotspotController.class); - mBluetooth = Dependency.get(BluetoothController.class); - mNextAlarmController = Dependency.get(NextAlarmController.class); - mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - mUserInfoController = Dependency.get(UserInfoController.class); - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - mRotationLockController = Dependency.get(RotationLockController.class); - mDataSaver = Dependency.get(DataSaverController.class); - mZenController = Dependency.get(ZenModeController.class); - mProvisionedController = Dependency.get(DeviceProvisionedController.class); - mKeyguardStateController = Dependency.get(KeyguardStateController.class); - mLocationController = Dependency.get(LocationController.class); - mSensorPrivacyController = Dependency.get(SensorPrivacyController.class); - mRecordingController = Dependency.get(RecordingController.class); + mCommandQueue = commandQueue; + mBroadcastDispatcher = broadcastDispatcher; + mResources = resources; + mCast = castController; + mHotspot = hotspotController; + mBluetooth = bluetoothController; + mNextAlarmController = nextAlarmController; + mAlarmManager = alarmManager; + mUserInfoController = userInfoController; + mIActivityManager = iActivityManager; + mUserManager = userManager; + mRotationLockController = rotationLockController; + mDataSaver = dataSaverController; + mZenController = zenModeController; + mProvisionedController = deviceProvisionedController; + mKeyguardStateController = keyguardStateController; + mLocationController = locationController; + mSensorPrivacyController = sensorPrivacyController; + mRecordingController = recordingController; mUiBgExecutor = uiBgExecutor; - - mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast); - mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot); - mSlotBluetooth = context.getString(com.android.internal.R.string.status_bar_bluetooth); - mSlotTty = context.getString(com.android.internal.R.string.status_bar_tty); - mSlotZen = context.getString(com.android.internal.R.string.status_bar_zen); - mSlotVolume = context.getString(com.android.internal.R.string.status_bar_volume); - mSlotAlarmClock = context.getString(com.android.internal.R.string.status_bar_alarm_clock); - mSlotManagedProfile = context.getString( + mAudioManager = audioManager; + mTelecomManager = telecomManager; + + mSlotCast = resources.getString(com.android.internal.R.string.status_bar_cast); + mSlotHotspot = resources.getString(com.android.internal.R.string.status_bar_hotspot); + mSlotBluetooth = resources.getString(com.android.internal.R.string.status_bar_bluetooth); + mSlotTty = resources.getString(com.android.internal.R.string.status_bar_tty); + mSlotZen = resources.getString(com.android.internal.R.string.status_bar_zen); + mSlotVolume = resources.getString(com.android.internal.R.string.status_bar_volume); + mSlotAlarmClock = resources.getString(com.android.internal.R.string.status_bar_alarm_clock); + mSlotManagedProfile = resources.getString( com.android.internal.R.string.status_bar_managed_profile); - mSlotRotate = context.getString(com.android.internal.R.string.status_bar_rotate); - mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset); - mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver); - mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location); - mSlotMicrophone = context.getString(com.android.internal.R.string.status_bar_microphone); - mSlotCamera = context.getString(com.android.internal.R.string.status_bar_camera); - mSlotSensorsOff = context.getString(com.android.internal.R.string.status_bar_sensors_off); - mSlotScreenRecord = context.getString( + mSlotRotate = resources.getString(com.android.internal.R.string.status_bar_rotate); + mSlotHeadset = resources.getString(com.android.internal.R.string.status_bar_headset); + mSlotDataSaver = resources.getString(com.android.internal.R.string.status_bar_data_saver); + mSlotLocation = resources.getString(com.android.internal.R.string.status_bar_location); + mSlotSensorsOff = resources.getString(com.android.internal.R.string.status_bar_sensors_off); + mSlotScreenRecord = resources.getString( com.android.internal.R.string.status_bar_screen_record); + mDisplayId = displayId; + mSharedPreferences = sharedPreferences; + mDateFormatUtil = dateFormatUtil; + } + + /** Initialize the object after construction. */ + public void init() { // listen for broadcasts IntentFilter filter = new IntentFilter(); filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); @@ -185,11 +216,11 @@ public class PhoneStatusBarPolicy filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE); filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); - broadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler); + mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler); // listen for user / profile change. try { - ActivityManager.getService().registerUserSwitchObserver(mUserSwitchListener, TAG); + mIActivityManager.registerUserSwitchObserver(mUserSwitchListener, TAG); } catch (RemoteException e) { // Ignore } @@ -219,26 +250,26 @@ public class PhoneStatusBarPolicy // hotspot mIconController.setIcon(mSlotHotspot, R.drawable.stat_sys_hotspot, - mContext.getString(R.string.accessibility_status_bar_hotspot)); + mResources.getString(R.string.accessibility_status_bar_hotspot)); mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled()); // managed profile mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status, - mContext.getString(R.string.accessibility_managed_profile)); + mResources.getString(R.string.accessibility_managed_profile)); mIconController.setIconVisibility(mSlotManagedProfile, mManagedProfileIconVisible); // data saver mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver, - context.getString(R.string.accessibility_data_saver_on)); + mResources.getString(R.string.accessibility_data_saver_on)); mIconController.setIconVisibility(mSlotDataSaver, false); mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID, - mContext.getString(R.string.accessibility_location_active)); + mResources.getString(R.string.accessibility_location_active)); mIconController.setIconVisibility(mSlotLocation, false); // sensors off mIconController.setIcon(mSlotSensorsOff, R.drawable.stat_sys_sensors_off, - mContext.getString(R.string.accessibility_sensors_off_active)); + mResources.getString(R.string.accessibility_sensors_off_active)); mIconController.setIconVisibility(mSlotSensorsOff, mSensorPrivacyController.isSensorPrivacyEnabled()); @@ -259,7 +290,7 @@ public class PhoneStatusBarPolicy mLocationController.addCallback(this); mRecordingController.addCallback(this); - commandQueue.addCallback(this); + mCommandQueue.addCallback(this); } @Override @@ -284,51 +315,17 @@ public class PhoneStatusBarPolicy private String buildAlarmContentDescription() { if (mNextAlarm == null) { - return mContext.getString(R.string.status_bar_alarm); + return mResources.getString(R.string.status_bar_alarm); } - return formatNextAlarm(mNextAlarm, mContext); - } - private static String formatNextAlarm(AlarmManager.AlarmClockInfo info, Context context) { - if (info == null) { - return ""; - } - String skeleton = DateFormat.is24HourFormat( - context, ActivityManager.getCurrentUser()) ? "EHm" : "Ehma"; + String skeleton = mDateFormatUtil.is24HourFormat() ? "EHm" : "Ehma"; String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); - String dateString = DateFormat.format(pattern, info.getTriggerTime()).toString(); + String dateString = DateFormat.format(pattern, mNextAlarm.getTriggerTime()).toString(); - return context.getString(R.string.accessibility_quick_settings_alarm, dateString); - } - - private final void updateSimState(Intent intent) { - String stateExtra = intent.getStringExtra(Intent.EXTRA_SIM_STATE); - if (Intent.SIM_STATE_ABSENT.equals(stateExtra)) { - mSimState = TelephonyManager.SIM_STATE_READY; - } else if (Intent.SIM_STATE_CARD_IO_ERROR.equals(stateExtra)) { - mSimState = TelephonyManager.SIM_STATE_CARD_IO_ERROR; - } else if (Intent.SIM_STATE_CARD_RESTRICTED.equals(stateExtra)) { - mSimState = TelephonyManager.SIM_STATE_CARD_RESTRICTED; - } else if (Intent.SIM_STATE_READY.equals(stateExtra)) { - mSimState = TelephonyManager.SIM_STATE_READY; - } else if (Intent.SIM_STATE_LOCKED.equals(stateExtra)) { - final String lockedReason = - intent.getStringExtra(Intent.EXTRA_SIM_LOCKED_REASON); - if (Intent.SIM_LOCKED_ON_PIN.equals(lockedReason)) { - mSimState = TelephonyManager.SIM_STATE_PIN_REQUIRED; - } else if (Intent.SIM_LOCKED_ON_PUK.equals(lockedReason)) { - mSimState = TelephonyManager.SIM_STATE_PUK_REQUIRED; - } else { - mSimState = TelephonyManager.SIM_STATE_NETWORK_LOCKED; - } - } else { - mSimState = TelephonyManager.SIM_STATE_UNKNOWN; - } + return mResources.getString(R.string.accessibility_quick_settings_alarm, dateString); } private final void updateVolumeZen() { - AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); - boolean zenVisible = false; int zenIconId = 0; String zenDescription = null; @@ -338,29 +335,29 @@ public class PhoneStatusBarPolicy String volumeDescription = null; int zen = mZenController.getZen(); - if (DndTile.isVisible(mContext) || DndTile.isCombinedIcon(mContext)) { + if (DndTile.isVisible(mSharedPreferences) || DndTile.isCombinedIcon(mSharedPreferences)) { zenVisible = zen != Global.ZEN_MODE_OFF; zenIconId = R.drawable.stat_sys_dnd; - zenDescription = mContext.getString(R.string.quick_settings_dnd_label); + zenDescription = mResources.getString(R.string.quick_settings_dnd_label); } else if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) { zenVisible = true; zenIconId = R.drawable.stat_sys_dnd; - zenDescription = mContext.getString(R.string.interruption_level_none); + zenDescription = mResources.getString(R.string.interruption_level_none); } else if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) { zenVisible = true; zenIconId = R.drawable.stat_sys_dnd; - zenDescription = mContext.getString(R.string.interruption_level_priority); + zenDescription = mResources.getString(R.string.interruption_level_priority); } if (!ZenModeConfig.isZenOverridingRinger(zen, mZenController.getConsolidatedPolicy())) { - if (audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) { + if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) { volumeVisible = true; volumeIconId = R.drawable.stat_sys_ringer_vibrate; - volumeDescription = mContext.getString(R.string.accessibility_ringer_vibrate); - } else if (audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) { + volumeDescription = mResources.getString(R.string.accessibility_ringer_vibrate); + } else if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) { volumeVisible = true; volumeIconId = R.drawable.stat_sys_ringer_silent; - volumeDescription = mContext.getString(R.string.accessibility_ringer_silent); + volumeDescription = mResources.getString(R.string.accessibility_ringer_silent); } } @@ -395,13 +392,13 @@ public class PhoneStatusBarPolicy private final void updateBluetooth() { int iconId = R.drawable.stat_sys_data_bluetooth_connected; String contentDescription = - mContext.getString(R.string.accessibility_quick_settings_bluetooth_on); + mResources.getString(R.string.accessibility_quick_settings_bluetooth_on); boolean bluetoothVisible = false; if (mBluetooth != null) { if (mBluetooth.isBluetoothConnected() && (mBluetooth.isBluetoothAudioActive() || !mBluetooth.isBluetoothAudioProfileOnly())) { - contentDescription = mContext.getString( + contentDescription = mResources.getString( R.string.accessibility_bluetooth_connected); bluetoothVisible = mBluetooth.isBluetoothEnabled(); } @@ -412,12 +409,10 @@ public class PhoneStatusBarPolicy } private final void updateTTY() { - TelecomManager telecomManager = - (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); - if (telecomManager == null) { + if (mTelecomManager == null) { updateTTY(TelecomManager.TTY_MODE_OFF); } else { - updateTTY(telecomManager.getCurrentTtyMode()); + updateTTY(mTelecomManager.getCurrentTtyMode()); } } @@ -430,7 +425,7 @@ public class PhoneStatusBarPolicy // TTY is on if (DEBUG) Log.v(TAG, "updateTTY: set TTY on"); mIconController.setIcon(mSlotTty, R.drawable.stat_sys_tty_mode, - mContext.getString(R.string.accessibility_tty_enabled)); + mResources.getString(R.string.accessibility_tty_enabled)); mIconController.setIconVisibility(mSlotTty, true); } else { // TTY is off @@ -452,7 +447,7 @@ public class PhoneStatusBarPolicy mHandler.removeCallbacks(mRemoveCastIconRunnable); if (isCasting && !mRecordingController.isRecording()) { // screen record has its own icon mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast, - mContext.getString(R.string.accessibility_casting)); + mResources.getString(R.string.accessibility_casting)); mIconController.setIconVisibility(mSlotCast, true); } else { // don't turn off the screen-record icon for a few seconds, just to make sure the user @@ -478,7 +473,7 @@ public class PhoneStatusBarPolicy showIcon = true; mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status, - mContext.getString(R.string.accessibility_managed_profile)); + mResources.getString(R.string.accessibility_managed_profile)); } else { showIcon = false; } @@ -545,7 +540,7 @@ public class PhoneStatusBarPolicy @Override public void appTransitionStarting(int displayId, long startTime, long duration, boolean forced) { - if (mContext.getDisplayId() == displayId) { + if (mDisplayId == displayId) { updateManagedProfile(); } } @@ -567,14 +562,14 @@ public class PhoneStatusBarPolicy @Override public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) { boolean portrait = RotationLockTile.isCurrentOrientationLockPortrait( - mRotationLockController, mContext); + mRotationLockController, mResources); if (rotationLocked) { if (portrait) { mIconController.setIcon(mSlotRotate, R.drawable.stat_sys_rotate_portrait, - mContext.getString(R.string.accessibility_rotation_lock_on_portrait)); + mResources.getString(R.string.accessibility_rotation_lock_on_portrait)); } else { mIconController.setIcon(mSlotRotate, R.drawable.stat_sys_rotate_landscape, - mContext.getString(R.string.accessibility_rotation_lock_on_landscape)); + mResources.getString(R.string.accessibility_rotation_lock_on_landscape)); } mIconController.setIconVisibility(mSlotRotate, true); } else { @@ -586,7 +581,7 @@ public class PhoneStatusBarPolicy boolean connected = intent.getIntExtra("state", 0) != 0; boolean hasMic = intent.getIntExtra("microphone", 0) != 0; if (connected) { - String contentDescription = mContext.getString(hasMic + String contentDescription = mResources.getString(hasMic ? R.string.accessibility_status_bar_headset : R.string.accessibility_status_bar_headphones); mIconController.setIcon(mSlotHeadset, hasMic ? R.drawable.stat_sys_headset_mic @@ -630,7 +625,6 @@ public class PhoneStatusBarPolicy if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) { break; } - updateSimState(intent); break; case TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED: updateTTY(intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java index e8bc2f58adb4..2052ee6cdac5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java @@ -29,23 +29,21 @@ public final class PhoneStatusBarTransitions extends BarTransitions { private static final float ICON_ALPHA_WHEN_LIGHTS_OUT_BATTERY_CLOCK = 0.5f; private static final float ICON_ALPHA_WHEN_LIGHTS_OUT_NON_BATTERY_CLOCK = 0; - private final PhoneStatusBarView mView; private final float mIconAlphaWhenOpaque; - private View mLeftSide, mStatusIcons, mBattery, mClock; + private View mLeftSide, mStatusIcons, mBattery; private Animator mCurrentAnimation; - public PhoneStatusBarTransitions(PhoneStatusBarView view) { - super(view, R.drawable.status_background); - mView = view; - final Resources res = mView.getContext().getResources(); + /** + * @param backgroundView view to apply the background drawable + */ + public PhoneStatusBarTransitions(PhoneStatusBarView statusBarView, View backgroundView) { + super(backgroundView, R.drawable.status_background); + final Resources res = statusBarView.getContext().getResources(); mIconAlphaWhenOpaque = res.getFraction(R.dimen.status_bar_icon_drawing_alpha, 1, 1); - } - - public void init() { - mLeftSide = mView.findViewById(R.id.status_bar_left_side); - mStatusIcons = mView.findViewById(R.id.statusIcons); - mBattery = mView.findViewById(R.id.battery); + mLeftSide = statusBarView.findViewById(R.id.status_bar_left_side); + mStatusIcons = statusBarView.findViewById(R.id.statusIcons); + mBattery = statusBarView.findViewById(R.id.battery); applyModeBackground(-1, getMode(), false /*animate*/); applyMode(getMode(), false /*animate*/); } 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 b949e3a5080c..1359f74d0b3a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -53,7 +53,6 @@ public class PhoneStatusBarView extends PanelBar { StatusBar mBar; boolean mIsFullyOpenedPanel = false; - private final PhoneStatusBarTransitions mBarTransitions; private ScrimController mScrimController; private float mMinFraction; private Runnable mHideExpandedRunnable = new Runnable() { @@ -83,15 +82,9 @@ public class PhoneStatusBarView extends PanelBar { public PhoneStatusBarView(Context context, AttributeSet attrs) { super(context, attrs); - - mBarTransitions = new PhoneStatusBarTransitions(this); mCommandQueue = Dependency.get(CommandQueue.class); } - public BarTransitions getBarTransitions() { - return mBarTransitions; - } - public void setBar(StatusBar bar) { mBar = bar; } @@ -102,7 +95,6 @@ public class PhoneStatusBarView extends PanelBar { @Override public void onFinishInflate() { - mBarTransitions.init(); mBattery = findViewById(R.id.battery); mCutoutSpace = findViewById(R.id.cutout_space_view); mCenterIconSpace = findViewById(R.id.centered_icon_area); 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 0d3b09a634e4..b620d17541b2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -676,6 +676,7 @@ public class StatusBar extends SystemUI implements DemoMode, KeyguardDismissUtil keyguardDismissUtil, ExtensionController extensionController, UserInfoControllerImpl userInfoControllerImpl, + PhoneStatusBarPolicy phoneStatusBarPolicy, DismissCallbackRegistry dismissCallbackRegistry, StatusBarTouchableRegionManager statusBarTouchableRegionManager) { super(context); @@ -751,6 +752,7 @@ public class StatusBar extends SystemUI implements DemoMode, mKeyguardDismissUtil = keyguardDismissUtil; mExtensionController = extensionController; mUserInfoControllerImpl = userInfoControllerImpl; + mIconPolicy = phoneStatusBarPolicy; mDismissCallbackRegistry = dismissCallbackRegistry; mBubbleExpandListener = @@ -875,8 +877,7 @@ public class StatusBar extends SystemUI implements DemoMode, // end old BaseStatusBar.start(). // Lastly, call to the icon policy to install/update all the icons. - mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController, mCommandQueue, - mBroadcastDispatcher, mUiBgExecutor); + mIconPolicy.init(); mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController); mKeyguardStateController.addCallback(this); @@ -2305,13 +2306,14 @@ public class StatusBar extends SystemUI implements DemoMode, } protected BarTransitions getStatusBarTransitions() { - return mStatusBarView.getBarTransitions(); + return mNotificationShadeWindowViewController.getBarTransitions(); } void checkBarModes() { if (mDemoMode) return; - if (mStatusBarView != null) checkBarMode(mStatusBarMode, mStatusBarWindowState, - getStatusBarTransitions()); + if (mNotificationShadeWindowViewController != null) { + checkBarMode(mStatusBarMode, mStatusBarWindowState, getStatusBarTransitions()); + } mNavigationBarController.checkNavBarModes(mDisplayId); mNoAnimationOnNextBarModeChange = false; } @@ -2329,8 +2331,9 @@ public class StatusBar extends SystemUI implements DemoMode, } private void finishBarAnimations() { - if (mStatusBarView != null) { - mStatusBarView.getBarTransitions().finishAnimations(); + if (mNotificationShadeWindowController != null + && mNotificationShadeWindowViewController.getBarTransitions() != null) { + mNotificationShadeWindowViewController.getBarTransitions().finishAnimations(); } mNavigationBarController.finishBarAnimations(mDisplayId); } @@ -2396,12 +2399,11 @@ public class StatusBar extends SystemUI implements DemoMode, pw.print(" mDozing="); pw.println(mDozing); pw.print(" mWallpaperSupported= "); pw.println(mWallpaperSupported); - if (mStatusBarView != null) { - dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions()); - } pw.println(" StatusBarWindowView: "); if (mNotificationShadeWindowViewController != null) { mNotificationShadeWindowViewController.dump(fd, pw, args); + dumpBarTransitions(pw, "PhoneStatusBarTransitions", + mNotificationShadeWindowViewController.getBarTransitions()); } pw.println(" mMediaManager: "); @@ -3004,8 +3006,10 @@ public class StatusBar extends SystemUI implements DemoMode, -1; if (barMode != -1) { boolean animate = true; - if (mStatusBarView != null) { - mStatusBarView.getBarTransitions().transitionTo(barMode, animate); + if (mNotificationShadeWindowController != null + && mNotificationShadeWindowViewController.getBarTransitions() != null) { + mNotificationShadeWindowViewController.getBarTransitions().transitionTo( + barMode, animate); } mNavigationBarController.transitionTo(mDisplayId, barMode, animate); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index e64f8214bb71..0a4fdc9eb9bc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -79,6 +79,7 @@ import com.android.systemui.statusbar.phone.LockscreenLockIconController; import com.android.systemui.statusbar.phone.LockscreenWallpaper; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationShadeWindowController; +import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; @@ -193,6 +194,7 @@ public interface StatusBarPhoneModule { KeyguardDismissUtil keyguardDismissUtil, ExtensionController extensionController, UserInfoControllerImpl userInfoControllerImpl, + PhoneStatusBarPolicy phoneStatusBarPolicy, DismissCallbackRegistry dismissCallbackRegistry, StatusBarTouchableRegionManager statusBarTouchableRegionManager) { return new StatusBar( @@ -269,6 +271,7 @@ public interface StatusBarPhoneModule { keyguardDismissUtil, extensionController, userInfoControllerImpl, + phoneStatusBarPolicy, dismissCallbackRegistry, statusBarTouchableRegionManager); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java index d0904049d85a..7d532a88caac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java @@ -56,8 +56,9 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof private int mHotspotState; private volatile int mNumConnectedDevices; - private volatile boolean mIsTetheringSupported; - private volatile boolean mHasTetherableWifiRegexs; + // Assume tethering is available until told otherwise + private volatile boolean mIsTetheringSupported = true; + private volatile boolean mHasTetherableWifiRegexs = true; private boolean mWaitingForTerminalState; private TetheringManager.TetheringEventCallback mTetheringCallback = @@ -97,6 +98,15 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof new HandlerExecutor(backgroundHandler), mTetheringCallback); } + /** + * Whether hotspot is currently supported. + * + * This will return {@code true} immediately on creation of the controller, but may be updated + * later. Callbacks from this controllers will notify if the state changes. + * + * @return {@code true} if hotspot is supported (or we haven't been told it's not) + * @see #addCallback + */ @Override public boolean isHotspotSupported() { return mIsTetheringSupported && mHasTetherableWifiRegexs diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java index 8bd0f2cadb2b..7c963869ed47 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java @@ -88,15 +88,16 @@ public class WifiSignalController extends boolean wifiVisible = mCurrentState.enabled && ((mCurrentState.connected && mCurrentState.inetCondition == 1) || !mHasMobileData || visibleWhenEnabled); - String wifiDesc = wifiVisible ? mCurrentState.ssid : null; + String wifiDesc = mCurrentState.connected ? mCurrentState.ssid : null; boolean ssidPresent = wifiVisible && mCurrentState.ssid != null; String contentDescription = getTextIfExists(getContentDescription()).toString(); if (mCurrentState.inetCondition == 0) { contentDescription += ("," + mContext.getString(R.string.data_connection_no_internet)); } IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription); - IconState qsIcon = new IconState(mCurrentState.connected, getQsCurrentIconId(), - contentDescription); + IconState qsIcon = new IconState(mCurrentState.connected, + mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected + : getQsCurrentIconId(), contentDescription); callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon, ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut, wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel); diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java index b5bede4ad31c..13ba1a3cbf31 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java @@ -40,12 +40,11 @@ import javax.inject.Inject; */ public class ProximitySensor { private static final String TAG = "ProxSensor"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final Sensor mSensor; private final AsyncSensorManager mSensorManager; - private final boolean mUsingBrightnessSensor; - private final float mMaxRange; + private final float mThreshold; private List<ProximitySensorListener> mListeners = new ArrayList<>(); private String mTag = null; @VisibleForTesting ProximityEvent mLastEvent; @@ -68,20 +67,27 @@ public class ProximitySensor { public ProximitySensor(@Main Resources resources, AsyncSensorManager sensorManager) { mSensorManager = sensorManager; - Sensor sensor = findBrightnessSensor(resources); + Sensor sensor = findCustomProxSensor(resources); + float threshold = 0; + if (sensor != null) { + try { + threshold = getCustomProxThreshold(resources); + } catch (IllegalStateException e) { + Log.e(TAG, "Can not load custom proximity sensor.", e); + sensor = null; + } + } if (sensor == null) { - mUsingBrightnessSensor = false; sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); - } else { - mUsingBrightnessSensor = true; + if (sensor != null) { + threshold = sensor.getMaximumRange(); + } } + + mThreshold = threshold; + mSensor = sensor; - if (mSensor != null) { - mMaxRange = mSensor.getMaximumRange(); - } else { - mMaxRange = 0; - } } public void setTag(String tag) { @@ -107,9 +113,15 @@ public class ProximitySensor { mPaused = false; registerInternal(); } + /** + * Returns a brightness sensor that can be used for proximity purposes. + */ + private Sensor findCustomProxSensor(Resources resources) { + String sensorType = resources.getString(R.string.proximity_sensor_type); + if (sensorType.isEmpty()) { + return null; + } - private Sensor findBrightnessSensor(Resources resources) { - String sensorType = resources.getString(R.string.doze_brightness_sensor_type); List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL); Sensor sensor = null; for (Sensor s : sensorList) { @@ -123,6 +135,17 @@ public class ProximitySensor { } /** + * Returns a threshold value that can be used along with {@link #findCustomProxSensor} + */ + private float getCustomProxThreshold(Resources resources) { + try { + return resources.getFloat(R.dimen.proximity_sensor_threshold); + } catch (Resources.NotFoundException e) { + throw new IllegalStateException("R.dimen.proximity_sensor_threshold must be set."); + } + } + + /** * Returns true if we are registered with the SensorManager. */ public boolean isRegistered() { @@ -157,7 +180,6 @@ public class ProximitySensor { if (mRegistered || mPaused || mListeners.isEmpty()) { return; } - logDebug("Using brightness sensor? " + mUsingBrightnessSensor); logDebug("Registering sensor listener"); mRegistered = true; mSensorManager.registerListener(mSensorEventListener, mSensor, mSensorDelay); @@ -196,10 +218,7 @@ public class ProximitySensor { } private void onSensorEvent(SensorEvent event) { - boolean near = event.values[0] < mMaxRange; - if (mUsingBrightnessSensor) { - near = event.values[0] == 0; - } + boolean near = event.values[0] < mThreshold; mLastEvent = new ProximityEvent(near, event.timestamp); alertListeners(); } diff --git a/packages/SystemUI/src/com/android/systemui/util/time/DateFormatUtil.java b/packages/SystemUI/src/com/android/systemui/util/time/DateFormatUtil.java new file mode 100644 index 000000000000..d7c4e93bb26e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/time/DateFormatUtil.java @@ -0,0 +1,40 @@ +/* + * 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.systemui.util.time; + +import android.app.ActivityManager; +import android.content.Context; +import android.text.format.DateFormat; + +import javax.inject.Inject; + +/** + * Instantiable wrapper around {@link DateFormat}. + */ +public class DateFormatUtil { + private final Context mContext; + + @Inject + public DateFormatUtil(Context context) { + mContext = context; + } + + /** Returns true if the phone is in 24 hour format. */ + public boolean is24HourFormat() { + return DateFormat.is24HourFormat(mContext, ActivityManager.getCurrentUser()); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java index 689eed9e2a61..678cfd23e3a5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java @@ -34,6 +34,7 @@ import android.graphics.Bitmap; import android.graphics.ColorSpace; import android.graphics.Rect; import android.hardware.display.DisplayManagerGlobal; +import android.os.Handler; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -72,6 +73,8 @@ public class ImageWallpaperTest extends SysuiTestCase { private Bitmap mWallpaperBitmap; @Mock private DozeParameters mDozeParam; + @Mock + private Handler mHandler; private CountDownLatch mEventCountdown; @@ -104,7 +107,7 @@ public class ImageWallpaperTest extends SysuiTestCase { return new ImageWallpaper(mDozeParam) { @Override public Engine onCreateEngine() { - return new GLEngine(mMockContext, mDozeParam) { + return new GLEngine(mDozeParam, mHandler) { @Override public Context getDisplayContext() { return mMockContext; @@ -196,5 +199,6 @@ public class ImageWallpaperTest extends SysuiTestCase { when(mSurfaceHolder.getSurfaceFrame()).thenReturn(frame); assertThat(engineSpy.checkIfShouldStopTransition()).isEqualTo(assertion); + // destroy } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java index 7ac5443d67d0..a36f2c754243 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java @@ -86,7 +86,9 @@ public abstract class SysuiTestCase { public void SysuiTeardown() { InstrumentationRegistry.registerInstance(mRealInstrumentation, InstrumentationRegistry.getArguments()); - // Reset the assert's testable looper to null. + if (TestableLooper.get(this) != null) { + TestableLooper.get(this).processAllMessages(); + } disallowTestableLooperAsMainThread(); SystemUIFactory.cleanup(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java index 545d2d4229b3..5b78067ef81a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java @@ -33,7 +33,9 @@ import com.android.systemui.classifier.brightline.BrightLineFalsingManager; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerFake; import com.android.systemui.dump.DumpManager; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.DeviceConfigProxyFake; import com.android.systemui.util.concurrency.FakeExecutor; @@ -63,6 +65,7 @@ public class FalsingManagerProxyTest extends SysuiTestCase { private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); private DockManager mDockManager = new DockManagerFake(); + private StatusBarStateController mStatusBarStateController = new StatusBarStateControllerImpl(); @Before public void setup() { @@ -83,7 +86,7 @@ public class FalsingManagerProxyTest extends SysuiTestCase { public void test_brightLineFalsingManagerDisabled() { mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics, mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor, - mDumpManager, mUiBgExecutor); + mDumpManager, mUiBgExecutor, mStatusBarStateController); assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class)); } @@ -94,7 +97,7 @@ public class FalsingManagerProxyTest extends SysuiTestCase { mExecutor.runAllReady(); mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics, mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor, - mDumpManager, mUiBgExecutor); + mDumpManager, mUiBgExecutor, mStatusBarStateController); assertThat(mProxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class)); } @@ -102,7 +105,7 @@ public class FalsingManagerProxyTest extends SysuiTestCase { public void test_brightLineFalsingManagerToggled() throws InterruptedException { mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics, mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor, - mDumpManager, mUiBgExecutor); + mDumpManager, mUiBgExecutor, mStatusBarStateController); assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class)); mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java index 0aaa3b6ad329..8b5cc9abb777 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java @@ -22,12 +22,16 @@ import static org.mockito.Mockito.verify; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.util.DisplayMetrics; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerFake; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.StatusBarStateControllerImpl; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.DeviceConfigProxyFake; import com.android.systemui.util.sensors.ProximitySensor; @@ -40,6 +44,7 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper public class BrightLineFalsingManagerTest extends SysuiTestCase { @@ -47,6 +52,7 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase { private KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock private ProximitySensor mProximitySensor; + private SysuiStatusBarStateController mStatusBarStateController; private BrightLineFalsingManager mFalsingManager; @@ -61,8 +67,11 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase { FalsingDataProvider falsingDataProvider = new FalsingDataProvider(dm); DeviceConfigProxy deviceConfigProxy = new DeviceConfigProxyFake(); DockManager dockManager = new DockManagerFake(); + mStatusBarStateController = new StatusBarStateControllerImpl(); + mStatusBarStateController.setState(StatusBarState.KEYGUARD); mFalsingManager = new BrightLineFalsingManager(falsingDataProvider, - mKeyguardUpdateMonitor, mProximitySensor, deviceConfigProxy, dockManager); + mKeyguardUpdateMonitor, mProximitySensor, deviceConfigProxy, dockManager, + mStatusBarStateController); } @Test @@ -98,4 +107,12 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase { mFalsingManager.onBouncerHidden(); verify(mProximitySensor).register(any(ProximitySensor.ProximitySensorListener.class)); } + + @Test + public void testUnregisterSensor_StateTransition() { + mFalsingManager.onScreenTurningOn(); + reset(mProximitySensor); + mStatusBarStateController.setState(StatusBarState.SHADE); + verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class)); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt new file mode 100644 index 000000000000..68e1ec1d9f1d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt @@ -0,0 +1,192 @@ +/* + * 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.controls.management + +import android.app.PendingIntent +import android.service.controls.Control +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.controls.ControlStatus +import com.android.systemui.controls.controller.ControlInfo +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class AllModelTest : SysuiTestCase() { + + companion object { + private const val EMPTY_STRING = "Other" + } + + @Mock + lateinit var pendingIntent: PendingIntent + + val idPrefix = "controlId" + val favoritesIndices = listOf(7, 3, 1, 9) + val favoritesList = favoritesIndices.map { "controlId$it" } + lateinit var controls: List<ControlStatus> + + lateinit var model: AllModel + + private fun zoneMap(id: Int): String? { + return when (id) { + 10 -> "" + 11 -> null + else -> ((id + 1) % 3).toString() + } + } + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + // controlId0 --> zone = 1 + // controlId1 --> zone = 2, favorite + // controlId2 --> zone = 0 + // controlId3 --> zone = 1, favorite + // controlId4 --> zone = 2 + // controlId5 --> zone = 0 + // controlId6 --> zone = 1 + // controlId7 --> zone = 2, favorite + // controlId8 --> zone = 0 + // controlId9 --> zone = 1, favorite + // controlId10 --> zone = "" + // controlId11 --> zone = null + controls = (0..11).map { + ControlStatus( + Control.StatelessBuilder("$idPrefix$it", pendingIntent) + .setZone(zoneMap(it)) + .build(), + it in favoritesIndices + ) + } + model = AllModel(controls, favoritesList, EMPTY_STRING) + } + + @Test + fun testElements() { + + // Zones are sorted by order of appearance, with empty at the end with special header. + val expected = listOf( + ZoneNameWrapper("1"), + ControlWrapper(controls[0]), + ControlWrapper(controls[3]), + ControlWrapper(controls[6]), + ControlWrapper(controls[9]), + ZoneNameWrapper("2"), + ControlWrapper(controls[1]), + ControlWrapper(controls[4]), + ControlWrapper(controls[7]), + ZoneNameWrapper("0"), + ControlWrapper(controls[2]), + ControlWrapper(controls[5]), + ControlWrapper(controls[8]), + ZoneNameWrapper(EMPTY_STRING), + ControlWrapper(controls[10]), + ControlWrapper(controls[11]) + ) + expected.zip(model.elements).forEachIndexed { index, it -> + assertEquals("Error in item at index $index", it.first, it.second) + } + } + + private fun sameControl(controlInfo: ControlInfo.Builder, control: Control): Boolean { + return controlInfo.controlId == control.controlId && + controlInfo.controlTitle == control.title && + controlInfo.deviceType == control.deviceType + } + + @Test + fun testAllEmpty_noHeader() { + val selected_controls = listOf(controls[10], controls[11]) + val new_model = AllModel(selected_controls, emptyList(), EMPTY_STRING) + val expected = listOf( + ControlWrapper(controls[10]), + ControlWrapper(controls[11]) + ) + + expected.zip(new_model.elements).forEachIndexed { index, it -> + assertEquals("Error in item at index $index", it.first, it.second) + } + } + + @Test + fun testFavorites() { + val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control) + model.favorites.zip(expectedFavorites).forEach { + assertTrue(sameControl(it.first, it.second)) + } + } + + @Test + fun testAddFavorite() { + val indexToAdd = 6 + model.changeFavoriteStatus("$idPrefix$indexToAdd", true) + + val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control) + + controls[indexToAdd].control + + model.favorites.zip(expectedFavorites).forEach { + assertTrue(sameControl(it.first, it.second)) + } + } + + @Test + fun testAddFavorite_alreadyThere() { + val indexToAdd = 7 + model.changeFavoriteStatus("$idPrefix$indexToAdd", true) + + val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control) + + model.favorites.zip(expectedFavorites).forEach { + assertTrue(sameControl(it.first, it.second)) + } + } + + @Test + fun testRemoveFavorite() { + val indexToRemove = 3 + model.changeFavoriteStatus("$idPrefix$indexToRemove", false) + + val expectedFavorites = (favoritesIndices.filterNot { it == indexToRemove }) + .map(controls::get) + .map(ControlStatus::control) + + model.favorites.zip(expectedFavorites).forEach { + assertTrue(sameControl(it.first, it.second)) + } + } + + @Test + fun testRemoveFavorite_notThere() { + val indexToRemove = 4 + model.changeFavoriteStatus("$idPrefix$indexToRemove", false) + + val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control) + + model.favorites.zip(expectedFavorites).forEach { + assertTrue(sameControl(it.first, it.second)) + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java new file mode 100644 index 000000000000..6c09a46833a2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java @@ -0,0 +1,181 @@ +/* + * 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.systemui.pip; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import android.graphics.Rect; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.IWindowContainer; +import android.view.SurfaceControl; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Unit tests against {@link PipAnimationController} to ensure that it sends the right callbacks + * depending on the various interactions. + */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class PipAnimationControllerTest extends SysuiTestCase { + + private PipAnimationController mPipAnimationController; + + @Mock + private IWindowContainer mWindowContainer; + + @Mock + private PipAnimationController.PipAnimationCallback mPipAnimationCallback; + + @Before + public void setUp() throws Exception { + mPipAnimationController = new PipAnimationController(mContext); + MockitoAnnotations.initMocks(this); + } + + @Test + public void getAnimator_withAlpha_returnFloatAnimator() { + final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + new Rect(), 0f, 1f); + + assertEquals("Expect ANIM_TYPE_ALPHA animation", + animator.getAnimationType(), PipAnimationController.ANIM_TYPE_ALPHA); + } + + @Test + public void getAnimator_withBounds_returnBoundsAnimator() { + final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + new Rect(), new Rect()); + + assertEquals("Expect ANIM_TYPE_BOUNDS animation", + animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS); + } + + @Test + public void getAnimator_whenSameTypeRunning_updateExistingAnimator() { + final Rect startValue = new Rect(0, 0, 100, 100); + final Rect endValue1 = new Rect(100, 100, 200, 200); + final Rect endValue2 = new Rect(200, 200, 300, 300); + final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + startValue, endValue1); + oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new); + oldAnimator.start(); + + final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + startValue, endValue2); + + assertEquals("getAnimator with same type returns same animator", + oldAnimator, newAnimator); + assertEquals("getAnimator with same type updates end value", + endValue2, newAnimator.getEndValue()); + } + + @Test + public void getAnimator_scheduleFinishPip() { + PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + new Rect(), 0f, 1f); + assertTrue("scheduleFinishPip is true", animator.shouldScheduleFinishPip()); + + animator = mPipAnimationController + .getAnimator(mWindowContainer, false /* scheduleFinishPip */, + new Rect(), 0f, 1f); + assertFalse("scheduleFinishPip is false", animator.shouldScheduleFinishPip()); + } + + @Test + public void pipTransitionAnimator_updateEndValue() { + final Rect startValue = new Rect(0, 0, 100, 100); + final Rect endValue1 = new Rect(100, 100, 200, 200); + final Rect endValue2 = new Rect(200, 200, 300, 300); + final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + startValue, endValue1); + + animator.updateEndValue(endValue2); + + assertEquals("updateEndValue updates end value", animator.getEndValue(), endValue2); + } + + @Test + public void pipTransitionAnimator_setPipAnimationCallback() { + final Rect startValue = new Rect(0, 0, 100, 100); + final Rect endValue = new Rect(100, 100, 200, 200); + final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + startValue, endValue); + animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new); + + animator.setPipAnimationCallback(mPipAnimationCallback); + + // onAnimationStart triggers onPipAnimationStart + animator.onAnimationStart(animator); + verify(mPipAnimationCallback).onPipAnimationStart(mWindowContainer, animator); + + // onAnimationCancel triggers onPipAnimationCancel + animator.onAnimationCancel(animator); + verify(mPipAnimationCallback).onPipAnimationCancel(mWindowContainer, animator); + + // onAnimationEnd triggers onPipAnimationEnd + animator.onAnimationEnd(animator); + verify(mPipAnimationCallback).onPipAnimationEnd(eq(mWindowContainer), + any(SurfaceControl.Transaction.class), eq(animator)); + } + + /** + * A dummy {@link SurfaceControl.Transaction} class. + * This is created as {@link Mock} does not support method chaining. + */ + private static class DummySurfaceControlTx extends SurfaceControl.Transaction { + @Override + public SurfaceControl.Transaction setAlpha(SurfaceControl leash, float alpha) { + return this; + } + + @Override + public SurfaceControl.Transaction setPosition(SurfaceControl leash, float x, float y) { + return this; + } + + @Override + public SurfaceControl.Transaction setWindowCrop(SurfaceControl leash, int w, int h) { + return this; + } + + @Override + public void apply() {} + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java index bc3ce8baddee..1dbcf10d08d6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java @@ -16,13 +16,7 @@ package com.android.systemui.pip; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; import android.content.ComponentName; import android.graphics.Rect; @@ -31,7 +25,6 @@ import android.testing.TestableLooper; import android.testing.TestableResources; import android.view.DisplayInfo; import android.view.Gravity; -import android.view.IPinnedStackController; import androidx.test.filters.SmallTest; @@ -40,9 +33,6 @@ import com.android.systemui.SysuiTestCase; 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; /** * Unit tests against {@link PipBoundsHandler}, including but not limited to: @@ -55,22 +45,18 @@ import org.mockito.MockitoAnnotations; @TestableLooper.RunWithLooper(setAsMainLooper = true) public class PipBoundsHandlerTest extends SysuiTestCase { private static final int ROUNDING_ERROR_MARGIN = 10; + private static final float DEFAULT_ASPECT_RATIO = 1f; + private static final Rect EMPTY_CURRENT_BOUNDS = null; private PipBoundsHandler mPipBoundsHandler; private DisplayInfo mDefaultDisplayInfo; - private Rect mDefaultDisplayRect; - - @Mock - private IPinnedStackController mPinnedStackController; @Before public void setUp() throws Exception { mPipBoundsHandler = new PipBoundsHandler(mContext); - MockitoAnnotations.initMocks(this); initializeMockResources(); mPipBoundsHandler.onDisplayInfoChanged(mDefaultDisplayInfo); - mPipBoundsHandler.setPinnedStackController(mPinnedStackController); } private void initializeMockResources() { @@ -94,142 +80,80 @@ public class PipBoundsHandlerTest extends SysuiTestCase { mDefaultDisplayInfo.displayId = 1; mDefaultDisplayInfo.logicalWidth = 1000; mDefaultDisplayInfo.logicalHeight = 1500; - mDefaultDisplayRect = new Rect(0, 0, - mDefaultDisplayInfo.logicalWidth, mDefaultDisplayInfo.logicalHeight); } @Test - public void setShelfHeight_offsetBounds() throws Exception { - final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class); + public void setShelfHeight_offsetBounds() { final int shelfHeight = 100; - - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); - - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect lastPosition = destinationBounds.getValue(); - // Reset the pinned stack controller since we will do another verify later on - reset(mPinnedStackController); + final Rect oldPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); mPipBoundsHandler.setShelfHeight(true, shelfHeight); - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + final Rect newPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - lastPosition.offset(0, -shelfHeight); - assertBoundsWithMargin("PiP bounds offset by shelf height", - lastPosition, destinationBounds.getValue()); + oldPosition.offset(0, -shelfHeight); + assertBoundsWithMargin("PiP bounds offset by shelf height", oldPosition, newPosition); } @Test - public void onImeVisibilityChanged_offsetBounds() throws Exception { - final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class); + public void onImeVisibilityChanged_offsetBounds() { final int imeHeight = 100; - - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); - - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect lastPosition = destinationBounds.getValue(); - // Reset the pinned stack controller since we will do another verify later on - reset(mPinnedStackController); + final Rect oldPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight); - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + final Rect newPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - lastPosition.offset(0, -imeHeight); - assertBoundsWithMargin("PiP bounds offset by IME height", - lastPosition, destinationBounds.getValue()); + oldPosition.offset(0, -imeHeight); + assertBoundsWithMargin("PiP bounds offset by IME height", oldPosition, newPosition); } @Test - public void onPrepareAnimation_startAnimation() throws Exception { - final Rect sourceRectHint = new Rect(100, 100, 200, 200); - final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class); - - mPipBoundsHandler.onPrepareAnimation(sourceRectHint, 1f, null); - - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), eq(sourceRectHint), anyInt()); - final Rect capturedDestinationBounds = destinationBounds.getValue(); - assertFalse("Destination bounds is not empty", - capturedDestinationBounds.isEmpty()); - assertBoundsWithMargin("Destination bounds within Display", - mDefaultDisplayRect, capturedDestinationBounds); - } - - @Test - public void onSaveReentryBounds_restoreLastPosition() throws Exception { + public void onSaveReentryBounds_restoreLastPosition() { final ComponentName componentName = new ComponentName(mContext, "component1"); - final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class); - - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + final Rect oldPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect lastPosition = destinationBounds.getValue(); - lastPosition.offset(0, -100); - mPipBoundsHandler.onSaveReentryBounds(componentName, lastPosition); - // Reset the pinned stack controller since we will do another verify later on - reset(mPinnedStackController); + oldPosition.offset(0, -100); + mPipBoundsHandler.onSaveReentryBounds(componentName, oldPosition); - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + final Rect newPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - assertBoundsWithMargin("Last position is restored", - lastPosition, destinationBounds.getValue()); + assertBoundsWithMargin("Last position is restored", oldPosition, newPosition); } @Test - public void onResetReentryBounds_componentMatch_useDefaultBounds() throws Exception { + public void onResetReentryBounds_componentMatch_useDefaultBounds() { final ComponentName componentName = new ComponentName(mContext, "component1"); - final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class); - - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); - - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect defaultBounds = new Rect(destinationBounds.getValue()); + final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); final Rect newBounds = new Rect(defaultBounds); newBounds.offset(0, -100); mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds); - // Reset the pinned stack controller since we will do another verify later on - reset(mPinnedStackController); mPipBoundsHandler.onResetReentryBounds(componentName); - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + final Rect actualBounds = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect actualBounds = destinationBounds.getValue(); assertBoundsWithMargin("Use default bounds", defaultBounds, actualBounds); } @Test - public void onResetReentryBounds_componentMismatch_restoreLastPosition() throws Exception { + public void onResetReentryBounds_componentMismatch_restoreLastPosition() { final ComponentName componentName = new ComponentName(mContext, "component1"); - final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class); - - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); - - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect defaultBounds = new Rect(destinationBounds.getValue()); + final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); final Rect newBounds = new Rect(defaultBounds); newBounds.offset(0, -100); mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds); - // Reset the pinned stack controller since we will do another verify later on - reset(mPinnedStackController); mPipBoundsHandler.onResetReentryBounds(new ComponentName(mContext, "component2")); - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + final Rect actualBounds = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect actualBounds = destinationBounds.getValue(); assertBoundsWithMargin("Last position is restored", newBounds, actualBounds); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt index f11c42bda6cc..f37836c7a506 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt @@ -208,7 +208,7 @@ class NotificationRankingManagerTest : SysuiTestCase() { .setTag("tag") .setNotification(aN) .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT)) - .setUser(mContext.getUser()) + .setUser(mContext.user) .setOverrideGroupKey("") .build() whenever(personNotificationIdentifier.isImportantPeopleNotification(a.sbn, a.ranking)) @@ -226,12 +226,12 @@ class NotificationRankingManagerTest : SysuiTestCase() { .setTag("tag") .setNotification(bN) .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT)) - .setUser(mContext.getUser()) + .setUser(mContext.user) .setOverrideGroupKey("") .build() - whenever(personNotificationIdentifier.isImportantPeopleNotification(a.sbn, a.ranking)) - .thenReturn(false) - whenever(personNotificationIdentifier.isPeopleNotification(a.sbn, a.ranking)) + whenever(personNotificationIdentifier.isImportantPeopleNotification(b.sbn, b.ranking)) + .thenReturn(true) + whenever(personNotificationIdentifier.isPeopleNotification(b.sbn, b.ranking)) .thenReturn(true) assertEquals( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java index e917c93597ee..c5b69694bdfa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java @@ -37,8 +37,9 @@ import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.NotificationShadeWindowBlurController; +import com.android.systemui.statusbar.PulseExpansionHandler; +import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -81,6 +82,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { @Mock private NotificationPanelViewController mNotificationPanelViewController; @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout; @Mock private NotificationShadeWindowBlurController mNotificationShadeWindowBlurController; + @Mock private SuperStatusBarViewFactory mStatusBarViewFactory; @Before public void setUp() { @@ -116,7 +118,8 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { mDockManager, mNotificationShadeWindowBlurController, mView, - mNotificationPanelViewController); + mNotificationPanelViewController, + mStatusBarViewFactory); mController.setupExpandedStatusBar(); mController.setService(mStatusBar); mController.setDragDownHelper(mDragDownHelper); 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 d81b8c2af246..5253e2ca9e42 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 @@ -247,6 +247,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private KeyguardDismissUtil mKeyguardDismissUtil; @Mock private ExtensionController mExtensionController; @Mock private UserInfoControllerImpl mUserInfoControllerImpl; + @Mock private PhoneStatusBarPolicy mPhoneStatusBarPolicy; private ShadeController mShadeController; private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); private InitController mInitController = new InitController(); @@ -400,6 +401,7 @@ public class StatusBarTest extends SysuiTestCase { mKeyguardDismissUtil, mExtensionController, mUserInfoControllerImpl, + mPhoneStatusBarPolicy, mDismissCallbackRegistry, mStatusBarTouchableRegionManager); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java index cd91f22bb753..57714722aea4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.policy; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -140,13 +139,16 @@ public class HotspotControllerImplTest extends SysuiTestCase { } @Test - public void testDefault_hotspotNotSupported() { - assertFalse(mController.isHotspotSupported()); + public void testHotspotSupported_default() { + assertTrue(mController.isHotspotSupported()); } @Test public void testHotspotSupported_rightConditions() { mTetheringCallbackCaptor.getValue().onTetheringSupported(true); + + assertTrue(mController.isHotspotSupported()); + mTetheringCallbackCaptor.getValue() .onTetherableInterfaceRegexpsChanged(mTetheringInterfaceRegexps); @@ -154,13 +156,21 @@ public class HotspotControllerImplTest extends SysuiTestCase { } @Test - public void testHotspotSupported_callbackCalledOnChange() { + public void testHotspotSupported_callbackCalledOnChange_tetheringSupported() { + mController.addCallback(mCallback1); + mTetheringCallbackCaptor.getValue().onTetheringSupported(false); + + verify(mCallback1).onHotspotAvailabilityChanged(false); + } + + @Test + public void testHotspotSupported_callbackCalledOnChange_tetherableInterfaces() { + when(mTetheringInterfaceRegexps.getTetherableWifiRegexs()) + .thenReturn(Collections.emptyList()); mController.addCallback(mCallback1); - mTetheringCallbackCaptor.getValue().onTetheringSupported(true); mTetheringCallbackCaptor.getValue() .onTetherableInterfaceRegexpsChanged(mTetheringInterfaceRegexps); - verify(mCallback1).onHotspotAvailabilityChanged(true); + verify(mCallback1).onHotspotAvailabilityChanged(false); } - } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java index 32da4c9650fb..9c250c5e8e16 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java @@ -72,7 +72,7 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { testSsid); setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, false, true); verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[0][testLevel], - null); + testSsid); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java index 0c9130d08d6d..e6b04405a4ce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java @@ -86,6 +86,7 @@ public class SecurityControllerTest extends SysuiTestCase implements SecurityCon mContext.addMockService(comp, mKeyChainService); when(mUserManager.getUserInfo(anyInt())).thenReturn(new UserInfo()); + when(mUserManager.isUserUnlocked(any())).thenReturn(true); when(mKeyChainService.getUserCaAliases()) .thenReturn(new StringParceledListSlice(new ArrayList<String>())); diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java index 54cb0b83fc8f..31d884c38f58 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java @@ -20,11 +20,9 @@ import android.content.res.Resources; public class FakeProximitySensor extends ProximitySensor { private boolean mAvailable; - private boolean mPaused; public FakeProximitySensor(Resources resources, AsyncSensorManager sensorManager) { super(resources, sensorManager); - mAvailable = true; } diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp index 3111ab701191..0c372351292c 100644 --- a/packages/Tethering/Android.bp +++ b/packages/Tethering/Android.bp @@ -95,11 +95,7 @@ java_defaults { // TODO (b/148190005): change to module-libs-api-stubs-current once it is ready. sdk_version: "core_platform", privileged: true, - // Build system doesn't track transitive dependeicies for jni_libs, list all the dependencies - // explicitly. jni_libs: [ - "liblog", - "libnativehelper_compat_libc++", "libtetherutilsjni", ], resource_dirs: [ diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp index 1a3d5b659ee3..5b73dd53a285 100644 --- a/packages/Tethering/common/TetheringLib/Android.bp +++ b/packages/Tethering/common/TetheringLib/Android.bp @@ -41,8 +41,7 @@ aidl_interface { java_library { name: "framework-tethering", - // TODO (b/146757305): change to module_app_current once available - sdk_version: "core_platform", + sdk_version: "module_current", srcs: [ "src/android/net/TetheredClient.java", "src/android/net/TetheringManager.java", @@ -56,7 +55,6 @@ java_library { libs: [ "framework-annotations-lib", - "android_system_stubs_current", ], hostdex: true, // for hiddenapi check diff --git a/proto/src/task_snapshot.proto b/proto/src/task_snapshot.proto index 789019ce8b75..2006fb3d7bf1 100644 --- a/proto/src/task_snapshot.proto +++ b/proto/src/task_snapshot.proto @@ -32,7 +32,12 @@ int32 system_ui_visibility = 8; bool is_translucent = 9; string top_activity_component = 10; - float scale = 11; + // deprecated because original width and height are stored now instead of the scale. + float legacy_scale = 11 [deprecated=true]; int64 id = 12; int32 rotation = 13; + // The task width when the snapshot was taken + int32 task_width = 14; + // The task height when the snapshot was taken + int32 task_height = 15; } diff --git a/services/api/current.txt b/services/api/current.txt index 4a0a0d8e5aef..8c90165eeb4a 100644 --- a/services/api/current.txt +++ b/services/api/current.txt @@ -49,14 +49,15 @@ package com.android.server { public abstract class SystemService { ctor public SystemService(@NonNull android.content.Context); method @NonNull public final android.content.Context getContext(); - method public boolean isSupportedUser(@NonNull com.android.server.SystemService.TargetUser); + method public boolean isUserSupported(@NonNull com.android.server.SystemService.TargetUser); method public void onBootPhase(int); - method public void onCleanupUser(@NonNull com.android.server.SystemService.TargetUser); method public abstract void onStart(); - method public void onStartUser(@NonNull com.android.server.SystemService.TargetUser); - method public void onStopUser(@NonNull com.android.server.SystemService.TargetUser); - method public void onSwitchUser(@Nullable com.android.server.SystemService.TargetUser, @NonNull com.android.server.SystemService.TargetUser); - method public void onUnlockUser(@NonNull com.android.server.SystemService.TargetUser); + method public void onUserStarting(@NonNull com.android.server.SystemService.TargetUser); + method public void onUserStopped(@NonNull com.android.server.SystemService.TargetUser); + method public void onUserStopping(@NonNull com.android.server.SystemService.TargetUser); + method public void onUserSwitching(@Nullable com.android.server.SystemService.TargetUser, @NonNull com.android.server.SystemService.TargetUser); + method public void onUserUnlocked(@NonNull com.android.server.SystemService.TargetUser); + method public void onUserUnlocking(@NonNull com.android.server.SystemService.TargetUser); method protected final void publishBinderService(@NonNull String, @NonNull android.os.IBinder); method protected final void publishBinderService(@NonNull String, @NonNull android.os.IBinder, boolean); field public static final int PHASE_ACTIVITY_MANAGER_READY = 550; // 0x226 diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index a8a27916f56a..ad21075809ec 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -641,8 +641,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku final SuspendDialogInfo dialogInfo = mPackageManagerInternal.getSuspendedDialogInfo(providerPackage, suspendingPackage, providerUserId); - // TODO(b/148035643): Send the original widget intent or ACTION_MAIN as an - // IntentSender to SuspendedAppActivity. + // onUnsuspend is null because we don't want to start any activity on + // unsuspending from a suspended widget. onClickIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent( providerPackage, suspendingPackage, dialogInfo, null, null, providerUserId); @@ -794,6 +794,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku proto.write(WidgetProto.PROVIDER_PACKAGE, widget.provider.id.componentName.getPackageName()); proto.write(WidgetProto.PROVIDER_CLASS, widget.provider.id.componentName.getClassName()); if (widget.options != null) { + proto.write(WidgetProto.RESTORE_COMPLETED, + widget.options.getBoolean(AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED)); proto.write(WidgetProto.MIN_WIDTH, widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 0)); proto.write(WidgetProto.MIN_HEIGHT, @@ -2509,7 +2511,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku out.endTag(null, "h"); } - private static void serializeAppWidget(XmlSerializer out, Widget widget) throws IOException { + private static void serializeAppWidget(XmlSerializer out, Widget widget, + boolean saveRestoreCompleted) throws IOException { out.startTag(null, "g"); out.attribute(null, "id", Integer.toHexString(widget.appWidgetId)); out.attribute(null, "rid", Integer.toHexString(widget.restoredId)); @@ -2528,10 +2531,50 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku out.attribute(null, "max_height", Integer.toHexString((maxHeight > 0) ? maxHeight : 0)); out.attribute(null, "host_category", Integer.toHexString(widget.options.getInt( AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY))); + if (saveRestoreCompleted) { + boolean restoreCompleted = widget.options.getBoolean( + AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED); + out.attribute(null, "restore_completed", Boolean.toString(restoreCompleted)); + } } out.endTag(null, "g"); } + private static Bundle parseWidgetIdOptions(XmlPullParser parser) { + Bundle options = new Bundle(); + String restoreCompleted = parser.getAttributeValue(null, "restore_completed"); + if (restoreCompleted != null) { + options.putBoolean(AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED, + Boolean.valueOf(restoreCompleted)); + } + String minWidthString = parser.getAttributeValue(null, "min_width"); + if (minWidthString != null) { + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, + Integer.parseInt(minWidthString, 16)); + } + String minHeightString = parser.getAttributeValue(null, "min_height"); + if (minHeightString != null) { + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, + Integer.parseInt(minHeightString, 16)); + } + String maxWidthString = parser.getAttributeValue(null, "max_width"); + if (maxWidthString != null) { + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, + Integer.parseInt(maxWidthString, 16)); + } + String maxHeightString = parser.getAttributeValue(null, "max_height"); + if (maxHeightString != null) { + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, + Integer.parseInt(maxHeightString, 16)); + } + String categoryString = parser.getAttributeValue(null, "host_category"); + if (categoryString != null) { + options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, + Integer.parseInt(categoryString, 16)); + } + return options; + } + @Override public List<String> getWidgetParticipants(int userId) { return mBackupRestoreController.getWidgetParticipants(userId); @@ -3064,7 +3107,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku if (widget.host.getUserId() != userId) { continue; } - serializeAppWidget(out, widget); + serializeAppWidget(out, widget, true); } Iterator<Pair<Integer, String>> it = mPackagesWithBindWidgetPermission.iterator(); @@ -3203,34 +3246,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku String restoredIdString = parser.getAttributeValue(null, "rid"); widget.restoredId = (restoredIdString == null) ? 0 : Integer.parseInt(restoredIdString, 16); - - Bundle options = new Bundle(); - String minWidthString = parser.getAttributeValue(null, "min_width"); - if (minWidthString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, - Integer.parseInt(minWidthString, 16)); - } - String minHeightString = parser.getAttributeValue(null, "min_height"); - if (minHeightString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, - Integer.parseInt(minHeightString, 16)); - } - String maxWidthString = parser.getAttributeValue(null, "max_width"); - if (maxWidthString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, - Integer.parseInt(maxWidthString, 16)); - } - String maxHeightString = parser.getAttributeValue(null, "max_height"); - if (maxHeightString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, - Integer.parseInt(maxHeightString, 16)); - } - String categoryString = parser.getAttributeValue(null, "host_category"); - if (categoryString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, - Integer.parseInt(categoryString, 16)); - } - widget.options = options; + widget.options = parseWidgetIdOptions(parser); final int hostTag = Integer.parseInt(parser.getAttributeValue( null, "h"), 16); @@ -4382,7 +4398,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku if (widget.host.isInPackageForUser(backedupPackage, userId) || (provider != null && provider.isInPackageForUser(backedupPackage, userId))) { - serializeAppWidget(out, widget); + serializeAppWidget(out, widget, false); } } @@ -4815,36 +4831,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku || widget.provider.getUserId() == userId); } - private Bundle parseWidgetIdOptions(XmlPullParser parser) { - Bundle options = new Bundle(); - String minWidthString = parser.getAttributeValue(null, "min_width"); - if (minWidthString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, - Integer.parseInt(minWidthString, 16)); - } - String minHeightString = parser.getAttributeValue(null, "min_height"); - if (minHeightString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, - Integer.parseInt(minHeightString, 16)); - } - String maxWidthString = parser.getAttributeValue(null, "max_width"); - if (maxWidthString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, - Integer.parseInt(maxWidthString, 16)); - } - String maxHeightString = parser.getAttributeValue(null, "max_height"); - if (maxHeightString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, - Integer.parseInt(maxHeightString, 16)); - } - String categoryString = parser.getAttributeValue(null, "host_category"); - if (categoryString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, - Integer.parseInt(categoryString, 16)); - } - return options; - } - private int countPendingUpdates(ArrayList<RestoreUpdateRecord> updates) { int pending = 0; final int N = updates.size(); diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 5405fc74dfb6..e49c1ed47c93 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -331,7 +331,7 @@ public final class AutofillManagerService } @Override // from SystemService - public boolean isSupportedUser(TargetUser user) { + public boolean isUserSupported(TargetUser user) { return user.getUserInfo().isFull() || user.getUserInfo().isManagedProfile(); } diff --git a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java index 5d5af535920b..7ad5632dd70e 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java @@ -86,9 +86,9 @@ public final class RemoteInlineSuggestionRenderService extends */ public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback, @NonNull InlinePresentation presentation, int width, int height, - @Nullable IBinder hostInputToken) { - scheduleAsyncRequest( - (s) -> s.renderSuggestion(callback, presentation, width, height, hostInputToken)); + @Nullable IBinder hostInputToken, int displayId) { + scheduleAsyncRequest((s) -> s.renderSuggestion(callback, presentation, width, height, + hostInputToken, displayId)); } @Nullable diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java index 0d8c89b4124d..fef49d44bed0 100644 --- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java +++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java @@ -134,7 +134,7 @@ public final class InlineSuggestionFactory { if (inlineAuthentication != null) { InlineSuggestion inlineAuthSuggestion = createInlineAuthSuggestion(inlineAuthentication, remoteRenderService, onClickFactory, onErrorCallback, - request.getHostInputToken()); + request.getHostInputToken(), request.getHostDisplayId()); inlineSuggestions.add(inlineAuthSuggestion); return new InlineSuggestionsResponse(inlineSuggestions); @@ -162,9 +162,10 @@ public final class InlineSuggestionFactory { continue; } InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset, - fieldIndex, mergedInlinePresentation(request, datasetIndex, inlinePresentation), + datasetIndex, + mergedInlinePresentation(request, datasetIndex, inlinePresentation), onClickFactory, remoteRenderService, onErrorCallback, - request.getHostInputToken()); + request.getHostInputToken(), request.getHostDisplayId()); inlineSuggestions.add(inlineSuggestion); } @@ -172,7 +173,8 @@ public final class InlineSuggestionFactory { for (InlinePresentation inlinePresentation : inlineActions) { final InlineSuggestion inlineAction = createInlineAction(isAugmented, context, mergedInlinePresentation(request, 0, inlinePresentation), - remoteRenderService, onErrorCallback, request.getHostInputToken()); + remoteRenderService, onErrorCallback, request.getHostInputToken(), + request.getHostDisplayId()); inlineSuggestions.add(inlineAction); } } @@ -215,7 +217,8 @@ public final class InlineSuggestionFactory { @NonNull Context context, @NonNull InlinePresentation inlinePresentation, @Nullable RemoteInlineSuggestionRenderService remoteRenderService, - @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken) { + @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken, + int displayId) { // TODO(b/146453195): fill in the autofill hint properly. final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo( inlinePresentation.getInlinePresentationSpec(), @@ -227,7 +230,7 @@ public final class InlineSuggestionFactory { }; return new InlineSuggestion(inlineSuggestionInfo, createInlineContentProvider(inlinePresentation, onClickAction, onErrorCallback, - remoteRenderService, hostInputToken)); + remoteRenderService, hostInputToken, displayId)); } private static InlineSuggestion createInlineSuggestion(boolean isAugmented, @@ -235,7 +238,8 @@ public final class InlineSuggestionFactory { @NonNull InlinePresentation inlinePresentation, @NonNull BiConsumer<Dataset, Integer> onClickFactory, @NonNull RemoteInlineSuggestionRenderService remoteRenderService, - @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken) { + @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken, + int displayId) { // TODO(b/146453195): fill in the autofill hint properly. final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo( inlinePresentation.getInlinePresentationSpec(), @@ -246,7 +250,7 @@ public final class InlineSuggestionFactory { final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo, createInlineContentProvider(inlinePresentation, () -> onClickFactory.accept(dataset, datasetIndex), onErrorCallback, - remoteRenderService, hostInputToken)); + remoteRenderService, hostInputToken, displayId)); return inlineSuggestion; } @@ -255,7 +259,7 @@ public final class InlineSuggestionFactory { @NonNull InlinePresentation inlinePresentation, @NonNull RemoteInlineSuggestionRenderService remoteRenderService, @NonNull BiConsumer<Dataset, Integer> onClickFactory, @NonNull Runnable onErrorCallback, - @Nullable IBinder hostInputToken) { + @Nullable IBinder hostInputToken, int displayId) { final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo( inlinePresentation.getInlinePresentationSpec(), InlineSuggestionInfo.SOURCE_AUTOFILL, null, InlineSuggestionInfo.TYPE_SUGGESTION); @@ -264,7 +268,7 @@ public final class InlineSuggestionFactory { createInlineContentProvider(inlinePresentation, () -> onClickFactory.accept(null, AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED), - onErrorCallback, remoteRenderService, hostInputToken)); + onErrorCallback, remoteRenderService, hostInputToken, displayId)); } /** @@ -291,7 +295,8 @@ public final class InlineSuggestionFactory { @NonNull InlinePresentation inlinePresentation, @Nullable Runnable onClickAction, @NonNull Runnable onErrorCallback, @Nullable RemoteInlineSuggestionRenderService remoteRenderService, - @Nullable IBinder hostInputToken) { + @Nullable IBinder hostInputToken, + int displayId) { return new IInlineContentProvider.Stub() { @Override public void provideContent(int width, int height, IInlineContentCallback callback) { @@ -305,7 +310,7 @@ public final class InlineSuggestionFactory { } remoteRenderService.renderSuggestion(uiCallback, inlinePresentation, - width, height, hostInputToken); + width, height, hostInputToken, displayId); }); } }; diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 9a33fc9548a0..5d2b9f359381 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -201,7 +201,7 @@ public final class ContentCaptureManagerService extends } @Override // from SystemService - public boolean isSupportedUser(TargetUser user) { + public boolean isUserSupported(TargetUser user) { return user.getUserInfo().isFull() || user.getUserInfo().isManagedProfile(); } diff --git a/services/core/Android.bp b/services/core/Android.bp index 4da60b8e4ffd..3180ceb8f8e1 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -99,6 +99,7 @@ java_library_static { "android.hardware.vibrator-java", "app-compat-annotations", "framework-tethering-stubs", + "ike-stubs", ], required: [ @@ -126,7 +127,6 @@ java_library_static { "android.hidl.manager-V1.2-java", "dnsresolver_aidl_interface-V2-java", "netd_event_listener_interface-java", - "ike-stubs", ], plugins: [ diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 1230bd79ff97..1b1e06a9a92f 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -458,8 +458,8 @@ public abstract class PackageManagerInternal { Bundle verificationBundle, int userId); /** - * Grants implicit access based on an interaction between two apps. This grants the target app - * access to the calling application's package metadata. + * Grants implicit access based on an interaction between two apps. This grants access to the + * from one application to the other's package metadata. * <p> * When an application explicitly tries to interact with another application [via an * activity, service or provider that is either declared in the caller's @@ -468,14 +468,22 @@ public abstract class PackageManagerInternal { * metadata about the calling app. If the calling application uses an implicit intent [ie * action VIEW, category BROWSABLE], it remains hidden from the launched app. * <p> + * If an interaction is not explicit, the {@code direct} argument should be set to false as + * visibility should not be granted in some cases. This method handles that logic. + * <p> * @param userId the user * @param intent the intent that triggered the grant - * @param callingUid The uid of the calling application - * @param targetAppId The app ID of the target application + * @param recipientAppId The app ID of the application that is being given access to {@code + * visibleUid} + * @param visibleUid The uid of the application that is becoming accessible to {@code + * recipientAppId} + * @param direct true if the access is being made due to direct interaction between visibleUid + * and recipientAppId. */ public abstract void grantImplicitAccess( - @UserIdInt int userId, Intent intent, int callingUid, - @AppIdInt int targetAppId); + @UserIdInt int userId, Intent intent, + @AppIdInt int recipientAppId, int visibleUid, + boolean direct); public abstract boolean isInstantAppInstallerComponent(ComponentName component); /** diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java index 93859b35fbe0..3148a6205871 100644 --- a/services/core/java/com/android/server/PinnerService.java +++ b/services/core/java/com/android/server/PinnerService.java @@ -345,7 +345,7 @@ public final class PinnerService extends SystemService { @Override public void onUidCachedChanged(int uid, boolean cached) throws RemoteException { } - }, UID_OBSERVER_GONE | UID_OBSERVER_ACTIVE, 0, "system"); + }, UID_OBSERVER_GONE | UID_OBSERVER_ACTIVE, 0, null); } catch (RemoteException e) { Slog.e(TAG, "Failed to register uid observer", e); } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index d86b2230ee7a..95bfbd72dc56 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -279,7 +279,7 @@ class StorageManagerService extends IStorageManager.Stub } @Override - public void onStartUser(TargetUser user) { + public void onUserStarting(TargetUser user) { mStorageManagerService.snapshotAndMonitorLegacyStorageAppOp(user.getUserHandle()); } } @@ -4458,8 +4458,11 @@ class StorageManagerService extends IStorageManager.Stub mVold.fixupAppDir(packageObbDir.getCanonicalPath() + "/", uid); } catch (IOException e) { Log.e(TAG, "Failed to get canonical path for " + packageName); - } catch (RemoteException e) { - Log.e(TAG, "Failed to fixup app dir for " + packageName); + } catch (RemoteException | ServiceSpecificException e) { + // TODO(b/149975102) there is a known case where this fails, when a new + // user is setup and we try to fixup app dirs for some existing apps. + // For now catch the exception and don't crash. + Log.e(TAG, "Failed to fixup app dir for " + packageName, e); } } } diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java index aabe98b3afa0..e5c05408f628 100644 --- a/services/core/java/com/android/server/SystemService.java +++ b/services/core/java/com/android/server/SystemService.java @@ -238,12 +238,12 @@ public abstract class SystemService { * <p>By default returns {@code true}, but subclasses should extend for optimization, if they * don't support some types (like headless system user). */ - public boolean isSupportedUser(@NonNull TargetUser user) { + public boolean isUserSupported(@NonNull TargetUser user) { return true; } /** - * Helper method used to dump which users are {@link #onStartUser(TargetUser) supported}. + * Helper method used to dump which users are {@link #onUserStarting(TargetUser) supported}. * * @hide */ @@ -264,7 +264,7 @@ public abstract class SystemService { } /** - * @deprecated subclasses should extend {@link #onStartUser(TargetUser)} instead + * @deprecated subclasses should extend {@link #onUserStarting(TargetUser)} instead * (which by default calls this method). * * @hide @@ -273,7 +273,7 @@ public abstract class SystemService { public void onStartUser(@UserIdInt int userId) {} /** - * @deprecated subclasses should extend {@link #onStartUser(TargetUser)} instead + * @deprecated subclasses should extend {@link #onUserStarting(TargetUser)} instead * (which by default calls this method). * * @hide @@ -287,17 +287,17 @@ public abstract class SystemService { * Called when a new user is starting, for system services to initialize any per-user * state they maintain for running users. * - * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports} + * <p>This method is only called when the service {@link #isUserSupported(TargetUser) supports} * this user. * * @param user target user */ - public void onStartUser(@NonNull TargetUser user) { + public void onUserStarting(@NonNull TargetUser user) { onStartUser(user.getUserInfo()); } /** - * @deprecated subclasses should extend {@link #onUnlockUser(TargetUser)} instead (which by + * @deprecated subclasses should extend {@link #onUserUnlocking(TargetUser)} instead (which by * default calls this method). * * @hide @@ -306,7 +306,7 @@ public abstract class SystemService { public void onUnlockUser(@UserIdInt int userId) {} /** - * @deprecated subclasses should extend {@link #onUnlockUser(TargetUser)} instead (which by + * @deprecated subclasses should extend {@link #onUserUnlocking(TargetUser)} instead (which by * default calls this method). * * @hide @@ -326,19 +326,30 @@ public abstract class SystemService { * the user will transition into the {@code STATE_RUNNING_UNLOCKED} state. * Code written inside system services should use * {@link UserManager#isUserUnlockingOrUnlocked(int)} to handle both of - * these states. + * these states, or use {@link #onUserUnlocked(TargetUser)} instead. * <p> - * This method is only called when the service {@link #isSupportedUser(TargetUser) supports} + * This method is only called when the service {@link #isUserSupported(TargetUser) supports} * this user. * * @param user target user */ - public void onUnlockUser(@NonNull TargetUser user) { + public void onUserUnlocking(@NonNull TargetUser user) { onUnlockUser(user.getUserInfo()); } /** - * @deprecated subclasses should extend {@link #onSwitchUser(TargetUser, TargetUser)} instead + * Called after an existing user is unlocked. + * + * <p>This method is only called when the service {@link #isUserSupported(TargetUser) supports} + * this user. + * + * @param user target user + */ + public void onUserUnlocked(@NonNull TargetUser user) { + } + + /** + * @deprecated subclasses should extend {@link #onUserSwitching(TargetUser, TargetUser)} instead * (which by default calls this method). * * @hide @@ -347,7 +358,7 @@ public abstract class SystemService { public void onSwitchUser(@UserIdInt int toUserId) {} /** - * @deprecated subclasses should extend {@link #onSwitchUser(TargetUser, TargetUser)} instead + * @deprecated subclasses should extend {@link #onUserSwitching(TargetUser, TargetUser)} instead * (which by default calls this method). * * @hide @@ -362,7 +373,7 @@ public abstract class SystemService { * special behavior for whichever user is currently in the foreground. This is called * before any application processes are aware of the new user. * - * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports} + * <p>This method is only called when the service {@link #isUserSupported(TargetUser) supports} * either of the users ({@code from} or {@code to}). * * <b>NOTE: </b> both {@code from} and {@code to} are "live" objects @@ -371,12 +382,12 @@ public abstract class SystemService { * @param from the user switching from * @param to the user switching to */ - public void onSwitchUser(@Nullable TargetUser from, @NonNull TargetUser to) { + public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { onSwitchUser((from == null ? null : from.getUserInfo()), to.getUserInfo()); } /** - * @deprecated subclasses should extend {@link #onStopUser(TargetUser)} instead + * @deprecated subclasses should extend {@link #onUserStopping(TargetUser)} instead * (which by default calls this method). * * @hide @@ -385,7 +396,7 @@ public abstract class SystemService { public void onStopUser(@UserIdInt int userId) {} /** - * @deprecated subclasses should extend {@link #onStopUser(TargetUser)} instead + * @deprecated subclasses should extend {@link #onUserStopping(TargetUser)} instead * (which by default calls this method). * * @hide @@ -402,19 +413,19 @@ public abstract class SystemService { * broadcast to the user; it is a good place to stop making use of any resources of that * user (such as binding to a service running in the user). * - * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports} + * <p>This method is only called when the service {@link #isUserSupported(TargetUser) supports} * this user. * * <p>NOTE: This is the last callback where the callee may access the target user's CE storage. * * @param user target user */ - public void onStopUser(@NonNull TargetUser user) { + public void onUserStopping(@NonNull TargetUser user) { onStopUser(user.getUserInfo()); } /** - * @deprecated subclasses should extend {@link #onCleanupUser(TargetUser)} instead (which by + * @deprecated subclasses should extend {@link #onUserStopped(TargetUser)} instead (which by * default calls this method). * * @hide @@ -423,7 +434,7 @@ public abstract class SystemService { public void onCleanupUser(@UserIdInt int userId) {} /** - * @deprecated subclasses should extend {@link #onCleanupUser(TargetUser)} instead (which by + * @deprecated subclasses should extend {@link #onUserStopped(TargetUser)} instead (which by * default calls this method). * * @hide @@ -434,20 +445,16 @@ public abstract class SystemService { } /** - * Called when an existing user is stopping, for system services to finalize any per-user - * state they maintain for running users. This is called after all application process - * teardown of the user is complete. + * Called after an existing user is stopped. * - * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports} - * this user. + * <p>This is called after all application process teardown of the user is complete. * - * <p>NOTE: When this callback is called, the CE storage for the target user may not be - * accessible already. Use {@link #onStopUser(TargetUser)} instead if you need to access the CE - * storage. + * <p>This method is only called when the service {@link #isUserSupported(TargetUser) supports} + * this user. * * @param user target user */ - public void onCleanupUser(@NonNull TargetUser user) { + public void onUserStopped(@NonNull TargetUser user) { onCleanupUser(user.getUserInfo()); } diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java index e7f78462e6ee..f16f6ef2e72d 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -51,7 +51,8 @@ public class SystemServiceManager { // Constants used on onUser(...) private static final String START = "Start"; - private static final String UNLOCK = "Unlock"; + private static final String UNLOCKING = "Unlocking"; + private static final String UNLOCKED = "Unlocked"; private static final String SWITCH = "Switch"; private static final String STOP = "Stop"; private static final String CLEANUP = "Cleanup"; @@ -260,7 +261,14 @@ public class SystemServiceManager { * Unlocks the given user. */ public void unlockUser(final @UserIdInt int userHandle) { - onUser(UNLOCK, userHandle); + onUser(UNLOCKING, userHandle); + } + + /** + * Called after the user was unlocked. + */ + public void onUserUnlocked(final @UserIdInt int userHandle) { + onUser(UNLOCKED, userHandle); } /** @@ -304,12 +312,12 @@ public class SystemServiceManager { for (int i = 0; i < serviceLen; i++) { final SystemService service = mServices.get(i); final String serviceName = service.getClass().getName(); - boolean supported = service.isSupportedUser(curUser); + boolean supported = service.isUserSupported(curUser); // Must check if either curUser or prevUser is supported (for example, if switching from // unsupported to supported, we still need to notify the services) if (!supported && prevUser != null) { - supported = service.isSupportedUser(prevUser); + supported = service.isUserSupported(prevUser); } if (!supported) { @@ -328,19 +336,22 @@ public class SystemServiceManager { try { switch (onWhat) { case SWITCH: - service.onSwitchUser(prevUser, curUser); + service.onUserSwitching(prevUser, curUser); break; case START: - service.onStartUser(curUser); + service.onUserStarting(curUser); + break; + case UNLOCKING: + service.onUserUnlocking(curUser); break; - case UNLOCK: - service.onUnlockUser(curUser); + case UNLOCKED: + service.onUserUnlocked(curUser); break; case STOP: - service.onStopUser(curUser); + service.onUserStopping(curUser); break; case CLEANUP: - service.onCleanupUser(curUser); + service.onUserStopped(curUser); break; default: throw new IllegalArgumentException(onWhat + " what?"); diff --git a/services/core/java/com/android/server/UserspaceRebootLogger.java b/services/core/java/com/android/server/UserspaceRebootLogger.java index 74f113f58c70..9a9374ce1822 100644 --- a/services/core/java/com/android/server/UserspaceRebootLogger.java +++ b/services/core/java/com/android/server/UserspaceRebootLogger.java @@ -26,6 +26,7 @@ import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPOR import android.os.SystemClock; import android.os.SystemProperties; +import android.text.TextUtils; import android.util.Slog; import com.android.internal.util.FrameworkStatsLog; @@ -45,7 +46,7 @@ public final class UserspaceRebootLogger { "sys.userspace_reboot.log.last_started"; private static final String USERSPACE_REBOOT_LAST_FINISHED_PROPERTY = "sys.userspace_reboot.log.last_finished"; - private static final String BOOT_REASON_PROPERTY = "sys.boot.reason"; + private static final String LAST_BOOT_REASON_PROPERTY = "sys.boot.reason.last"; private UserspaceRebootLogger() {} @@ -111,26 +112,28 @@ public final class UserspaceRebootLogger { if (SystemProperties.getLong(USERSPACE_REBOOT_LAST_STARTED_PROPERTY, -1) != -1) { return USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS; } - String reason = SystemProperties.get(BOOT_REASON_PROPERTY, ""); + String reason = TextUtils.emptyIfNull(SystemProperties.get(LAST_BOOT_REASON_PROPERTY, "")); if (reason.startsWith("reboot,")) { reason = reason.substring("reboot".length()); } - switch (reason) { - case "userspace_failed,watchdog_fork": - // Since fork happens before shutdown sequence, attribute it to - // USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED. - case "userspace_failed,shutdown_aborted": - return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED; - case "userspace_failed,init_user0_failed": - // init_user0 will fail if userdata wasn't remounted correctly, attribute to - // USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT. - case "mount_userdata_failed": - return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT; - case "userspace_failed,watchdog_triggered": - return - USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED; - default: - return USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN; + if (reason.startsWith("userspace_failed,watchdog_fork")) { + return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED; } + if (reason.startsWith("userspace_failed,shutdown_aborted")) { + return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED; + } + if (reason.startsWith("mount_userdata_failed")) { + return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT; + } + if (reason.startsWith("userspace_failed,init_user0")) { + return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT; + } + if (reason.startsWith("userspace_failed,enablefilecrypto")) { + return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT; + } + if (reason.startsWith("userspace_failed,watchdog_triggered")) { + return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED; + } + return USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN; } } diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java index 6c68c312bbde..e49357bee896 100644 --- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java +++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java @@ -1853,6 +1853,7 @@ public class AdbDebuggingManager { public void removeKey(String key) { if (mKeyMap.containsKey(key)) { mKeyMap.remove(key); + writeKeys(mKeyMap.keySet()); sendPersistKeyStoreMessage(); } } diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java index 7ccb28474604..7aaf9be1fdd2 100644 --- a/services/core/java/com/android/server/adb/AdbService.java +++ b/services/core/java/com/android/server/adb/AdbService.java @@ -144,6 +144,18 @@ public class AdbService extends IAdbManager.Stub { public File getAdbTempKeysFile() { return mDebuggingManager.getAdbTempKeysFile(); } + + @Override + public void startAdbdForTransport(byte transportType) { + FgThread.getHandler().sendMessage(obtainMessage( + AdbService::setAdbdEnabledForTransport, AdbService.this, true, transportType)); + } + + @Override + public void stopAdbdForTransport(byte transportType) { + FgThread.getHandler().sendMessage(obtainMessage( + AdbService::setAdbdEnabledForTransport, AdbService.this, false, transportType)); + } } private void initAdbState() { @@ -437,6 +449,19 @@ public class AdbService extends IAdbManager.Stub { } } + private void setAdbdEnabledForTransport(boolean enable, byte transportType) { + if (transportType == AdbTransportType.USB) { + mIsAdbUsbEnabled = enable; + } else if (transportType == AdbTransportType.WIFI) { + mIsAdbWifiEnabled = enable; + } + if (enable) { + startAdbd(); + } else { + stopAdbd(); + } + } + private void setAdbEnabled(boolean enable, byte transportType) { if (DEBUG) { Slog.d(TAG, "setAdbEnabled(" + enable + "), mIsAdbUsbEnabled=" + mIsAdbUsbEnabled diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 7f98c7f2ba85..f408fe749eb4 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST; @@ -36,6 +37,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE_E import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -88,11 +90,13 @@ import android.util.EventLog; import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseIntArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import android.webkit.WebViewZygote; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.app.procstats.ServiceState; import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.notification.SystemNotificationChannels; @@ -177,6 +181,10 @@ public final class ActiveServices { /** Temporary list for holding the results of calls to {@link #collectPackageServicesLocked} */ private ArrayList<ServiceRecord> mTmpCollectionResults = null; + /** Mapping from uid to their foreground service AppOpCallbacks (if they have one). */ + @GuardedBy("mAm") + private final SparseArray<AppOpCallback> mFgsAppOpCallbacks = new SparseArray<>(); + /** * For keeping ActiveForegroundApps retaining state while the screen is off. */ @@ -685,8 +693,8 @@ public final class ActiveServices { if (!r.mAllowWhileInUsePermissionInFgs) { r.mAllowWhileInUsePermissionInFgs = - shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingUid, - service, r, allowBackgroundActivityStarts); + shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid, + callingUid, service, r, allowBackgroundActivityStarts); } return cmp; @@ -1455,7 +1463,9 @@ public final class ActiveServices { null, true, false, ""); FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortInstanceName, - FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER); + FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER, + r.mAllowWhileInUsePermissionInFgs); + registerAppOpCallbackLocked(r); mAm.updateForegroundServiceUsageStats(r.name, r.userId, true); } r.postNotification(); @@ -1504,9 +1514,11 @@ public final class ActiveServices { mAm.mAppOpsService.finishOperation( AppOpsManager.getToken(mAm.mAppOpsService), AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null); + unregisterAppOpCallbackLocked(r); FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortInstanceName, - FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT); + FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT, + r.mAllowWhileInUsePermissionInFgs); mAm.updateForegroundServiceUsageStats(r.name, r.userId, false); if (r.app != null) { mAm.updateLruProcessLocked(r.app, false, null); @@ -1527,6 +1539,207 @@ public final class ActiveServices { } } + /** Registers an AppOpCallback for monitoring special AppOps for this foreground service. */ + private void registerAppOpCallbackLocked(@NonNull ServiceRecord r) { + if (r.app == null) { + return; + } + final int uid = r.appInfo.uid; + AppOpCallback callback = mFgsAppOpCallbacks.get(uid); + if (callback == null) { + callback = new AppOpCallback(r.app, mAm.getAppOpsManager()); + mFgsAppOpCallbacks.put(uid, callback); + } + callback.registerLocked(); + } + + /** Unregisters a foreground service's AppOpCallback. */ + private void unregisterAppOpCallbackLocked(@NonNull ServiceRecord r) { + final int uid = r.appInfo.uid; + final AppOpCallback callback = mFgsAppOpCallbacks.get(uid); + if (callback != null) { + callback.unregisterLocked(); + if (callback.isObsoleteLocked()) { + mFgsAppOpCallbacks.remove(uid); + } + } + } + + /** + * For monitoring when {@link #LOGGED_AP_OPS} AppOps occur by an app while it is holding + * at least one foreground service and is not also in the TOP state. + * Once the uid no longer holds any foreground services, this callback becomes stale + * (marked by {@link #isObsoleteLocked()}) and must no longer be used. + * + * Methods that end in Locked should only be called while the mAm lock is held. + */ + private static final class AppOpCallback { + /** AppOps that should be logged if they occur during a foreground service. */ + private static final int[] LOGGED_AP_OPS = new int[] { + AppOpsManager.OP_COARSE_LOCATION, + AppOpsManager.OP_FINE_LOCATION, + AppOpsManager.OP_RECORD_AUDIO, + AppOpsManager.OP_CAMERA + }; + + private final ProcessRecord mProcessRecord; + + /** Count of acceptances per appop (for LOGGED_AP_OPS) during this fgs session. */ + @GuardedBy("mCounterLock") + private final SparseIntArray mAcceptedOps = new SparseIntArray(); + /** Count of rejections per appop (for LOGGED_AP_OPS) during this fgs session. */ + @GuardedBy("mCounterLock") + private final SparseIntArray mRejectedOps = new SparseIntArray(); + + /** Lock for the purposes of mAcceptedOps and mRejectedOps. */ + private final Object mCounterLock = new Object(); + + /** + * AppOp Mode (e.g. {@link AppOpsManager#MODE_ALLOWED} per op. + * This currently cannot change without the process being killed, so they are constants. + */ + private final SparseIntArray mAppOpModes = new SparseIntArray(); + + /** + * Number of foreground services currently associated with this AppOpCallback (i.e. + * currently held for this uid). + */ + @GuardedBy("mAm") + private int mNumFgs = 0; + + /** + * Indicates that this Object is stale and must not be used. + * Specifically, when mNumFgs decreases down to 0, the callbacks will be unregistered and + * this AppOpCallback is unusable. + */ + @GuardedBy("mAm") + private boolean mDestroyed = false; + + private final AppOpsManager mAppOpsManager; + + AppOpCallback(@NonNull ProcessRecord r, @NonNull AppOpsManager appOpsManager) { + mProcessRecord = r; + mAppOpsManager = appOpsManager; + for (int op : LOGGED_AP_OPS) { + int mode = appOpsManager.unsafeCheckOpRawNoThrow(op, r.uid, r.info.packageName); + mAppOpModes.put(op, mode); + } + } + + private final AppOpsManager.OnOpNotedListener mOpNotedCallback = + new AppOpsManager.OnOpNotedListener() { + @Override + public void onOpNoted(int op, int uid, String pkgName, int result) { + if (uid == mProcessRecord.uid && isNotTop()) { + incrementOpCount(op, result == AppOpsManager.MODE_ALLOWED); + } + } + }; + + private final AppOpsManager.OnOpActiveChangedInternalListener mOpActiveCallback = + new AppOpsManager.OnOpActiveChangedInternalListener() { + @Override + public void onOpActiveChanged(int op, int uid, String pkgName, boolean active) { + if (uid == mProcessRecord.uid && active && isNotTop()) { + incrementOpCount(op, true); + } + } + }; + + private boolean isNotTop() { + return mProcessRecord.getCurProcState() != ActivityManager.PROCESS_STATE_TOP; + } + + private void incrementOpCount(int op, boolean allowed) { + synchronized (mCounterLock) { + final SparseIntArray counter = allowed ? mAcceptedOps : mRejectedOps; + final int index = counter.indexOfKey(op); + if (index < 0) { + counter.put(op, 1); + } else { + counter.setValueAt(index, counter.valueAt(index) + 1); + } + } + } + + void registerLocked() { + if (isObsoleteLocked()) { + Slog.wtf(TAG, "Trying to register on a stale AppOpCallback."); + return; + } + mNumFgs++; + if (mNumFgs == 1) { + mAppOpsManager.startWatchingNoted(LOGGED_AP_OPS, mOpNotedCallback); + mAppOpsManager.startWatchingActive(LOGGED_AP_OPS, mOpActiveCallback); + } + } + + void unregisterLocked() { + mNumFgs--; + if (mNumFgs <= 0) { + mDestroyed = true; + logFinalValues(); + mAppOpsManager.stopWatchingNoted(mOpNotedCallback); + mAppOpsManager.stopWatchingActive(mOpActiveCallback); + } + } + + /** + * Indicates that all foreground services for this uid are now over and the callback is + * stale and must never be used again. + */ + boolean isObsoleteLocked() { + return mDestroyed; + } + + private void logFinalValues() { + synchronized (mCounterLock) { + for (int op : LOGGED_AP_OPS) { + final int acceptances = mAcceptedOps.get(op); + final int rejections = mRejectedOps.get(op); + if (acceptances > 0 || rejections > 0) { + FrameworkStatsLog.write( + FrameworkStatsLog.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED, + mProcessRecord.uid, opToEnum(op), + modeToEnum(mAppOpModes.get(op)), + acceptances, rejections + ); + } + } + } + } + + /** Maps AppOp mode to atoms.proto enum. */ + private static int modeToEnum(int mode) { + switch (mode) { + case AppOpsManager.MODE_ALLOWED: return FrameworkStatsLog + .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_ALLOWED; + case AppOpsManager.MODE_IGNORED: return FrameworkStatsLog + .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_IGNORED; + case AppOpsManager.MODE_FOREGROUND: return FrameworkStatsLog + .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_FOREGROUND; + default: return FrameworkStatsLog + .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_UNKNOWN; + } + } + } + + /** Maps AppOp op value to atoms.proto enum. */ + private static int opToEnum(int op) { + switch (op) { + case AppOpsManager.OP_COARSE_LOCATION: return FrameworkStatsLog + .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_COARSE_LOCATION; + case AppOpsManager.OP_FINE_LOCATION: return FrameworkStatsLog + .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_FINE_LOCATION; + case AppOpsManager.OP_CAMERA: return FrameworkStatsLog + .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_CAMERA; + case AppOpsManager.OP_RECORD_AUDIO: return FrameworkStatsLog + .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_RECORD_AUDIO; + default: return FrameworkStatsLog + .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_NONE; + } + } + private void cancelForegroundNotificationLocked(ServiceRecord r) { if (r.foregroundId != 0) { // First check to see if this app has any other active foreground services @@ -1865,9 +2078,9 @@ public final class ActiveServices { } if (!s.mAllowWhileInUsePermissionInFgs) { - final int callingUid = Binder.getCallingUid(); s.mAllowWhileInUsePermissionInFgs = - shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingUid, + shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, + Binder.getCallingPid(), Binder.getCallingUid(), service, s, false); } @@ -3136,9 +3349,11 @@ public final class ActiveServices { mAm.mAppOpsService.finishOperation( AppOpsManager.getToken(mAm.mAppOpsService), AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null); + unregisterAppOpCallbackLocked(r); FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortInstanceName, - FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT); + FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT, + r.mAllowWhileInUsePermissionInFgs); mAm.updateForegroundServiceUsageStats(r.name, r.userId, false); } @@ -4625,7 +4840,8 @@ public final class ActiveServices { * @return true if allow, false otherwise. */ private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage, - int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) { + int callingPid, int callingUid, Intent intent, ServiceRecord r, + boolean allowBackgroundActivityStarts) { // Is the background FGS start restriction turned on? if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) { return true; @@ -4635,13 +4851,6 @@ public final class ActiveServices { return true; } - // Is the service in a whitelist? - final boolean hasAllowBackgroundActivityStartsToken = r.app != null - ? r.app.mAllowBackgroundActivityStartsTokens.contains(r) : false; - if (hasAllowBackgroundActivityStartsToken) { - return true; - } - boolean isCallerSystem = false; final int callingAppId = UserHandle.getAppId(callingUid); switch (callingAppId) { @@ -4660,6 +4869,24 @@ public final class ActiveServices { return true; } + if (r.app != null) { + ActiveInstrumentation instr = r.app.getActiveInstrumentation(); + if (instr != null && instr.mHasBackgroundActivityStartsPermission) { + return true; + } + } + + final boolean hasAllowBackgroundActivityStartsToken = r.app != null + ? !r.app.mAllowBackgroundActivityStartsTokens.isEmpty() : false; + if (hasAllowBackgroundActivityStartsToken) { + return true; + } + + if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid) + == PERMISSION_GRANTED) { + return true; + } + // Is the calling UID at PROCESS_STATE_TOP or above? final boolean isCallingUidTopApp = appIsTopLocked(callingUid); if (isCallingUidTopApp) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ca0b03dff259..b2fb53071539 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -218,6 +218,7 @@ import android.content.pm.PathPermission; import android.content.pm.PermissionInfo; import android.content.pm.ProcessInfo; import android.content.pm.ProviderInfo; +import android.content.pm.ProviderInfoList; import android.content.pm.ResolveInfo; import android.content.pm.SELinuxUtil; import android.content.pm.ServiceInfo; @@ -5151,12 +5152,13 @@ public class ActivityManagerService extends IActivityManager.Stub if (mPlatformCompat != null) { mPlatformCompat.resetReporting(app.info); } + final ProviderInfoList providerList = ProviderInfoList.fromList(providers); if (app.isolatedEntryPoint != null) { // This is an isolated process which should just call an entry point instead of // being bound to an application. thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs); } else if (instr2 != null) { - thread.bindApplication(processName, appInfo, providers, + thread.bindApplication(processName, appInfo, providerList, instr2.mClass, profilerInfo, instr2.mArguments, instr2.mWatcher, @@ -5169,7 +5171,7 @@ public class ActivityManagerService extends IActivityManager.Stub buildSerial, autofillOptions, contentCaptureOptions, app.mDisabledCompatChanges); } else { - thread.bindApplication(processName, appInfo, providers, null, profilerInfo, + thread.bindApplication(processName, appInfo, providerList, null, profilerInfo, null, null, null, testMode, mBinderTransactionTrackingEnabled, enableTrackAllocation, isRestrictedBackupMode || !normalMode, app.isPersistent(), @@ -6285,9 +6287,9 @@ public class ActivityManagerService extends IActivityManager.Stub } @VisibleForTesting - public void grantImplicitAccess(int userId, Intent intent, int callingUid, int targetAppId) { + public void grantImplicitAccess(int userId, Intent intent, int visibleUid, int recipientAppId) { getPackageManagerInternalLocked(). - grantImplicitAccess(userId, intent, callingUid, targetAppId); + grantImplicitAccess(userId, intent, recipientAppId, visibleUid, true /*direct*/); } /** @@ -6513,14 +6515,6 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds, - Rect tempDockedTaskInsetBounds, - Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) { - mActivityTaskManager.resizeDockedStack(dockedBounds, tempDockedTaskBounds, - tempDockedTaskInsetBounds, tempOtherTaskBounds, tempOtherTaskInsetBounds); - } - - @Override public void positionTaskInStack(int taskId, int stackId, int position) { mActivityTaskManager.positionTaskInStack(taskId, stackId, position); } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index c7f5f630ca1a..2941e77fbb67 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -2570,10 +2570,6 @@ final class ActivityManagerShellCommand extends ShellCommand { switch (op) { case "move-task": return runStackMoveTask(pw); - case "resize-animated": - return runStackResizeAnimated(pw); - case "resize-docked-stack": - return runStackResizeDocked(pw); case "positiontask": return runStackPositionTask(pw); case "list": @@ -2648,34 +2644,6 @@ final class ActivityManagerShellCommand extends ShellCommand { return 0; } - int runStackResizeAnimated(PrintWriter pw) throws RemoteException { - String stackIdStr = getNextArgRequired(); - int stackId = Integer.parseInt(stackIdStr); - final Rect bounds; - if ("null".equals(peekNextArg())) { - bounds = null; - } else { - bounds = getBounds(); - if (bounds == null) { - getErrPrintWriter().println("Error: invalid input bounds"); - return -1; - } - } - mTaskInterface.animateResizePinnedStack(stackId, bounds, -1); - return 0; - } - - int runStackResizeDocked(PrintWriter pw) throws RemoteException { - final Rect bounds = getBounds(); - final Rect taskBounds = getBounds(); - if (bounds == null || taskBounds == null) { - getErrPrintWriter().println("Error: invalid input bounds"); - return -1; - } - mTaskInterface.resizeDockedStack(bounds, taskBounds, null, null, null); - return 0; - } - int runStackPositionTask(PrintWriter pw) throws RemoteException { String taskIdStr = getNextArgRequired(); int taskId = Integer.parseInt(taskIdStr); @@ -3285,8 +3253,6 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" move-task <TASK_ID> <STACK_ID> [true|false]"); pw.println(" Move <TASK_ID> from its current stack to the top (true) or"); pw.println(" bottom (false) of <STACK_ID>."); - pw.println(" resize-animated <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>"); - pw.println(" Same as resize, but allow animation."); pw.println(" resize-docked-stack <LEFT,TOP,RIGHT,BOTTOM> [<TASK_LEFT,TASK_TOP,TASK_RIGHT,TASK_BOTTOM>]"); pw.println(" Change docked stack to <LEFT,TOP,RIGHT,BOTTOM>"); pw.println(" and supplying temporary different task bounds indicated by"); diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index fb48db4a5921..a7125b4a1151 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -146,6 +146,7 @@ class UserController implements Handler.Callback { static final int REPORT_USER_SWITCH_COMPLETE_MSG = 80; static final int USER_SWITCH_CALLBACKS_TIMEOUT_MSG = 90; static final int USER_UNLOCK_MSG = 100; + static final int USER_UNLOCKED_MSG = 105; static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 110; static final int START_USER_SWITCH_FG_MSG = 120; @@ -625,6 +626,9 @@ class UserController implements Handler.Callback { FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FRAMEWORK_BOOT_COMPLETED, elapsedTimeMs); } + + mHandler.obtainMessage(USER_UNLOCKED_MSG, userId, 0).sendToTarget(); + final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null); bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT @@ -2366,6 +2370,9 @@ class UserController implements Handler.Callback { }); finishUserUnlocked((UserState) msg.obj); break; + case USER_UNLOCKED_MSG: + mInjector.getSystemServiceManager().onUserUnlocked(msg.arg1); + break; case USER_CURRENT_MSG: mInjector.batteryStatsServiceNoteEvent( BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_FINISH, diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 7cb84585a57c..612fd39634ea 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -178,6 +178,8 @@ public class DisplayModeDirector { float maxRefreshRate = Float.POSITIVE_INFINITY; int lowestConsideredPriority = Vote.MIN_PRIORITY; while (lowestConsideredPriority <= Vote.MAX_PRIORITY) { + minRefreshRate = 0f; + maxRefreshRate = Float.POSITIVE_INFINITY; int height = Vote.INVALID_SIZE; int width = Vote.INVALID_SIZE; diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java index fbad8dede87d..532045320988 100644 --- a/services/core/java/com/android/server/dreams/DreamController.java +++ b/services/core/java/com/android/server/dreams/DreamController.java @@ -16,9 +16,6 @@ package com.android.server.dreams; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; - import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -27,10 +24,10 @@ import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.IBinder.DeathRecipient; import android.os.IRemoteCallback; import android.os.PowerManager; import android.os.RemoteException; -import android.os.IBinder.DeathRecipient; import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; @@ -38,15 +35,14 @@ import android.service.dreams.DreamService; import android.service.dreams.IDreamService; import android.util.Slog; import android.view.IWindowManager; -import android.view.WindowManager; import android.view.WindowManagerGlobal; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; + import java.io.PrintWriter; import java.util.NoSuchElementException; -import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.WindowManager.LayoutParams.TYPE_DREAM; - /** * Internal controller for starting and stopping the current dream and managing related state. * @@ -86,12 +82,9 @@ final class DreamController { } }; - private final Runnable mStopStubbornDreamRunnable = new Runnable() { - @Override - public void run() { - Slog.w(TAG, "Stubborn dream did not finish itself in the time allotted"); - stopDream(true /*immediate*/); - } + private final Runnable mStopStubbornDreamRunnable = () -> { + Slog.w(TAG, "Stubborn dream did not finish itself in the time allotted"); + stopDream(true /*immediate*/); }; public DreamController(Context context, Handler handler, Listener listener) { @@ -140,14 +133,6 @@ final class DreamController { MetricsLogger.visible(mContext, mCurrentDream.mCanDoze ? MetricsEvent.DOZING : MetricsEvent.DREAMING); - try { - mIWindowManager.addWindowToken(token, TYPE_DREAM, DEFAULT_DISPLAY); - } catch (RemoteException ex) { - Slog.e(TAG, "Unable to add window token for dream.", ex); - stopDream(true /*immediate*/); - return; - } - Intent intent = new Intent(DreamService.SERVICE_INTERFACE); intent.setComponent(name); intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); @@ -216,9 +201,6 @@ final class DreamController { } if (oldDream.mService != null) { - // Tell the dream that it's being stopped so that - // it can shut down nicely before we yank its window token out from - // under it. try { oldDream.mService.detach(); } catch (RemoteException ex) { @@ -238,18 +220,7 @@ final class DreamController { } oldDream.releaseWakeLockIfNeeded(); - try { - mIWindowManager.removeWindowToken(oldDream.mToken, DEFAULT_DISPLAY); - } catch (RemoteException ex) { - Slog.w(TAG, "Error removing window token for dream.", ex); - } - - mHandler.post(new Runnable() { - @Override - public void run() { - mListener.onDreamStopped(oldDream.mToken); - } - }); + mHandler.post(() -> mListener.onDreamStopped(oldDream.mToken)); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } @@ -313,13 +284,10 @@ final class DreamController { // May be called on any thread. @Override public void binderDied() { - mHandler.post(new Runnable() { - @Override - public void run() { - mService = null; - if (mCurrentDream == DreamRecord.this) { - stopDream(true /*immediate*/); - } + mHandler.post(() -> { + mService = null; + if (mCurrentDream == DreamRecord.this) { + stopDream(true /*immediate*/); } }); } @@ -327,16 +295,13 @@ final class DreamController { // May be called on any thread. @Override public void onServiceConnected(ComponentName name, final IBinder service) { - mHandler.post(new Runnable() { - @Override - public void run() { - mConnected = true; - if (mCurrentDream == DreamRecord.this && mService == null) { - attach(IDreamService.Stub.asInterface(service)); - // Wake lock will be released once dreaming starts. - } else { - releaseWakeLockIfNeeded(); - } + mHandler.post(() -> { + mConnected = true; + if (mCurrentDream == DreamRecord.this && mService == null) { + attach(IDreamService.Stub.asInterface(service)); + // Wake lock will be released once dreaming starts. + } else { + releaseWakeLockIfNeeded(); } }); } @@ -344,13 +309,10 @@ final class DreamController { // May be called on any thread. @Override public void onServiceDisconnected(ComponentName name) { - mHandler.post(new Runnable() { - @Override - public void run() { - mService = null; - if (mCurrentDream == DreamRecord.this) { - stopDream(true /*immediate*/); - } + mHandler.post(() -> { + mService = null; + if (mCurrentDream == DreamRecord.this) { + stopDream(true /*immediate*/); } }); } @@ -373,4 +335,4 @@ final class DreamController { } }; } -}
\ No newline at end of file +} diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 3052e3cdcd21..eb0257e95b6c 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -268,6 +268,10 @@ public final class DreamManagerService extends SystemService { } } + private ComponentName getActiveDreamComponentInternal(boolean doze) { + return chooseDreamForUser(doze, ActivityManager.getCurrentUser()); + } + private ComponentName chooseDreamForUser(boolean doze, int userId) { if (doze) { ComponentName dozeComponent = getDozeComponent(userId); @@ -501,12 +505,18 @@ public final class DreamManagerService extends SystemService { @Override // Binder call public ComponentName[] getDreamComponents() { + return getDreamComponentsForUser(UserHandle.getCallingUserId()); + } + + @Override // Binder call + public ComponentName[] getDreamComponentsForUser(int userId) { checkPermission(android.Manifest.permission.READ_DREAM_STATE); + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), userId, false, true, "getDreamComponents", null); - final int userId = UserHandle.getCallingUserId(); final long ident = Binder.clearCallingIdentity(); try { - return getDreamComponentsForUser(userId); + return DreamManagerService.this.getDreamComponentsForUser(userId); } finally { Binder.restoreCallingIdentity(ident); } @@ -526,13 +536,28 @@ public final class DreamManagerService extends SystemService { } @Override // Binder call - public ComponentName getDefaultDreamComponent() { + public void setDreamComponentsForUser(int userId, ComponentName[] componentNames) { + checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), userId, false, true, "setDreamComponents", null); + + final long ident = Binder.clearCallingIdentity(); + try { + DreamManagerService.this.setDreamComponentsForUser(userId, componentNames); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call + public ComponentName getDefaultDreamComponentForUser(int userId) { checkPermission(android.Manifest.permission.READ_DREAM_STATE); + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), userId, false, true, "getDefaultDreamComponent", null); - final int userId = UserHandle.getCallingUserId(); final long ident = Binder.clearCallingIdentity(); try { - return getDefaultDreamComponentForUser(userId); + return DreamManagerService.this.getDefaultDreamComponentForUser(userId); } finally { Binder.restoreCallingIdentity(ident); } @@ -563,24 +588,25 @@ public final class DreamManagerService extends SystemService { } @Override // Binder call - public void testDream(ComponentName dream) { + public void testDream(int userId, ComponentName dream) { if (dream == null) { throw new IllegalArgumentException("dream must not be null"); } checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), userId, false, true, "testDream", null); - final int callingUserId = UserHandle.getCallingUserId(); final int currentUserId = ActivityManager.getCurrentUser(); - if (callingUserId != currentUserId) { + if (userId != currentUserId) { // This check is inherently prone to races but at least it's something. Slog.w(TAG, "Aborted attempt to start a test dream while a different " - + " user is active: callingUserId=" + callingUserId + + " user is active: userId=" + userId + ", currentUserId=" + currentUserId); return; } final long ident = Binder.clearCallingIdentity(); try { - testDreamInternal(dream, callingUserId); + testDreamInternal(dream, userId); } finally { Binder.restoreCallingIdentity(ident); } @@ -671,6 +697,11 @@ public final class DreamManagerService extends SystemService { public boolean isDreaming() { return isDreamingInternal(); } + + @Override + public ComponentName getActiveDreamComponent(boolean doze) { + return getActiveDreamComponentInternal(doze); + } } private final Runnable mSystemPropertiesChanged = new Runnable() { diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java index 8206fef90ab7..2672f848f192 100644 --- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java +++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java @@ -713,7 +713,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem /** * Gets a list of all supported users (i.e., those that pass the - * {@link #isSupportedUser(TargetUser)}check). + * {@link #isUserSupported(TargetUser)}check). */ @NonNull protected List<UserInfo> getSupportedUsers() { @@ -722,7 +722,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem final List<UserInfo> supportedUsers = new ArrayList<>(size); for (int i = 0; i < size; i++) { final UserInfo userInfo = allUsers[i]; - if (isSupportedUser(new TargetUser(userInfo))) { + if (isUserSupported(new TargetUser(userInfo))) { supportedUsers.add(userInfo); } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java index eac2d24c3dab..f24699a6ae64 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java @@ -21,6 +21,7 @@ import android.annotation.UserIdInt; import android.view.inputmethod.InlineSuggestionsRequest; import android.view.inputmethod.InputMethodInfo; +import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.view.IInlineSuggestionsRequestCallback; import com.android.internal.view.InlineSuggestionsRequestInfo; import com.android.server.LocalServices; @@ -51,7 +52,7 @@ public abstract class InputMethodManagerInternal { /** * Hides the current input method, if visible. */ - public abstract void hideCurrentInputMethod(); + public abstract void hideCurrentInputMethod(@SoftInputShowHideReason int reason); /** * Returns the list of installed input methods for the specified user. @@ -106,7 +107,7 @@ public abstract class InputMethodManagerInternal { } @Override - public void hideCurrentInputMethod() { + public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) { } @Override diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 87262a844b18..e3c545c3cf28 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -131,6 +131,7 @@ import com.android.internal.content.PackageMonitor; import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; import com.android.internal.inputmethod.InputMethodDebug; +import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.inputmethod.StartInputFlags; import com.android.internal.inputmethod.StartInputReason; import com.android.internal.inputmethod.UnbindReason; @@ -767,6 +768,75 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("mMethodMap") private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>(); + private static final class SoftInputShowHideHistory { + private Entry[] mEntries = new Entry[16]; + private int mNextIndex = 0; + private static final AtomicInteger sSequenceNumber = new AtomicInteger(0); + + // TODO(b/141738570): add requestWindowToken to track who request show / hide softInput. + private static final class Entry { + ClientState mClientState; + String mFocusedWindowString; + @SoftInputModeFlags + int mFocusedWindowSoftInputMode; + @SoftInputShowHideReason + int mReason; + boolean mRequestShowKeyboard; + // The timing of handling MSG_SHOW_SOFT_INPUT or MSG_HIDE_SOFT_INPUT. + long mTimestamp; + long mWallTime; + int mTargetDisplayId; + + Entry(ClientState client, String focusedWindow, @SoftInputModeFlags int softInputMode, + @SoftInputShowHideReason int reason, boolean show) { + mClientState = client; + mFocusedWindowString = focusedWindow; + mFocusedWindowSoftInputMode = softInputMode; + mReason = reason; + mRequestShowKeyboard = show; + mTimestamp = SystemClock.uptimeMillis(); + mWallTime = System.currentTimeMillis(); + } + } + + void addEntry(@NonNull Entry entry) { + final int index = mNextIndex; + mEntries[index] = entry; + mNextIndex = (mNextIndex + 1) % mEntries.length; + } + + void dump(@NonNull PrintWriter pw, @NonNull String prefix) { + final SimpleDateFormat dataFormat = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US); + + for (int i = 0; i < mEntries.length; ++i) { + final Entry entry = mEntries[(i + mNextIndex) % mEntries.length]; + if (entry == null) { + continue; + } + pw.print(prefix); + pw.println("SoftInputShowHideHistory #" + sSequenceNumber.getAndIncrement() + ":"); + + pw.print(prefix); + pw.println(" time=" + dataFormat.format(new Date(entry.mWallTime)) + + " (timestamp=" + entry.mTimestamp + ")"); + + pw.print(prefix); + pw.print(" requestShowKeyboard=" + entry.mRequestShowKeyboard); + pw.print(" targetDisplayId=" + entry.mTargetDisplayId); + pw.println(" reason=" + entry.mReason); + + pw.print(prefix); + pw.print(" requestClient=" + entry.mClientState); + pw.println(" focusedWindow=" + entry.mFocusedWindowString); + + pw.print(prefix); + pw.println(" focusedWindowSoftInputMode=" + InputMethodDebug.softInputModeToString( + entry.mFocusedWindowSoftInputMode)); + } + } + } + /** * Map of generated token to windowToken that is requesting * {@link InputMethodManager#showSoftInput(View, int)}. @@ -933,6 +1003,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @NonNull private final StartInputHistory mStartInputHistory = new StartInputHistory(); + @GuardedBy("mMethodMap") + @NonNull + private final SoftInputShowHideHistory mSoftInputShowHideHistory = + new SoftInputShowHideHistory(); + class SettingsObserver extends ContentObserver { int mUserId; boolean mRegistered = false; @@ -989,11 +1064,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub == AccessibilityService.SHOW_MODE_HIDDEN; if (mAccessibilityRequestingNoSoftKeyboard) { final boolean showRequested = mShowRequested; - hideCurrentInputLocked(0, null); + hideCurrentInputLocked(0, null, + SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE); mShowRequested = showRequested; } else if (mShowRequested) { - showCurrentInputLocked( - mCurFocusedWindow, InputMethodManager.SHOW_IMPLICIT, null); + showCurrentInputLocked(mCurFocusedWindow, + InputMethodManager.SHOW_IMPLICIT, null, + SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE); } } else { boolean enabledChanged = false; @@ -1618,7 +1695,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // TODO: Is it really possible that switchUserLocked() happens before system ready? if (mSystemReady) { - hideCurrentInputLocked(0, null); + hideCurrentInputLocked(0, null, SoftInputShowHideReason.HIDE_SWITCH_USER); resetCurrentMethodAndClient(UnbindReason.SWITCH_USER); buildInputMethodListLocked(initialUserSwitch); if (TextUtils.isEmpty(mSettings.getSelectedInputMethod())) { @@ -1882,7 +1959,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub executeOrSendMessage(mCurMethod, mCaller.obtainMessageOOO(MSG_INLINE_SUGGESTIONS_REQUEST, mCurMethod, requestInfo, new InlineSuggestionsRequestCallbackDecorator(callback, - imi.getPackageName()))); + imi.getPackageName(), mCurTokenDisplayId))); } else { callback.onInlineSuggestionsUnsupported(); } @@ -1902,11 +1979,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @NonNull private final String mImePackageName; + private final int mImeDisplayId; + InlineSuggestionsRequestCallbackDecorator( @NonNull IInlineSuggestionsRequestCallback callback, - @NonNull String imePackageName) { + @NonNull String imePackageName, int displayId) { mCallback = callback; mImePackageName = imePackageName; + mImeDisplayId = displayId; } @Override @@ -1923,6 +2003,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + "] doesn't match the IME package name=[" + mImePackageName + "]."); } + request.setHostDisplayId(mImeDisplayId); mCallback.onInlineSuggestionsRequest(request, callback); } } @@ -2154,7 +2235,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub startInputToken, session, mCurInputContext, mCurAttribute)); if (mShowRequested) { if (DEBUG) Slog.v(TAG, "Attach new input asks to show input"); - showCurrentInputLocked(mCurFocusedWindow, getAppShowFlags(), null); + showCurrentInputLocked(mCurFocusedWindow, getAppShowFlags(), null, + SoftInputShowHideReason.ATTACH_NEW_INPUT); } return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION, session.session, (session.channel != null ? session.channel.dup() : null), @@ -2893,7 +2975,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } if (DEBUG) Slog.v(TAG, "Client requesting input be shown"); - return showCurrentInputLocked(windowToken, flags, resultReceiver); + return showCurrentInputLocked(windowToken, flags, resultReceiver, + SoftInputShowHideReason.SHOW_SOFT_INPUT); } finally { Binder.restoreCallingIdentity(ident); } @@ -2901,7 +2984,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @GuardedBy("mMethodMap") - boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver) { + boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver, + @SoftInputShowHideReason int reason) { mShowRequested = true; if (mAccessibilityRequestingNoSoftKeyboard) { return false; @@ -2924,9 +3008,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // create a dummy token for IMS so that IMS cannot inject windows into client app. Binder showInputToken = new Binder(); mShowRequestWindowMap.put(showInputToken, windowToken); - executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOOO( - MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod, - resultReceiver, showInputToken)); + executeOrSendMessage(mCurMethod, mCaller.obtainMessageIIOOO( + MSG_SHOW_SOFT_INPUT, getImeShowFlags(), reason, mCurMethod, resultReceiver, + showInputToken)); mInputShown = true; if (mHaveConnection && !mVisibleBound) { bindCurrentInputMethodServiceLocked( @@ -2984,14 +3068,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (DEBUG) Slog.v(TAG, "Client requesting input be hidden"); - return hideCurrentInputLocked(flags, resultReceiver); + return hideCurrentInputLocked(flags, resultReceiver, + SoftInputShowHideReason.HIDE_SOFT_INPUT); } finally { Binder.restoreCallingIdentity(ident); } } } - boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver) { + boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver, + @SoftInputShowHideReason int reason) { if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0 && (mShowExplicitlyRequested || mShowForced)) { if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide"); @@ -3018,8 +3104,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // delivered to the IME process as an IPC. Hence the inconsistency between // IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in // the final state. - executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( - MSG_HIDE_SOFT_INPUT, mCurMethod, resultReceiver)); + executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(MSG_HIDE_SOFT_INPUT, + reason, mCurMethod, resultReceiver)); res = true; } else { res = false; @@ -3156,7 +3242,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Slog.w(TAG, "If you need to impersonate a foreground user/profile from" + " a background user, use EditorInfo.targetInputMethodUser with" + " INTERACT_ACROSS_USERS_FULL permission."); - hideCurrentInputLocked(0, null); + hideCurrentInputLocked(0, null, SoftInputShowHideReason.HIDE_INVALID_USER); return InputBindResult.INVALID_USER; } @@ -3219,7 +3305,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // be behind any soft input window, so hide the // soft input window if it is shown. if (DEBUG) Slog.v(TAG, "Unspecified window will hide input"); - hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null); + hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null, + SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW); // If focused display changed, we should unbind current method // to make app window in previous display relayout after Ime @@ -3245,7 +3332,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub attribute, startInputFlags, startInputReason); didStart = true; } - showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null); + showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null, + SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV); } break; case LayoutParams.SOFT_INPUT_STATE_UNCHANGED: @@ -3254,12 +3342,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub case LayoutParams.SOFT_INPUT_STATE_HIDDEN: if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { if (DEBUG) Slog.v(TAG, "Window asks to hide input going forward"); - hideCurrentInputLocked(0, null); + hideCurrentInputLocked(0, null, + SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV); } break; case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN: if (DEBUG) Slog.v(TAG, "Window asks to hide input"); - hideCurrentInputLocked(0, null); + hideCurrentInputLocked(0, null, + SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE); break; case LayoutParams.SOFT_INPUT_STATE_VISIBLE: if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { @@ -3271,7 +3361,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub attribute, startInputFlags, startInputReason); didStart = true; } - showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null); + showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null, + SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV); } else { Slog.e(TAG, "SOFT_INPUT_STATE_VISIBLE is ignored because" + " there is no focused view that also returns true from" @@ -3288,7 +3379,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub attribute, startInputFlags, startInputReason); didStart = true; } - showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null); + showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null, + SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE); } else { Slog.e(TAG, "SOFT_INPUT_STATE_ALWAYS_VISIBLE is ignored because" + " there is no focused view that also returns true from" @@ -3780,7 +3872,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } long ident = Binder.clearCallingIdentity(); try { - hideCurrentInputLocked(flags, null); + hideCurrentInputLocked(flags, null, SoftInputShowHideReason.HIDE_MY_SOFT_INPUT); } finally { Binder.restoreCallingIdentity(ident); } @@ -3795,7 +3887,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } long ident = Binder.clearCallingIdentity(); try { - showCurrentInputLocked(mLastImeTargetWindow, flags, null); + showCurrentInputLocked(mLastImeTargetWindow, flags, null, + SoftInputShowHideReason.SHOW_MY_SOFT_INPUT); } finally { Binder.restoreCallingIdentity(ident); } @@ -3878,10 +3971,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub case MSG_SHOW_SOFT_INPUT: args = (SomeArgs)msg.obj; try { + final @SoftInputShowHideReason int reason = msg.arg2; if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput(" - + msg.arg1 + ", " + args.arg2 + ")"); + + msg.arg1 + ", " + args.arg2 + ") for reason: " + + InputMethodDebug.softInputDisplayReasonToString(reason)); ((IInputMethod) args.arg1).showSoftInput( (IBinder) args.arg3, msg.arg1, (ResultReceiver) args.arg2); + mSoftInputShowHideHistory.addEntry( + new SoftInputShowHideHistory.Entry(mCurClient, + InputMethodDebug.objToString(mCurFocusedWindow), + mCurFocusedWindowSoftInputMode, reason, true /* show */)); } catch (RemoteException e) { } args.recycle(); @@ -3889,16 +3988,23 @@ public class InputMethodManagerService extends IInputMethodManager.Stub case MSG_HIDE_SOFT_INPUT: args = (SomeArgs)msg.obj; try { + final @SoftInputShowHideReason int reason = msg.arg1; if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, " - + args.arg2 + ")"); + + args.arg2 + ") for reason: " + + InputMethodDebug.softInputDisplayReasonToString(reason)); ((IInputMethod)args.arg1).hideSoftInput(0, (ResultReceiver)args.arg2); + mSoftInputShowHideHistory.addEntry( + new SoftInputShowHideHistory.Entry(mCurClient, + InputMethodDebug.objToString(mCurFocusedWindow), + mCurFocusedWindowSoftInputMode, reason, false /* show */)); } catch (RemoteException e) { } args.recycle(); return true; case MSG_HIDE_CURRENT_INPUT_METHOD: synchronized (mMethodMap) { - hideCurrentInputLocked(0, null); + final @SoftInputShowHideReason int reason = (int) msg.obj; + hideCurrentInputLocked(0, null, reason); } return true; case MSG_INITIALIZE_IME: @@ -4682,9 +4788,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @Override - public void hideCurrentInputMethod() { + public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) { mService.mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD); - mService.mHandler.sendEmptyMessage(MSG_HIDE_CURRENT_INPUT_METHOD); + mService.mHandler.obtainMessage(MSG_HIDE_CURRENT_INPUT_METHOD, reason).sendToTarget(); } @Override @@ -4841,6 +4947,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub p.println(" mStartInputHistory:"); mStartInputHistory.dump(pw, " "); + + p.println(" mSoftInputShowHideHistory:"); + mSoftInputShowHideHistory.dump(pw, " "); } p.println(" "); @@ -5300,7 +5409,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final String nextIme; final List<InputMethodInfo> nextEnabledImes; if (userId == mSettings.getCurrentUserId()) { - hideCurrentInputLocked(0, null); + hideCurrentInputLocked(0, null, + SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND); unbindCurrentMethodLocked(); // Reset the current IME resetSelectedInputMethodAndSubtypeLocked(null); diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 4904061ec1af..1aff23b09c0f 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -73,6 +73,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.inputmethod.IMultiClientInputMethod; import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations; import com.android.internal.inputmethod.IMultiClientInputMethodSession; +import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.inputmethod.StartInputFlags; import com.android.internal.inputmethod.StartInputReason; import com.android.internal.inputmethod.UnbindReason; @@ -174,7 +175,7 @@ public final class MultiClientInputMethodManagerService { } @Override - public void hideCurrentInputMethod() { + public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) { reportNotSupported(); } diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index 63054cf0c516..fd8e159bf791 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -40,7 +40,6 @@ import android.content.integrity.Rule; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; -import android.content.pm.PackageParser; import android.content.pm.PackageUserState; import android.content.pm.ParceledListSlice; import android.content.pm.Signature; @@ -202,7 +201,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { intent, /* onFinished= */ null, /* handler= */ null); - } catch (IntentSender.SendIntentException e) { + } catch (Exception e) { Slog.e(TAG, "Error sending status feedback.", e); } }); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 475f229562dd..ceb1cd41f567 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -51,6 +51,7 @@ import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT; import static android.content.Context.BIND_AUTO_CREATE; import static android.content.Context.BIND_FOREGROUND_SERVICE; import static android.content.Context.BIND_NOT_PERCEPTIBLE; +import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED; import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC; import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED; import static android.content.pm.PackageManager.FEATURE_LEANBACK; @@ -380,7 +381,7 @@ public class NotificationManagerService extends SystemService { private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2; private static final long MIN_PACKAGE_OVERRATE_LOG_INTERVAL = 5000; // milliseconds - private static final long DELAY_FOR_ASSISTANT_TIME = 100; + private static final long DELAY_FOR_ASSISTANT_TIME = 200; private static final String ACTION_NOTIFICATION_TIMEOUT = NotificationManagerService.class.getSimpleName() + ".TIMEOUT"; @@ -3448,16 +3449,10 @@ public class NotificationManagerService extends SystemService { ArrayList<ConversationChannelWrapper> conversations = mPreferencesHelper.getConversations(onlyImportant); for (ConversationChannelWrapper conversation : conversations) { - LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery() - .setPackage(conversation.getPkg()) - .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED) - .setShortcutIds(Arrays.asList( - conversation.getNotificationChannel().getConversationId())); - List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts( - query, UserHandle.of(UserHandle.getUserId(conversation.getUid()))); - if (shortcuts != null && !shortcuts.isEmpty()) { - conversation.setShortcutInfo(shortcuts.get(0)); - } + conversation.setShortcutInfo(getShortcutInfo( + conversation.getNotificationChannel().getConversationId(), + conversation.getPkg(), + UserHandle.of(UserHandle.getUserId(conversation.getUid())))); } return new ParceledListSlice<>(conversations); } @@ -3477,16 +3472,10 @@ public class NotificationManagerService extends SystemService { ArrayList<ConversationChannelWrapper> conversations = mPreferencesHelper.getConversations(pkg, uid); for (ConversationChannelWrapper conversation : conversations) { - LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery() - .setPackage(pkg) - .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED) - .setShortcutIds(Arrays.asList( - conversation.getNotificationChannel().getConversationId())); - List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts( - query, UserHandle.of(UserHandle.getUserId(uid))); - if (shortcuts != null && !shortcuts.isEmpty()) { - conversation.setShortcutInfo(shortcuts.get(0)); - } + conversation.setShortcutInfo(getShortcutInfo( + conversation.getNotificationChannel().getConversationId(), + pkg, + UserHandle.of(UserHandle.getUserId(uid)))); } return new ParceledListSlice<>(conversations); } @@ -5646,6 +5635,8 @@ public class NotificationManagerService extends SystemService { } } + r.setShortcutInfo(getShortcutInfo(notification.getShortcutId(), pkg, user)); + if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r, r.getSbn().getOverrideGroupKey() != null)) { return; @@ -5959,20 +5950,33 @@ public class NotificationManagerService extends SystemService { return false; } - private boolean hasValidShortcutInfo(String shortcutId, String packageName, UserHandle user) { - LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery(); - if (packageName != null) { - query.setPackage(packageName); - } - if (shortcutId != null) { - query.setShortcutIds(Arrays.asList(shortcutId)); + private ShortcutInfo getShortcutInfo(String shortcutId, String packageName, UserHandle user) { + final long token = Binder.clearCallingIdentity(); + try { + if (shortcutId == null || packageName == null || user == null) { + return null; + } + LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery(); + if (packageName != null) { + query.setPackage(packageName); + } + if (shortcutId != null) { + query.setShortcutIds(Arrays.asList(shortcutId)); + } + query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED); + List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user); + ShortcutInfo shortcutInfo = shortcuts != null && shortcuts.size() > 0 + ? shortcuts.get(0) + : null; + return shortcutInfo; + } finally { + Binder.restoreCallingIdentity(token); } - query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED); - List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user); - ShortcutInfo shortcutInfo = shortcuts != null && shortcuts.size() > 0 - ? shortcuts.get(0) - : null; - return shortcutInfo != null; + } + + private boolean hasValidShortcutInfo(String shortcutId, String packageName, UserHandle user) { + ShortcutInfo shortcutInfo = getShortcutInfo(shortcutId, packageName, user); + return shortcutInfo != null && shortcutInfo.isLongLived(); } private void logBubbleError(String key, String failureMessage) { diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index f92e1fcddcf2..9d243e4d75a9 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -35,6 +35,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; +import android.content.pm.ShortcutInfo; import android.graphics.Bitmap; import android.media.AudioAttributes; import android.media.AudioSystem; @@ -166,6 +167,7 @@ public final class NotificationRecord { private boolean mAllowBubble; private Light mLight; private boolean mIsNotConversationOverride; + private ShortcutInfo mShortcutInfo; /** * This list contains system generated smart actions from NAS, app-generated smart actions are * stored in Notification.actions with isContextual() set to true. @@ -1338,14 +1340,20 @@ public final class NotificationRecord { return hasCustomRemoteView && !hasDecoratedStyle; } - /** Whether this notification is a conversation notification. */ + public void setShortcutInfo(ShortcutInfo shortcutInfo) { + mShortcutInfo = shortcutInfo; + } + + /** + * Whether this notification is a conversation notification. + */ public boolean isConversation() { Notification notification = getNotification(); if (mChannel.isDemoted() || !Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) { return false; } - if (notification.getShortcutId() == null + if (mShortcutInfo == null && !FeatureFlagUtils.isEnabled( mContext, FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ)) { return false; @@ -1353,7 +1361,6 @@ public final class NotificationRecord { if (mIsNotConversationOverride) { return false; } - // STOPSHIP b/137397357: Check shortcut to make a further decision return true; } diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index ae6e058ee931..0ad0b2373a79 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -354,14 +354,13 @@ public class AppsFilter { * Grants access based on an interaction between a calling and target package, granting * visibility of the caller from the target. * - * @param callingUid the uid initiating the interaction - * @param targetUid the uid being interacted with and thus gaining visibility of the - * initiating uid. + * @param recipientUid the uid gaining visibility of the {@code visibleUid}. + * @param visibleUid the uid becoming visible to the {@recipientUid} */ - public void grantImplicitAccess(int callingUid, int targetUid) { - if (targetUid != callingUid - && mImplicitlyQueryable.add(targetUid, callingUid) && DEBUG_LOGGING) { - Slog.wtf(TAG, "implicit access granted: " + targetUid + " -> " + callingUid); + public void grantImplicitAccess(int recipientUid, int visibleUid) { + if (recipientUid != visibleUid + && mImplicitlyQueryable.add(recipientUid, visibleUid) && DEBUG_LOGGING) { + Slog.wtf(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid); } } diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java index 85810e3a9954..f497f114c05f 100644 --- a/services/core/java/com/android/server/pm/ComponentResolver.java +++ b/services/core/java/com/android/server/pm/ComponentResolver.java @@ -28,6 +28,7 @@ import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.AuxiliaryResolveInfo; import android.content.pm.InstantAppResolveInfo; import android.content.pm.PackageManager; @@ -56,6 +57,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ArrayUtils; import com.android.server.IntentResolver; import com.android.server.pm.parsing.PackageInfoUtils; +import com.android.server.pm.parsing.PackageInfoUtils.CachedApplicationInfoGenerator; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.PrintWriter; @@ -272,6 +274,7 @@ public class ComponentResolver { return null; } List<ProviderInfo> providerList = null; + CachedApplicationInfoGenerator appInfoGenerator = null; synchronized (mLock) { for (int i = mProviders.mProviders.size() - 1; i >= 0; --i) { final ParsedProvider p = mProviders.mProviders.valueAt(i); @@ -300,8 +303,18 @@ public class ComponentResolver { && (p.getMetaData() == null || !p.getMetaData().containsKey(metaDataKey))) { continue; } + if (appInfoGenerator == null) { + appInfoGenerator = new CachedApplicationInfoGenerator(); + } + final PackageUserState state = ps.readUserState(userId); + final ApplicationInfo appInfo = + appInfoGenerator.generate(pkg, flags, state, userId, ps); + if (appInfo == null) { + continue; + } + final ProviderInfo info = PackageInfoUtils.generateProviderInfo( - pkg, p, flags, ps.readUserState(userId), userId, ps); + pkg, p, flags, state, appInfo, userId, ps); if (info == null) { continue; } @@ -330,14 +343,20 @@ public class ComponentResolver { if (pkg == null) { return null; } - return PackageInfoUtils.generateProviderInfo(pkg, p, flags, - ps.readUserState(userId), userId, ps); + final PackageUserState state = ps.readUserState(userId); + ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo( + pkg, flags, state, userId, ps); + if (appInfo == null) { + return null; + } + return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, appInfo, userId, ps); } } void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo, boolean safeMode, int userId) { synchronized (mLock) { + CachedApplicationInfoGenerator appInfoGenerator = null; for (int i = mProvidersByAuthority.size() - 1; i >= 0; --i) { final ParsedProvider p = mProvidersByAuthority.valueAt(i); if (!p.isSyncable()) { @@ -359,9 +378,18 @@ public class ComponentResolver { if (safeMode && !pkg.isSystem()) { continue; } - final ProviderInfo info = - PackageInfoUtils.generateProviderInfo(pkg, p, 0, - ps.readUserState(userId), userId, ps); + if (appInfoGenerator == null) { + appInfoGenerator = new CachedApplicationInfoGenerator(); + } + final PackageUserState state = ps.readUserState(userId); + final ApplicationInfo appInfo = + appInfoGenerator.generate(pkg, 0, state, userId, ps); + if (appInfo == null) { + continue; + } + + final ProviderInfo info = PackageInfoUtils.generateProviderInfo( + pkg, p, 0, state, appInfo, userId, ps); if (info == null) { continue; } @@ -1706,8 +1734,13 @@ public class ComponentResolver { if (userState.instantApp && ps.isUpdateAvailable()) { return null; } + final ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo( + pkg, mFlags, userState, userId, ps); + if (appInfo == null) { + return null; + } ProviderInfo pi = PackageInfoUtils.generateProviderInfo(pkg, provider, mFlags, - userState, userId, ps); + userState, appInfo, userId, ps); if (pi == null) { return null; } diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java index cf85b0f3da7a..0eaac4140c14 100644 --- a/services/core/java/com/android/server/pm/InstantAppRegistry.java +++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java @@ -403,7 +403,7 @@ class InstantAppRegistry { @GuardedBy("mService.mLock") public void grantInstantAccessLPw(@UserIdInt int userId, @Nullable Intent intent, - int instantAppId, int targetAppId) { + int recipientUid, int instantAppId) { if (mInstalledInstantAppUids == null) { return; // no instant apps installed; no need to grant } @@ -411,7 +411,7 @@ class InstantAppRegistry { if (instantAppList == null || !instantAppList.get(instantAppId)) { return; // instant app id isn't installed; no need to grant } - if (instantAppList.get(targetAppId)) { + if (instantAppList.get(recipientUid)) { return; // target app id is an instant app; no need to grant } if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) { @@ -428,10 +428,10 @@ class InstantAppRegistry { targetAppList = new SparseArray<>(); mInstantGrants.put(userId, targetAppList); } - SparseBooleanArray instantGrantList = targetAppList.get(targetAppId); + SparseBooleanArray instantGrantList = targetAppList.get(recipientUid); if (instantGrantList == null) { instantGrantList = new SparseBooleanArray(); - targetAppList.put(targetAppId, instantGrantList); + targetAppList.put(recipientUid, instantGrantList); } instantGrantList.put(instantAppId, true /*granted*/); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 077fc6f01429..f714af03336f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -5459,7 +5459,13 @@ public class PackageManagerService extends IPackageManager.Stub return null; } PackageUserState state = ps.readUserState(userId); - return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, userId, ps); + final ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo( + pkg, flags, state, userId, ps); + if (appInfo == null) { + return null; + } + return PackageInfoUtils.generateProviderInfo( + pkg, p, flags, state, appInfo, userId, ps); } } return null; @@ -10969,10 +10975,10 @@ public class PackageManagerService extends IPackageManager.Stub // to null here, only to reset them at a later point. Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting, destCodeFile, destResourceFile, parsedPackage.getNativeLibraryDir(), - AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage), - AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage), - PackageInfoWithoutStateUtils.appInfoFlags(parsedPackage), - PackageInfoWithoutStateUtils.appInfoPrivateFlags(parsedPackage), + AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting), + AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting), + PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting), + PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting), UserManagerService.getInstance(), usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups()); @@ -11164,6 +11170,8 @@ public class PackageManagerService extends IPackageManager.Stub // TODO(b/135203078): Remove, move to constructor pkgSetting.pkg = parsedPackage; pkgSetting.pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting); + pkgSetting.pkgPrivateFlags = + PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting); if (parsedPackage.getLongVersionCode() != pkgSetting.versionCode) { pkgSetting.versionCode = parsedPackage.getLongVersionCode(); } @@ -16343,7 +16351,27 @@ public class PackageManagerService extends IPackageManager.Stub REASON_INSTALL, DexoptOptions.DEXOPT_BOOT_COMPLETE | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE); - mPackageDexOptimizer.performDexOpt(pkg, reconciledPkg.pkgSetting, + ScanResult result = reconciledPkg.scanResult; + + // This mirrors logic from commitReconciledScanResultLocked, where the library files + // needed for dexopt are assigned. + // TODO: Fix this to have 1 mutable PackageSetting for scan/install. If the previous + // setting needs to be passed to have a comparison, hide it behind an immutable + // interface. There's no good reason to have 3 different ways to access the real + // PackageSetting object, only one of which is actually correct. + PackageSetting realPkgSetting = result.existingSettingCopied + ? result.request.pkgSetting : result.pkgSetting; + if (realPkgSetting == null) { + realPkgSetting = reconciledPkg.pkgSetting; + } + + // Unfortunately, the updated system app flag is only tracked on this PackageSetting + boolean isUpdatedSystemApp = reconciledPkg.pkgSetting.getPkgState() + .isUpdatedSystemApp(); + + realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp); + + mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting, null /* instructionSets */, getOrCreateCompilerPackageStats(pkg), mDexManager.getPackageUseInfoOrDefault(packageName), @@ -16954,6 +16982,7 @@ public class PackageManagerService extends IPackageManager.Stub final boolean vendor = oldPackage.isVendor(); final boolean product = oldPackage.isProduct(); final boolean odm = oldPackage.isOdm(); + final boolean systemExt = oldPackage.isSystemExt(); final @ParseFlags int systemParseFlags = parseFlags; final @ScanFlags int systemScanFlags = scanFlags | SCAN_AS_SYSTEM @@ -16961,14 +16990,14 @@ public class PackageManagerService extends IPackageManager.Stub | (oem ? SCAN_AS_OEM : 0) | (vendor ? SCAN_AS_VENDOR : 0) | (product ? SCAN_AS_PRODUCT : 0) - | (odm ? SCAN_AS_ODM : 0); + | (odm ? SCAN_AS_ODM : 0) + | (systemExt ? SCAN_AS_SYSTEM_EXT : 0); if (DEBUG_INSTALL) { Slog.d(TAG, "replaceSystemPackageLI: new=" + parsedPackage + ", old=" + oldPackage); } res.setReturnCode(PackageManager.INSTALL_SUCCEEDED); - ps.getPkgState().setUpdatedSystemApp(true); targetParseFlags = systemParseFlags; targetScanFlags = systemScanFlags; } else { // non system replace @@ -23566,22 +23595,27 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void grantImplicitAccess(int userId, Intent intent, - int callingUid, int targetAppId) { + int recipientAppId, int visibleUid, boolean direct) { synchronized (mLock) { - final AndroidPackage callingPackage = getPackage(callingUid); - final int targetUid = UserHandle.getUid(userId, targetAppId); - final AndroidPackage targetPackage = getPackage(targetUid); - if (callingPackage == null || targetPackage == null) { + final AndroidPackage visiblePackage = getPackage(visibleUid); + final int recipientUid = UserHandle.getUid(userId, recipientAppId); + if (visiblePackage == null || getPackage(recipientUid) == null) { return; } - final boolean instantApp = isInstantAppInternal(callingPackage.getPackageName(), - userId, callingUid); + final boolean instantApp = + isInstantAppInternal(visiblePackage.getPackageName(), userId, visibleUid); if (instantApp) { + if (!direct) { + // if the interaction that lead to this granting access to an instant app + // was indirect (i.e.: URI permission grant), do not actually execute the + // grant. + return; + } mInstantAppRegistry.grantInstantAccessLPw(userId, intent, - UserHandle.getAppId(callingUid), targetAppId); + recipientAppId, UserHandle.getAppId(visibleUid) /*instantAppId*/); } else { - mAppsFilter.grantImplicitAccess(callingUid, targetUid); + mAppsFilter.grantImplicitAccess(recipientUid, visibleUid); } } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index af5c53606f9f..24533184f5bf 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -521,6 +521,9 @@ public final class Settings { p.secondaryCpuAbiString, p.cpuAbiOverrideString, p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags, p.usesStaticLibraries, p.usesStaticLibrariesVersions, p.mimeGroups); + if (ret != null) { + ret.getPkgState().setUpdatedSystemApp(false); + } mDisabledSysPackages.remove(name); return ret; } diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index fe992290a3c9..7dd2e5506bb7 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -1246,7 +1246,7 @@ public class StagingManager { try { IStorageManager storageManager = PackageHelper.getStorageManager(); if (storageManager.supportsCheckpoint()) { - storageManager.startCheckpoint(1); + storageManager.startCheckpoint(2); } } catch (Exception e) { // Failed to get hold of StorageManager 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 23bdf5f101f3..4ab1f39663d0 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -48,7 +48,7 @@ import android.content.pm.parsing.component.ParsedService; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.Pair; +import android.util.Slog; import com.android.internal.util.ArrayUtils; import com.android.server.pm.PackageSetting; @@ -61,6 +61,7 @@ import libcore.util.EmptyArray; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; @@ -72,6 +73,7 @@ import java.util.Set; * @hide **/ public class PackageInfoUtils { + private static final String TAG = PackageParser2.TAG; /** * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage. @@ -315,26 +317,19 @@ public class PackageInfoUtils { */ @Nullable public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p, - @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId, - @Nullable PackageSetting pkgSetting) { - return generateProviderInfo(pkg, p, flags, state, null, userId, pkgSetting); - } - - /** - * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage. - */ - @Nullable - private static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p, @PackageManager.ComponentInfoFlags int flags, PackageUserState state, - @Nullable ApplicationInfo applicationInfo, int userId, + @NonNull ApplicationInfo applicationInfo, int userId, @Nullable PackageSetting pkgSetting) { if (p == null) return null; + if (applicationInfo == null || !pkg.getPackageName().equals(applicationInfo.packageName)) { + Slog.wtf(TAG, "AppInfo's package name is different. Expected=" + pkg.getPackageName() + + " actual=" + (applicationInfo == null ? "(null AppInfo)" + : applicationInfo.packageName)); + applicationInfo = generateApplicationInfo(pkg, flags, state, userId, pkgSetting); + } if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)) { return null; } - if (applicationInfo == null) { - applicationInfo = generateApplicationInfo(pkg, flags, state, userId, pkgSetting); - } ProviderInfo info = PackageInfoWithoutStateUtils.generateProviderInfo(pkg, p, flags, state, applicationInfo, userId); if (info == null) { @@ -480,4 +475,29 @@ public class PackageInfoUtils { | flag(pkg.isSignedWithPlatformKey(), ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY); // @formatter:on } + + /** + * Wraps {@link PackageInfoUtils#generateApplicationInfo} with a cache. + */ + public static class CachedApplicationInfoGenerator { + // Map from a package name to the corresponding app info. + private ArrayMap<String, ApplicationInfo> mCache = new ArrayMap<>(); + + /** + * {@link PackageInfoUtils#generateApplicationInfo} with a cache. + */ + @Nullable + public ApplicationInfo generate(AndroidPackage pkg, + @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId, + @Nullable PackageSetting pkgSetting) { + ApplicationInfo appInfo = mCache.get(pkg.getPackageName()); + if (appInfo != null) { + return appInfo; + } + appInfo = PackageInfoUtils.generateApplicationInfo( + pkg, flags, state, userId, pkgSetting); + mCache.put(pkg.getPackageName(), appInfo); + return appInfo; + } + } } diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java index f99791a28f46..d561b9c1ffdd 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java +++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java @@ -42,7 +42,7 @@ import java.io.File; */ public class PackageParser2 { - private static final String TAG = "PackageParser2"; + static final String TAG = "PackageParser2"; private static final boolean LOG_PARSE_TIMINGS = Build.IS_DEBUGGABLE; private static final int LOG_PARSE_TIMINGS_THRESHOLD_MS = 100; diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 840c865d4732..f647b6a94bf6 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -2794,7 +2794,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } if ((changedInstallPermission || replace) && !ps.areInstallPermissionsFixed() && - !ps.isSystem() || !ps.getPkgState().isUpdatedSystemApp()) { + !ps.isSystem() || ps.getPkgState().isUpdatedSystemApp()) { // This is the first that we have heard about this package, so the // permissions we have now selected are fixed until explicitly // changed. diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java index c008d93ca91a..e27bf48b6b61 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java +++ b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java @@ -35,6 +35,9 @@ import java.util.List; * * It is assumed that anything inside the package was not cached or written to disk, so none of * these fields are either. They must be set on every boot from other state on the device. + * + * These fields are also not copied into any cloned PackageSetting, to preserve the old behavior + * where they would be lost implicitly by re-generating the package object. */ @DataClass(genSetters = true, genConstructor = false, genBuilder = false) public class PackageStateUnserialized { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 19e6062401c0..1b5cc6a248e3 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -186,6 +186,7 @@ import android.view.autofill.AutofillManagerInternal; import com.android.internal.R; import com.android.internal.accessibility.AccessibilityShortcutController; +import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.os.RoSystemProperties; @@ -482,6 +483,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { MetricsLogger mLogger; boolean mWakeOnDpadKeyPress; boolean mWakeOnAssistKeyPress; + boolean mWakeOnBackKeyPress; private boolean mHandleVolumeKeysInWM; @@ -1105,7 +1107,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { LocalServices.getService(InputMethodManagerInternal.class); } if (mInputMethodManagerInternal != null) { - mInputMethodManagerInternal.hideCurrentInputMethod(); + mInputMethodManagerInternal.hideCurrentInputMethod( + SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME); } } else { shortPressPowerGoHome(); @@ -1736,6 +1739,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { res.getBoolean(com.android.internal.R.bool.config_wakeOnDpadKeyPress); mWakeOnAssistKeyPress = res.getBoolean(com.android.internal.R.bool.config_wakeOnAssistKeyPress); + mWakeOnBackKeyPress = + res.getBoolean(com.android.internal.R.bool.config_wakeOnBackKeyPress); // Init display burn-in protection boolean burnInProtectionEnabled = context.getResources().getBoolean( @@ -3085,7 +3090,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { event.getAction(), fallbackAction.keyCode, event.getRepeatCount(), fallbackAction.metaState, event.getDeviceId(), event.getScanCode(), - flags, event.getSource(), event.getDisplayId(), null /* hmac */, null); + flags, event.getSource(), event.getDisplayId(), null); if (!interceptFallback(focusedToken, fallbackEvent, policyFlags)) { fallbackEvent.recycle(); @@ -4107,6 +4112,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_ASSIST: return mWakeOnAssistKeyPress; + + case KeyEvent.KEYCODE_BACK: + return mWakeOnBackKeyPress; } return true; diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java index 233417da3e00..059861b65e20 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java @@ -16,8 +16,13 @@ package com.android.server.power.batterysaver; import android.annotation.IntDef; +import android.app.UiModeManager; +import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Configuration; import android.database.ContentObserver; import android.net.Uri; import android.os.BatterySaverPolicyConfig; @@ -183,18 +188,15 @@ public class BatterySaverPolicy extends ContentObserver { private String mEventLogKeys; /** - * Whether vibration should *really* be disabled -- i.e. {@link Policy#disableVibration} - * is true *and* {@link #mAccessibilityEnabled} is false. - */ - @GuardedBy("mLock") - private boolean mDisableVibrationEffective; - - /** * Whether accessibility is currently enabled or not. */ @GuardedBy("mLock") private boolean mAccessibilityEnabled; + /** Whether the phone is projecting in car mode or not. */ + @GuardedBy("mLock") + private boolean mCarModeEnabled; + /** The current default adaptive policy. */ @GuardedBy("mLock") private Policy mDefaultAdaptivePolicy = DEFAULT_ADAPTIVE_POLICY; @@ -207,6 +209,13 @@ public class BatterySaverPolicy extends ContentObserver { @GuardedBy("mLock") private Policy mFullPolicy = DEFAULT_FULL_POLICY; + /** + * The current effective policy. This is based on the current policy level's policy, with any + * required adjustments. + */ + @GuardedBy("mLock") + private Policy mEffectivePolicy = OFF_POLICY; + @IntDef(prefix = {"POLICY_LEVEL_"}, value = { POLICY_LEVEL_OFF, POLICY_LEVEL_ADAPTIVE, @@ -230,6 +239,20 @@ public class BatterySaverPolicy extends ContentObserver { private final ContentResolver mContentResolver; private final BatterySavingStats mBatterySavingStats; + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + switch (intent.getAction()) { + case UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED: + setCarModeEnabled(true); + break; + case UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED: + setCarModeEnabled(false); + break; + } + } + }; + @GuardedBy("mLock") private final List<BatterySaverPolicyListener> mListeners = new ArrayList<>(); @@ -263,16 +286,25 @@ public class BatterySaverPolicy extends ContentObserver { final AccessibilityManager acm = mContext.getSystemService(AccessibilityManager.class); - acm.addAccessibilityStateChangeListener((enabled) -> { - synchronized (mLock) { - mAccessibilityEnabled = enabled; - } - refreshSettings(); - }); - final boolean enabled = acm.isEnabled(); + acm.addAccessibilityStateChangeListener((enabled) -> setAccessibilityEnabled(enabled)); + final boolean accessibilityEnabled = acm.isEnabled(); synchronized (mLock) { - mAccessibilityEnabled = enabled; + mAccessibilityEnabled = accessibilityEnabled; } + + final IntentFilter filter = new IntentFilter( + UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED); + filter.addAction(UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED); + // The ENTER/EXIT_CAR_MODE_PRIORITIZED intents are sent to UserHandle.ALL, so no need to + // register as all users here. + mContext.registerReceiver(mBroadcastReceiver, filter); + final boolean carModeEnabled = + mContext.getSystemService(UiModeManager.class).getCurrentModeType() + == Configuration.UI_MODE_TYPE_CAR; + synchronized (mLock) { + mCarModeEnabled = carModeEnabled; + } + onChange(true, null); } @@ -299,13 +331,34 @@ public class BatterySaverPolicy extends ContentObserver { PowerManager.invalidatePowerSaveModeCaches(); } + /** + * Notifies listeners of a policy change on the handler thread only if the current policy level + * is not {@link POLICY_LEVEL_OFF}. + */ + private void maybeNotifyListenersOfPolicyChange() { + final BatterySaverPolicyListener[] listeners; + synchronized (mLock) { + if (getPolicyLevelLocked() == POLICY_LEVEL_OFF) { + // Current policy is OFF, so there's no change to notify listeners of. + return; + } + // Don't call out to listeners with the lock held. + listeners = mListeners.toArray(new BatterySaverPolicyListener[mListeners.size()]); + } + + mHandler.post(() -> { + for (BatterySaverPolicyListener listener : listeners) { + listener.onBatterySaverPolicyChanged(this); + } + }); + } + @Override public void onChange(boolean selfChange, Uri uri) { refreshSettings(); } private void refreshSettings() { - final BatterySaverPolicyListener[] listeners; synchronized (mLock) { // Load the non-device-specific setting. final String setting = getGlobalSetting(Settings.Global.BATTERY_SAVER_CONSTANTS); @@ -334,16 +387,9 @@ public class BatterySaverPolicy extends ContentObserver { // Nothing of note changed. return; } - - listeners = mListeners.toArray(new BatterySaverPolicyListener[0]); } - // Notify the listeners. - mHandler.post(() -> { - for (BatterySaverPolicyListener listener : listeners) { - listener.onBatterySaverPolicyChanged(this); - } - }); + maybeNotifyListenersOfPolicyChange(); } @GuardedBy("mLock") @@ -404,31 +450,63 @@ public class BatterySaverPolicy extends ContentObserver { @GuardedBy("mLock") private void updatePolicyDependenciesLocked() { - final Policy currPolicy = getCurrentPolicyLocked(); - // Update the effective vibration policy. - mDisableVibrationEffective = currPolicy.disableVibration - && !mAccessibilityEnabled; // Don't disable vibration when accessibility is on. + final Policy rawPolicy = getCurrentRawPolicyLocked(); + + final int locationMode; + if (mCarModeEnabled + && rawPolicy.locationMode != PowerManager.LOCATION_MODE_NO_CHANGE + && rawPolicy.locationMode != PowerManager.LOCATION_MODE_FOREGROUND_ONLY) { + // If car projection is enabled, ensure that navigation works. + locationMode = PowerManager.LOCATION_MODE_FOREGROUND_ONLY; + } else { + locationMode = rawPolicy.locationMode; + } + mEffectivePolicy = new Policy( + rawPolicy.adjustBrightnessFactor, + rawPolicy.advertiseIsEnabled, + rawPolicy.deferFullBackup, + rawPolicy.deferKeyValueBackup, + rawPolicy.disableAnimation, + rawPolicy.disableAod, + rawPolicy.disableLaunchBoost, + rawPolicy.disableOptionalSensors, + rawPolicy.disableSoundTrigger, + // Don't disable vibration when accessibility is on. + rawPolicy.disableVibration && !mAccessibilityEnabled, + rawPolicy.enableAdjustBrightness, + rawPolicy.enableDataSaver, + rawPolicy.enableFirewall, + // Don't force night mode when car projection is enabled. + rawPolicy.enableNightMode && !mCarModeEnabled, + rawPolicy.enableQuickDoze, + rawPolicy.filesForInteractive, + rawPolicy.filesForNoninteractive, + rawPolicy.forceAllAppsStandby, + rawPolicy.forceBackgroundCheck, + locationMode + ); + final StringBuilder sb = new StringBuilder(); - if (currPolicy.forceAllAppsStandby) sb.append("A"); - if (currPolicy.forceBackgroundCheck) sb.append("B"); + if (mEffectivePolicy.forceAllAppsStandby) sb.append("A"); + if (mEffectivePolicy.forceBackgroundCheck) sb.append("B"); - if (mDisableVibrationEffective) sb.append("v"); - if (currPolicy.disableAnimation) sb.append("a"); - if (currPolicy.disableSoundTrigger) sb.append("s"); - if (currPolicy.deferFullBackup) sb.append("F"); - if (currPolicy.deferKeyValueBackup) sb.append("K"); - if (currPolicy.enableFirewall) sb.append("f"); - if (currPolicy.enableDataSaver) sb.append("d"); - if (currPolicy.enableAdjustBrightness) sb.append("b"); + if (mEffectivePolicy.disableVibration) sb.append("v"); + if (mEffectivePolicy.disableAnimation) sb.append("a"); + if (mEffectivePolicy.disableSoundTrigger) sb.append("s"); + if (mEffectivePolicy.deferFullBackup) sb.append("F"); + if (mEffectivePolicy.deferKeyValueBackup) sb.append("K"); + if (mEffectivePolicy.enableFirewall) sb.append("f"); + if (mEffectivePolicy.enableDataSaver) sb.append("d"); + if (mEffectivePolicy.enableAdjustBrightness) sb.append("b"); - if (currPolicy.disableLaunchBoost) sb.append("l"); - if (currPolicy.disableOptionalSensors) sb.append("S"); - if (currPolicy.disableAod) sb.append("o"); - if (currPolicy.enableQuickDoze) sb.append("q"); + if (mEffectivePolicy.disableLaunchBoost) sb.append("l"); + if (mEffectivePolicy.disableOptionalSensors) sb.append("S"); + if (mEffectivePolicy.disableAod) sb.append("o"); + if (mEffectivePolicy.enableQuickDoze) sb.append("q"); - sb.append(currPolicy.locationMode); + sb.append(mEffectivePolicy.locationMode); mEventLogKeys = sb.toString(); } @@ -857,7 +935,7 @@ public class BatterySaverPolicy extends ContentObserver { return builder.setBatterySaverEnabled(currPolicy.disableSoundTrigger) .build(); case ServiceType.VIBRATION: - return builder.setBatterySaverEnabled(mDisableVibrationEffective) + return builder.setBatterySaverEnabled(currPolicy.disableVibration) .build(); case ServiceType.FORCE_ALL_APPS_STANDBY: return builder.setBatterySaverEnabled(currPolicy.forceAllAppsStandby) @@ -933,6 +1011,10 @@ public class BatterySaverPolicy extends ContentObserver { } private Policy getCurrentPolicyLocked() { + return mEffectivePolicy; + } + + private Policy getCurrentRawPolicyLocked() { switch (getPolicyLevelLocked()) { case POLICY_LEVEL_FULL: return mFullPolicy; @@ -994,11 +1076,13 @@ public class BatterySaverPolicy extends ContentObserver { pw.println(" value: " + mAdaptiveDeviceSpecificSettings); pw.println(" mAccessibilityEnabled=" + mAccessibilityEnabled); + pw.println(" mCarModeEnabled=" + mCarModeEnabled); pw.println(" mPolicyLevel=" + getPolicyLevelLocked()); dumpPolicyLocked(pw, " ", "full", mFullPolicy); dumpPolicyLocked(pw, " ", "default adaptive", mDefaultAdaptivePolicy); dumpPolicyLocked(pw, " ", "current adaptive", mAdaptivePolicy); + dumpPolicyLocked(pw, " ", "effective", mEffectivePolicy); } } @@ -1009,11 +1093,7 @@ public class BatterySaverPolicy extends ContentObserver { pw.print(indent); pw.println(" " + KEY_ADVERTISE_IS_ENABLED + "=" + p.advertiseIsEnabled); pw.print(indent); - pw.println(" " + KEY_VIBRATION_DISABLED + ":config=" + p.disableVibration); - // mDisableVibrationEffective is based on the currently selected policy - pw.print(indent); - pw.println(" " + KEY_VIBRATION_DISABLED + ":effective=" + (p.disableVibration - && !mAccessibilityEnabled)); + pw.println(" " + KEY_VIBRATION_DISABLED + "=" + p.disableVibration); pw.print(indent); pw.println(" " + KEY_ANIMATION_DISABLED + "=" + p.disableAnimation); pw.print(indent); @@ -1070,10 +1150,24 @@ public class BatterySaverPolicy extends ContentObserver { } @VisibleForTesting - public void setAccessibilityEnabledForTest(boolean enabled) { + void setAccessibilityEnabled(boolean enabled) { synchronized (mLock) { - mAccessibilityEnabled = enabled; - updatePolicyDependenciesLocked(); + if (mAccessibilityEnabled != enabled) { + mAccessibilityEnabled = enabled; + updatePolicyDependenciesLocked(); + maybeNotifyListenersOfPolicyChange(); + } + } + } + + @VisibleForTesting + void setCarModeEnabled(boolean enabled) { + synchronized (mLock) { + if (mCarModeEnabled != enabled) { + mCarModeEnabled = enabled; + updatePolicyDependenciesLocked(); + maybeNotifyListenersOfPolicyChange(); + } } } diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index 8d090f1b6fce..801d75b90a54 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -218,11 +218,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve packageInstaller.getSessionInfo(sessionId); if (sessionInfo.isStagedSessionReady() && markStagedSessionHandled(rollbackId)) { mContext.unregisterReceiver(listener); - if (logPackage != null) { - // We save the rollback id so that after reboot, we can log if rollback was - // successful or not. If logPackage is null, then there is nothing to log. - saveStagedRollbackId(rollbackId); - } + saveStagedRollbackId(rollbackId); WatchdogRollbackLogger.logEvent(logPackage, FrameworkStatsLog .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED, diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java index 1be6f227b44b..659de0093ead 100644 --- a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java +++ b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java @@ -159,6 +159,12 @@ public final class WatchdogRollbackLogger { return; } + // If no logging packages are found, use a null package to ensure the rollback status + // is still logged. + if (oldLoggingPackages.isEmpty()) { + oldLoggingPackages.add(null); + } + for (VersionedPackage oldLoggingPackage : oldLoggingPackages) { if (sessionInfo.isStagedSessionApplied()) { logEvent(oldLoggingPackage, diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index aed2d9bb9dc7..3f8f6bfed9ca 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -188,6 +188,7 @@ public class StatsPullAtomService extends SystemService { private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000; private static final int CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES = 8; private static final int OP_FLAGS_PULLED = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED; + private static final String COMMON_PERMISSION_PREFIX = "android.permission."; private final Object mNetworkStatsLock = new Object(); @GuardedBy("mNetworkStatsLock") @@ -395,6 +396,8 @@ public class StatsPullAtomService extends SystemService { case FrameworkStatsLog.BATTERY_VOLTAGE: case FrameworkStatsLog.BATTERY_CYCLE_COUNT: return pullHealthHal(atomTag, data); + case FrameworkStatsLog.APP_FEATURES_OPS: + return pullAppFeaturesOps(atomTag, data); default: throw new UnsupportedOperationException("Unknown tagId=" + atomTag); } @@ -550,6 +553,7 @@ public class StatsPullAtomService extends SystemService { registerAppsOnExternalStorageInfo(); registerFaceSettings(); registerAppOps(); + registerAppFeaturesOps(); registerRuntimeAppOpAccessMessage(); registerNotificationRemoteViews(); registerDangerousPermissionState(); @@ -2624,6 +2628,10 @@ public class StatsPullAtomService extends SystemService { continue; } + if (permName.startsWith(COMMON_PERMISSION_PREFIX)) { + permName = permName.substring(COMMON_PERMISSION_PREFIX.length()); + } + StatsEvent.Builder e = StatsEvent.newBuilder(); e.setAtomId(atomTag); e.writeString(permName); @@ -2843,7 +2851,6 @@ public class StatsPullAtomService extends SystemService { BackgroundThread.getExecutor(), mStatsCallbackImpl ); - } private void registerRuntimeAppOpAccessMessage() { @@ -2854,7 +2861,6 @@ public class StatsPullAtomService extends SystemService { BackgroundThread.getExecutor(), mStatsCallbackImpl ); - } int pullAppOps(int atomTag, List<StatsEvent> pulledData) { @@ -2917,6 +2923,84 @@ public class StatsPullAtomService extends SystemService { return StatsManager.PULL_SUCCESS; } + private void registerAppFeaturesOps() { + int tagId = FrameworkStatsLog.APP_FEATURES_OPS; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + BackgroundThread.getExecutor(), + mStatsCallbackImpl + ); + } + + int pullAppFeaturesOps(int atomTag, List<StatsEvent> pulledData) { + final long token = Binder.clearCallingIdentity(); + try { + AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); + + CompletableFuture<HistoricalOps> ops = new CompletableFuture<>(); + HistoricalOpsRequest histOpsRequest = + new HistoricalOpsRequest.Builder(0, Long.MAX_VALUE).setFlags( + OP_FLAGS_PULLED).build(); + appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete); + + HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, + TimeUnit.MILLISECONDS); + + for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) { + final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx); + final int uid = uidOps.getUid(); + for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) { + final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx); + for (int featureIdx = 0; featureIdx < packageOps.getFeatureCount(); + featureIdx++) { + final AppOpsManager.HistoricalFeatureOps featureOps = + packageOps.getFeatureOpsAt(featureIdx); + for (int opIdx = 0; opIdx < featureOps.getOpCount(); opIdx++) { + final AppOpsManager.HistoricalOp op = featureOps.getOpAt(opIdx); + StatsEvent.Builder e = StatsEvent.newBuilder(); + e.setAtomId(atomTag); + e.writeInt(uid); + e.writeString(packageOps.getPackageName()); + e.writeString(featureOps.getFeatureId()); + e.writeString(op.getOpName()); + e.writeLong(op.getForegroundAccessCount(OP_FLAGS_PULLED)); + e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_PULLED)); + e.writeLong(op.getForegroundRejectCount(OP_FLAGS_PULLED)); + e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_PULLED)); + e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_PULLED)); + e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_PULLED)); + + String perm = AppOpsManager.opToPermission(op.getOpCode()); + if (perm == null) { + e.writeBoolean(false); + } else { + PermissionInfo permInfo; + try { + permInfo = mContext.getPackageManager().getPermissionInfo(perm, + 0); + e.writeBoolean( + permInfo.getProtection() == PROTECTION_DANGEROUS); + } catch (PackageManager.NameNotFoundException exception) { + e.writeBoolean(false); + } + } + pulledData.add(e.build()); + } + + } + } + } + } catch (Throwable t) { + // TODO: catch exceptions at a more granular level + Slog.e(TAG, "Could not read appops", t); + return StatsManager.PULL_SKIP; + } finally { + Binder.restoreCallingIdentity(token); + } + return StatsManager.PULL_SUCCESS; + } + int pullRuntimeAppOpAccessMessage(int atomTag, List<StatsEvent> pulledData) { final long token = Binder.clearCallingIdentity(); try { diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java index bad2b78dab48..4eff954f1ec8 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java @@ -13,9 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.android.server.tv.tunerresourcemanager; +import java.util.ArrayList; +import java.util.List; + /** * A client profile object used by the Tuner Resource Manager to record the registered clients' * information. @@ -23,12 +25,14 @@ package com.android.server.tv.tunerresourcemanager; * @hide */ public final class ClientProfile { + public static final int INVALID_GROUP_ID = -1; + /** * Client id sent to the client when registering with * {@link #registerClientProfile(ResourceClientProfile, TunerResourceManagerCallback, int[])} */ - private final int mClientId; + private final int mId; /** * see {@link ResourceClientProfile} @@ -41,7 +45,7 @@ public final class ClientProfile { private final int mUseCase; /** - * Process id queried from {@link TvInputManager#} + * Process id queried from {@link TvInputManager#getPid(String)}. */ private final int mProcessId; @@ -59,6 +63,11 @@ public final class ClientProfile { private int mNiceValue; /** + * List of the frontend ids that are used by the current client. + */ + private List<Integer> mUsingFrontendIds = new ArrayList<>(); + + /** * Optional arbitrary priority value given by the client. * * <p>This value can override the default priorotiy calculated from @@ -66,18 +75,15 @@ public final class ClientProfile { */ private int mPriority; - private ClientProfile(ClientProfileBuilder builder) { - this.mClientId = builder.mClientId; + private ClientProfile(Builder builder) { + this.mId = builder.mId; this.mTvInputSessionId = builder.mTvInputSessionId; this.mUseCase = builder.mUseCase; this.mProcessId = builder.mProcessId; - this.mGroupId = builder.mGroupId; - this.mNiceValue = builder.mNiceValue; - this.mPriority = builder.mPriority; } - public int getClientId() { - return mClientId; + public int getId() { + return mId; } public String getTvInputSessionId() { @@ -116,26 +122,47 @@ public final class ClientProfile { mNiceValue = niceValue; } + /** + * Set when the client starts to use a frontend. + * + * @param frontendId being used. + */ + public void useFrontend(int frontendId) { + mUsingFrontendIds.add(frontendId); + } + + public List<Integer> getInUseFrontendIds() { + return mUsingFrontendIds; + } + + /** + * Called when the client released a frontend. + * + * <p>This could happen when client resource reclaimed. + * + * @param frontendId being released. + */ + public void releaseFrontend(int frontendId) { + mUsingFrontendIds.remove(frontendId); + } + @Override public String toString() { - return "ClientProfile: " + this.mClientId + ", " + this.mTvInputSessionId + ", " - + this.mUseCase + ", " + this.mProcessId; + return "ClientProfile[id=" + this.mId + ", tvInputSessionId=" + this.mTvInputSessionId + + ", useCase=" + this.mUseCase + ", processId=" + this.mProcessId + "]"; } /** * Builder class for {@link ClientProfile}. */ - public static class ClientProfileBuilder { - private final int mClientId; + public static class Builder { + private final int mId; private String mTvInputSessionId; private int mUseCase; private int mProcessId; - private int mGroupId; - private int mNiceValue; - private int mPriority; - ClientProfileBuilder(int clientId) { - this.mClientId = clientId; + Builder(int id) { + this.mId = id; } /** @@ -143,7 +170,7 @@ public final class ClientProfile { * * @param useCase the useCase of the client. */ - public ClientProfileBuilder useCase(int useCase) { + public Builder useCase(int useCase) { this.mUseCase = useCase; return this; } @@ -153,7 +180,7 @@ public final class ClientProfile { * * @param tvInputSessionId the id of the tv input session. */ - public ClientProfileBuilder tvInputSessionId(String tvInputSessionId) { + public Builder tvInputSessionId(String tvInputSessionId) { this.mTvInputSessionId = tvInputSessionId; return this; } @@ -163,42 +190,11 @@ public final class ClientProfile { * * @param processId the id of process. */ - public ClientProfileBuilder processId(int processId) { + public Builder processId(int processId) { this.mProcessId = processId; return this; } - - /** - * Builder for {@link ClientProfile}. - * - * @param groupId the id of the group that shares the same resource. - */ - public ClientProfileBuilder groupId(int groupId) { - this.mGroupId = groupId; - return this; - } - - /** - * Builder for {@link ClientProfile}. - * - * @param niceValue the nice value of the client. - */ - public ClientProfileBuilder niceValue(int niceValue) { - this.mNiceValue = niceValue; - return this; - } - - /** - * Builder for {@link ClientProfile}. - * - * @param priority the priority value of the client. - */ - public ClientProfileBuilder priority(int priority) { - this.mPriority = priority; - return this; - } - /** * Build a {@link ClientProfile}. * diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java new file mode 100644 index 000000000000..a109265e2f50 --- /dev/null +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java @@ -0,0 +1,204 @@ +/* + * Copyright 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.server.tv.tunerresourcemanager; + +import android.annotation.Nullable; +import android.media.tv.tuner.frontend.FrontendSettings.Type; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * A frontend resource object used by the Tuner Resource Manager to record the tuner frontend + * information. + * + * @hide + */ +public final class FrontendResource { + public static final int INVALID_OWNER_ID = -1; + + /** + * Id of the current frontend. Should not be changed and should be aligned with the driver level + * implementation. + */ + private final int mId; + + /** + * see {@link android.media.tv.tuner.frontend.FrontendSettings.Type} + */ + @Type private final int mType; + + /** + * The exclusive group id of the FE. FEs under the same id can't be used at the same time. + */ + private final int mExclusiveGroupId; + + /** + * An array to save all the FE ids under the same exclisive group. + */ + private List<Integer> mExclusiveGroupMemberFeIds = new ArrayList<>(); + + /** + * If the current resource is in use. Once resources under the same exclusive group id is in use + * all other resources in the same group would be considered in use. + */ + private boolean mIsInUse; + + /** + * The owner client's id if this resource is occupied. Owner of the resource under the same + * exclusive group id would be considered as the whole group's owner. + */ + private int mOwnerClientId = INVALID_OWNER_ID; + + private FrontendResource(Builder builder) { + this.mId = builder.mId; + this.mType = builder.mType; + this.mExclusiveGroupId = builder.mExclusiveGroupId; + } + + public int getId() { + return mId; + } + + public int getType() { + return mType; + } + + public int getExclusiveGroupId() { + return mExclusiveGroupId; + } + + public List<Integer> getExclusiveGroupMemberFeIds() { + return mExclusiveGroupMemberFeIds; + } + + /** + * Add one id into the exclusive group member id list. + * + * @param id the id to be added. + */ + public void addExclusiveGroupMemberFeId(int id) { + mExclusiveGroupMemberFeIds.add(id); + } + + /** + * Add one id list to the exclusive group member id list. + * + * @param ids the id list to be added. + */ + public void addExclusiveGroupMemberFeId(List<Integer> ids) { + mExclusiveGroupMemberFeIds.addAll(ids); + } + + /** + * Remove one id from the exclusive group member id list. + * + * @param id the id to be removed. + */ + public void removeExclusiveGroupMemberFeId(int id) { + mExclusiveGroupMemberFeIds.remove(new Integer(id)); + } + + public boolean isInUse() { + return mIsInUse; + } + + public int getOwnerClientId() { + return mOwnerClientId; + } + + /** + * Set an owner client on the resource. + * + * @param ownerClientId the id of the owner client. + */ + public void setOwner(int ownerClientId) { + mIsInUse = true; + mOwnerClientId = ownerClientId; + } + + /** + * Remove an owner client from the resource. + */ + public void removeOwner() { + mIsInUse = false; + mOwnerClientId = INVALID_OWNER_ID; + } + + @Override + public String toString() { + return "FrontendResource[id=" + this.mId + ", type=" + this.mType + + ", exclusiveGId=" + this.mExclusiveGroupId + ", exclusiveGMemeberIds=" + + Arrays.toString(this.mExclusiveGroupMemberFeIds.toArray()) + + ", isInUse=" + this.mIsInUse + ", ownerClientId=" + this.mOwnerClientId + "]"; + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof FrontendResource) { + FrontendResource fe = (FrontendResource) o; + return mId == fe.getId() && mType == fe.getType() + && mExclusiveGroupId == fe.getExclusiveGroupId() + && mExclusiveGroupMemberFeIds.equals(fe.getExclusiveGroupMemberFeIds()) + && mIsInUse == fe.isInUse() && mOwnerClientId == fe.getOwnerClientId(); + } + return false; + } + + /** + * Builder class for {@link FrontendResource}. + */ + public static class Builder { + private final int mId; + @Type private int mType; + private int mExclusiveGroupId; + + Builder(int id) { + this.mId = id; + } + + /** + * Builder for {@link FrontendResource}. + * + * @param type the type of the frontend. See {@link Type} + */ + public Builder type(@Type int type) { + this.mType = type; + return this; + } + + /** + * Builder for {@link FrontendResource}. + * + * @param exclusiveGroupId the id of exclusive group. + */ + public Builder exclusiveGroupId(int exclusiveGroupId) { + this.mExclusiveGroupId = exclusiveGroupId; + return this; + } + + /** + * Build a {@link FrontendResource}. + * + * @return {@link FrontendResource}. + */ + public FrontendResource build() { + FrontendResource frontendResource = new FrontendResource(this); + return frontendResource; + } + } +} 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 49a7045bf57a..cb31a502ecfa 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -28,32 +28,48 @@ import android.media.tv.tunerresourcemanager.TunerFrontendInfo; import android.media.tv.tunerresourcemanager.TunerFrontendRequest; import android.media.tv.tunerresourcemanager.TunerLnbRequest; import android.media.tv.tunerresourcemanager.TunerResourceManager; +import android.os.Binder; import android.os.RemoteException; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.SystemService; import java.util.ArrayList; import java.util.List; /** - * This class provides a system service that manages the TV tuner resources. - * - * @hide - */ + * This class provides a system service that manages the TV tuner resources. + * + * @hide + */ public class TunerResourceManagerService extends SystemService { private static final String TAG = "TunerResourceManagerService"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private SparseArray<ClientProfile> mClientProfiles = new SparseArray<>(); - private SparseArray<IResourcesReclaimListener> mListeners = new SparseArray<>(); - private int mNextUnusedFrontendId = 0; - private List<Integer> mReleasedClientId = new ArrayList<Integer>(); + public static final int INVALID_CLIENT_ID = -1; + private static final int MAX_CLIENT_PRIORITY = 1000; + + // Array of the registered client profiles + @VisibleForTesting private SparseArray<ClientProfile> mClientProfiles = new SparseArray<>(); + private int mNextUnusedClientId = 0; + private List<Integer> mRegisteredClientIds = new ArrayList<Integer>(); + + // Array of the current available frontend resources + @VisibleForTesting + private SparseArray<FrontendResource> mFrontendResources = new SparseArray<>(); + // Array of the current available frontend ids private List<Integer> mAvailableFrontendIds = new ArrayList<Integer>(); + private SparseArray<IResourcesReclaimListener> mListeners = new SparseArray<>(); + private TvInputManager mManager; + private UseCasePriorityHints mPriorityCongfig = new UseCasePriorityHints(); + + // Used to synchronize the access to the service. + private final Object mLock = new Object(); public TunerResourceManagerService(@Nullable Context context) { super(context); @@ -61,97 +77,78 @@ public class TunerResourceManagerService extends SystemService { @Override public void onStart() { - publishBinderService(Context.TV_TUNER_RESOURCE_MGR_SERVICE, new BinderService()); - mManager = (TvInputManager) getContext() - .getSystemService(Context.TV_INPUT_SERVICE); + onStart(false /*isForTesting*/); + } + + @VisibleForTesting + protected void onStart(boolean isForTesting) { + if (!isForTesting) { + publishBinderService(Context.TV_TUNER_RESOURCE_MGR_SERVICE, new BinderService()); + } + mManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE); + mPriorityCongfig.parse(); } private final class BinderService extends ITunerResourceManager.Stub { @Override public void registerClientProfile(@NonNull ResourceClientProfile profile, - @NonNull IResourcesReclaimListener listener, - @NonNull int[] clientId) { - if (DEBUG) { - Slog.d(TAG, "registerClientProfile(clientProfile=" + profile + ")"); + @NonNull IResourcesReclaimListener listener, @NonNull int[] clientId) + throws RemoteException { + enforceAccessPermission(); + if (profile == null) { + throw new RemoteException("ResourceClientProfile can't be null"); } - // TODO tell if the client already exists - if (mReleasedClientId.isEmpty()) { - clientId[0] = mNextUnusedFrontendId++; - } else { - clientId[0] = mReleasedClientId.get(0); - mReleasedClientId.remove(0); + if (clientId == null) { + throw new RemoteException("clientId can't be null!"); } - if (mManager == null) { - Slog.e(TAG, "TvInputManager is null. Can't register client profile."); - return; + if (!mPriorityCongfig.isDefinedUseCase(profile.getUseCase())) { + throw new RemoteException("Use undefined client use case:" + profile.getUseCase()); } - int callingPid = mManager.getClientPid(profile.getTvInputSessionId()); - - ClientProfile clientProfile = new ClientProfile.ClientProfileBuilder( - clientId[0]) - .tvInputSessionId(profile.getTvInputSessionId()) - .useCase(profile.getUseCase()) - .processId(callingPid) - .build(); - mClientProfiles.append(clientId[0], clientProfile); - mListeners.append(clientId[0], listener); + synchronized (mLock) { + registerClientProfileInternal(profile, listener, clientId); + } } @Override - public void unregisterClientProfile(int clientId) { - if (DEBUG) { - Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")"); + public void unregisterClientProfile(int clientId) throws RemoteException { + enforceAccessPermission(); + synchronized (mLock) { + if (!checkClientExists(clientId)) { + Slog.e(TAG, "Unregistering non exists client:" + clientId); + return; + } + unregisterClientProfileInternal(clientId); } - - mClientProfiles.remove(clientId); - mListeners.remove(clientId); - mReleasedClientId.add(clientId); } @Override public boolean updateClientPriority(int clientId, int priority, int niceValue) { - if (DEBUG) { - Slog.d(TAG, "updateClientPriority(clientId=" + clientId - + ", priority=" + priority + ", niceValue=" + niceValue + ")"); - } - - ClientProfile profile = mClientProfiles.get(clientId); - if (profile == null) { - Slog.e(TAG, "Can not find client profile with id " + clientId - + " when trying to update the client priority."); - return false; + enforceAccessPermission(); + synchronized (mLock) { + return updateClientPriorityInternal(clientId, priority, niceValue); } - - profile.setPriority(priority); - profile.setNiceValue(niceValue); - - return true; } @Override - public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos) - throws RemoteException { - if (infos == null || infos.length == 0) { - Slog.d(TAG, "Can't update with empty frontend info"); - return; + public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos) throws RemoteException { + enforceAccessPermission(); + if (infos == null) { + throw new RemoteException("TunerFrontendInfo can't be null"); } - - if (DEBUG) { - Slog.d(TAG, "updateFrontendInfo:"); - for (int i = 0; i < infos.length; i++) { - Slog.d(TAG, infos[i].toString()); - } + synchronized (mLock) { + setFrontendInfoListInternal(infos); } } @Override public void updateCasInfo(int casSystemId, int maxSessionNum) { if (DEBUG) { - Slog.d(TAG, "updateCasInfo(casSystemId=" - + casSystemId + ", maxSessionNum=" + maxSessionNum + ")"); + Slog.d(TAG, + "updateCasInfo(casSystemId=" + casSystemId + + ", maxSessionNum=" + maxSessionNum + ")"); } } @@ -166,49 +163,30 @@ public class TunerResourceManagerService extends SystemService { @Override public boolean requestFrontend(@NonNull TunerFrontendRequest request, - @NonNull int[] frontendId) throws RemoteException { - if (DEBUG) { - Slog.d(TAG, "requestFrontend(request=" + request + ")"); - } - - frontendId[0] = TunerResourceManager.INVALID_FRONTEND_ID; - - if (getContext() == null) { - Slog.e(TAG, "Can not find context when requesting frontend"); - return false; + @NonNull int[] frontendId) throws RemoteException { + enforceAccessPermission(); + if (frontendId == null) { + throw new RemoteException("frontendId can't be null"); } - - if (mClientProfiles.get(request.getClientId()) == null) { - Slog.e(TAG, "Request from unregistered client. Id: " - + request.getClientId()); - return false; - } - - String sessionId = mClientProfiles.get(request.getClientId()) - .getTvInputSessionId(); - - if (DEBUG) { - Slog.d(TAG, "session Id:" + sessionId + ")"); - } - - if (DEBUG) { - Slog.d(TAG, "No available Frontend found."); + synchronized (mLock) { + try { + return requestFrontendInternal(request, frontendId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } - - return false; } @Override public void shareFrontend(int selfClientId, int targetClientId) { if (DEBUG) { - Slog.d(TAG, "shareFrontend from " - + selfClientId + " with " + targetClientId); + Slog.d(TAG, "shareFrontend from " + selfClientId + " with " + targetClientId); } } @Override - public boolean requestCasSession(@NonNull CasSessionRequest request, - @NonNull int[] sessionResourceId) { + public boolean requestCasSession( + @NonNull CasSessionRequest request, @NonNull int[] sessionResourceId) { if (DEBUG) { Slog.d(TAG, "requestCasSession(request=" + request + ")"); } @@ -246,13 +224,284 @@ public class TunerResourceManagerService extends SystemService { } @Override - public boolean isHigherPriority(ResourceClientProfile challengerProfile, - ResourceClientProfile holderProfile) { + public boolean isHigherPriority( + ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile) { if (DEBUG) { - Slog.d(TAG, "isHigherPriority(challengerProfile=" + challengerProfile - + ", holderProfile=" + challengerProfile + ")"); + Slog.d(TAG, + "isHigherPriority(challengerProfile=" + challengerProfile + + ", holderProfile=" + challengerProfile + ")"); } return true; } } + + @VisibleForTesting + protected void registerClientProfileInternal(ResourceClientProfile profile, + IResourcesReclaimListener listener, int[] clientId) { + if (DEBUG) { + Slog.d(TAG, "registerClientProfile(clientProfile=" + profile + ")"); + } + + clientId[0] = INVALID_CLIENT_ID; + if (mManager == null) { + Slog.e(TAG, "TvInputManager is null. Can't register client profile."); + return; + } + // TODO tell if the client already exists + clientId[0] = mNextUnusedClientId++; + + int pid = profile.getTvInputSessionId() == null + ? Binder.getCallingPid() /*callingPid*/ + : mManager.getClientPid(profile.getTvInputSessionId()); /*tvAppId*/ + + ClientProfile clientProfile = new ClientProfile.Builder(clientId[0]) + .tvInputSessionId(profile.getTvInputSessionId()) + .useCase(profile.getUseCase()) + .processId(pid) + .build(); + clientProfile.setPriority(getClientPriority(profile.getUseCase(), pid)); + + mClientProfiles.append(clientId[0], clientProfile); + mListeners.append(clientId[0], listener); + mRegisteredClientIds.add(clientId[0]); + } + + @VisibleForTesting + protected void unregisterClientProfileInternal(int clientId) { + if (DEBUG) { + Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")"); + } + for (int id : getClientProfile(clientId).getInUseFrontendIds()) { + getFrontendResource(id).removeOwner(); + for (int groupMemberId : getFrontendResource(id).getExclusiveGroupMemberFeIds()) { + getFrontendResource(groupMemberId).removeOwner(); + } + } + mClientProfiles.remove(clientId); + mListeners.remove(clientId); + mRegisteredClientIds.remove(clientId); + } + + @VisibleForTesting + protected boolean updateClientPriorityInternal(int clientId, int priority, int niceValue) { + if (DEBUG) { + Slog.d(TAG, + "updateClientPriority(clientId=" + clientId + ", priority=" + priority + + ", niceValue=" + niceValue + ")"); + } + + ClientProfile profile = getClientProfile(clientId); + if (profile == null) { + Slog.e(TAG, + "Can not find client profile with id " + clientId + + " when trying to update the client priority."); + return false; + } + + profile.setPriority(priority); + profile.setNiceValue(niceValue); + + return true; + } + + @VisibleForTesting + protected void setFrontendInfoListInternal(TunerFrontendInfo[] infos) { + if (DEBUG) { + Slog.d(TAG, "updateFrontendInfo:"); + for (int i = 0; i < infos.length; i++) { + Slog.d(TAG, infos[i].toString()); + } + } + + // An arrayList to record the frontends pending on updating. Ids will be removed + // from this list once its updating finished. Any frontend left in this list when all + // the updates are done will be removed from mAvailableFrontendIds and + // mFrontendResources. + List<Integer> updatingFrontendIds = new ArrayList<>(mAvailableFrontendIds); + + // Update frontendResources sparse array and other mappings accordingly + for (int i = 0; i < infos.length; i++) { + if (getFrontendResource(infos[i].getId()) != null) { + if (DEBUG) { + Slog.d(TAG, "Frontend id=" + infos[i].getId() + "exists."); + } + updatingFrontendIds.remove(new Integer(infos[i].getId())); + } else { + // Add a new fe resource + FrontendResource newFe = new FrontendResource.Builder(infos[i].getId()) + .type(infos[i].getFrontendType()) + .exclusiveGroupId(infos[i].getExclusiveGroupId()) + .build(); + // Update the exclusive group member list in all the existing Frontend resource + for (Integer feId : mAvailableFrontendIds) { + FrontendResource fe = getFrontendResource(feId.intValue()); + if (fe.getExclusiveGroupId() == newFe.getExclusiveGroupId()) { + newFe.addExclusiveGroupMemberFeId(fe.getId()); + newFe.addExclusiveGroupMemberFeId(fe.getExclusiveGroupMemberFeIds()); + for (Integer excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) { + getFrontendResource(excGroupmemberFeId.intValue()) + .addExclusiveGroupMemberFeId(newFe.getId()); + } + fe.addExclusiveGroupMemberFeId(newFe.getId()); + break; + } + } + // Update resource list and available id list + mFrontendResources.append(newFe.getId(), newFe); + mAvailableFrontendIds.add(newFe.getId()); + } + } + + // TODO check if the removing resource is in use or not. Handle the conflict. + for (Integer removingId : updatingFrontendIds) { + // update the exclusive group id memver list + FrontendResource fe = getFrontendResource(removingId.intValue()); + fe.removeExclusiveGroupMemberFeId(new Integer(fe.getId())); + for (Integer excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) { + getFrontendResource(excGroupmemberFeId.intValue()) + .removeExclusiveGroupMemberFeId(new Integer(fe.getId())); + } + mFrontendResources.remove(removingId.intValue()); + mAvailableFrontendIds.remove(removingId); + } + } + + @VisibleForTesting + protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendId) + throws RemoteException { + if (DEBUG) { + Slog.d(TAG, "requestFrontend(request=" + request + ")"); + } + + frontendId[0] = TunerResourceManager.INVALID_FRONTEND_ID; + if (!checkClientExists(request.getClientId())) { + Slog.e(TAG, "Request frontend from unregistered client:" + request.getClientId()); + return false; + } + ClientProfile requestClient = getClientProfile(request.getClientId()); + int grantingFrontendId = -1; + int inUseLowestPriorityFrId = -1; + // Priority max value is 1000 + int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; + for (int id : mAvailableFrontendIds) { + FrontendResource fr = getFrontendResource(id); + if (fr.getType() == request.getFrontendType()) { + if (!fr.isInUse()) { + // Grant unused frontend with no exclusive group members first. + if (fr.getExclusiveGroupMemberFeIds().size() == 0) { + grantingFrontendId = id; + break; + } else if (grantingFrontendId < 0) { + // Grant the unused frontend with lower id first if all the unused + // frontends have exclusive group members. + grantingFrontendId = id; + } + } else if (grantingFrontendId < 0) { + // Record the frontend id with the lowest client priority among all the + // in use frontends when no available frontend has been found. + int priority = getOwnerClientPriority(id); + if (currentLowestPriority > priority) { + inUseLowestPriorityFrId = id; + currentLowestPriority = priority; + } + } + } + } + + // Grant frontend when there is unused resource. + if (grantingFrontendId > -1) { + frontendId[0] = grantingFrontendId; + updateFrontendClientMappingOnNewGrant(frontendId[0], request.getClientId()); + return true; + } + + // When all the resources are occupied, grant the lowest priority resource if the + // request client has higher priority. + if (inUseLowestPriorityFrId > -1 && (requestClient.getPriority() > currentLowestPriority)) { + frontendId[0] = inUseLowestPriorityFrId; + reclaimFrontendResource(getFrontendResource(frontendId[0]).getOwnerClientId()); + updateFrontendClientMappingOnNewGrant(frontendId[0], request.getClientId()); + return true; + } + + return false; + } + + @VisibleForTesting + protected int getClientPriority(int useCase, int pid) { + if (DEBUG) { + Slog.d(TAG, "getClientPriority useCase=" + useCase + + ", pid=" + pid + ")"); + } + + if (isForeground(pid)) { + return mPriorityCongfig.getForegroundPriority(useCase); + } + return mPriorityCongfig.getBackgroundPriority(useCase); + } + + @VisibleForTesting + protected boolean isForeground(int pid) { + // TODO: how to get fg/bg information from pid + return true; + } + + @VisibleForTesting + protected void reclaimFrontendResource(int reclaimingId) throws RemoteException { + if (mListeners.get(reclaimingId) != null) { + try { + mListeners.get(reclaimingId).onReclaimResources(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + private void updateFrontendClientMappingOnNewGrant(int grantingId, int ownerClientId) { + FrontendResource grantingFrontend = getFrontendResource(grantingId); + ClientProfile ownerProfile = getClientProfile(ownerClientId); + grantingFrontend.setOwner(ownerClientId); + ownerProfile.useFrontend(grantingId); + for (int exclusiveGroupMember : grantingFrontend.getExclusiveGroupMemberFeIds()) { + getFrontendResource(exclusiveGroupMember).setOwner(ownerClientId); + ownerProfile.useFrontend(exclusiveGroupMember); + } + } + + /** + * Get the owner client's priority from the frontend id. + * + * @param frontendId an in use frontend id. + * @return the priority of the owner client of the frontend. + */ + private int getOwnerClientPriority(int frontendId) { + return getClientProfile(getFrontendResource(frontendId).getOwnerClientId()).getPriority(); + } + + private ClientProfile getClientProfile(int clientId) { + return mClientProfiles.get(clientId); + } + + protected FrontendResource getFrontendResource(int frontendId) { + return mFrontendResources.get(frontendId); + } + + @VisibleForTesting + protected SparseArray<ClientProfile> getClientProfiles() { + return mClientProfiles; + } + + @VisibleForTesting + protected SparseArray<FrontendResource> getFrontendResources() { + return mFrontendResources; + } + + private boolean checkClientExists(int clientId) { + return mRegisteredClientIds.contains(clientId); + } + + private void enforceAccessPermission() { + getContext().enforceCallingOrSelfPermission( + "android.permission.TUNER_RESOURCE_ACCESS", TAG); + } } diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java new file mode 100644 index 000000000000..8c2de475b99b --- /dev/null +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java @@ -0,0 +1,236 @@ +/* + * Copyright 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.server.tv.tunerresourcemanager; + +import android.media.tv.TvInputService; +import android.media.tv.TvInputService.PriorityHintUseCaseType; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; +import android.util.Xml; + +import com.android.internal.annotations.VisibleForTesting; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides the Tuner Resource Manager use case priority hints config info including a + * parser that can read the xml config from the vendors. + * + * @hide + */ +public class UseCasePriorityHints { + private static final String TAG = "UseCasePriorityHints"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private static final String PATH_TO_VENDOR_CONFIG_XML = + "/vendor/etc/tunerResourceManagerUseCaseConfig.xml"; + private static final int INVALID_PRIORITY_VALUE = -1; + private static final int INVALID_USE_CASE = -1; + + /** + * Array of the configured use case priority hints. Key is the use case id. Value is a size 2 + * int array. The first element carries the priority of the use case on foreground. The second + * shows the background priority. + */ + SparseArray<int[]> mPriorityHints = new SparseArray<>(); + + List<Integer> mVendorDefinedUseCase = new ArrayList<>(); + + private int mDefaultForeground = 150; + private int mDefaultBackground = 50; + + int getForegroundPriority(int useCase) { + if (mPriorityHints.get(useCase) != null && mPriorityHints.get(useCase).length == 2) { + return mPriorityHints.get(useCase)[0]; + } + return mDefaultForeground; + } + + int getBackgroundPriority(int useCase) { + if (mPriorityHints.get(useCase) != null && mPriorityHints.get(useCase).length == 2) { + return mPriorityHints.get(useCase)[1]; + } + return mDefaultBackground; + } + + boolean isDefinedUseCase(int useCase) { + return (mVendorDefinedUseCase.contains(useCase) || isPredefinedUseCase(useCase)); + } + + /** + * To parse the vendor use case config. + */ + public void parse() { + // Override the default priority with vendor setting if available. + File file = new File(PATH_TO_VENDOR_CONFIG_XML); + if (file.exists()) { + try { + InputStream in = new FileInputStream(file); + parseInternal(in); + return; + } catch (IOException e) { + Slog.e(TAG, "Error reading vendor file: " + file, e); + } catch (XmlPullParserException e) { + Slog.e(TAG, "Unable to parse vendor file: " + file, e); + } + } else { + if (DEBUG) { + Slog.i(TAG, "no vendor priority configuration available. Using default priority"); + } + addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND, 180, 100); + addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN, 450, 200); + addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 480, 300); + addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, 490, 400); + addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, 600, 500); + } + } + + // We don't use namespaces + private static final String NS = null; + + @VisibleForTesting + protected void parseInternal(InputStream in) + throws IOException, XmlPullParserException { + try { + XmlPullParser parser = Xml.newPullParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); + parser.setInput(in, null); + parser.nextTag(); + readUseCase(parser); + in.close(); + } catch (IOException | XmlPullParserException e) { + throw e; + } + for (int i = 0; i < mPriorityHints.size(); i++) { + int useCase = mPriorityHints.keyAt(i); + int[] priorities = mPriorityHints.get(useCase); + if (DEBUG) { + Slog.d(TAG, "{defaultFg=" + mDefaultForeground + + ", defaultBg=" + mDefaultBackground + "}"); + Slog.d(TAG, "{useCase=" + useCase + + ", fg=" + priorities[0] + + ", bg=" + priorities[1] + + "}"); + } + } + } + + private void readUseCase(XmlPullParser parser) + throws XmlPullParserException, IOException { + parser.require(XmlPullParser.START_TAG, NS, "config"); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + int useCase; + if (name.equals("useCaseDefault")) { + mDefaultForeground = readAttributeToInt("fgPriority", parser); + mDefaultBackground = readAttributeToInt("bgPriority", parser); + parser.nextTag(); + parser.require(XmlPullParser.END_TAG, NS, name); + } else if (name.equals("useCasePreDefined")) { + useCase = formatTypeToNum("type", parser); + if (useCase == INVALID_USE_CASE) { + Slog.e(TAG, "Wrong predefined use case name given in the vendor config."); + continue; + } + addNewUseCasePriority(useCase, + readAttributeToInt("fgPriority", parser), + readAttributeToInt("bgPriority", parser)); + parser.nextTag(); + parser.require(XmlPullParser.END_TAG, NS, name); + } else if (name.equals("useCaseVendor")) { + useCase = readAttributeToInt("id", parser); + addNewUseCasePriority(useCase, + readAttributeToInt("fgPriority", parser), + readAttributeToInt("bgPriority", parser)); + mVendorDefinedUseCase.add(useCase); + parser.nextTag(); + parser.require(XmlPullParser.END_TAG, NS, name); + } else { + skip(parser); + } + } + } + + private void skip(XmlPullParser parser) throws XmlPullParserException, IOException { + if (parser.getEventType() != XmlPullParser.START_TAG) { + throw new IllegalStateException(); + } + int depth = 1; + while (depth != 0) { + switch (parser.next()) { + case XmlPullParser.END_TAG: + depth--; + break; + case XmlPullParser.START_TAG: + depth++; + break; + } + } + } + + private int readAttributeToInt(String attributeName, XmlPullParser parser) { + return Integer.valueOf(parser.getAttributeValue(null, attributeName)); + } + + private void addNewUseCasePriority(int useCase, int fgPriority, int bgPriority) { + int[] priorities = {fgPriority, bgPriority}; + mPriorityHints.append(useCase, priorities); + } + + @PriorityHintUseCaseType + private static int formatTypeToNum(String attributeName, XmlPullParser parser) { + String useCaseName = parser.getAttributeValue(null, attributeName); + switch (useCaseName) { + case "USE_CASE_BACKGROUND": + return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND; + case "USE_CASE_SCAN": + return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN; + case "USE_CASE_PLAYBACK": + return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK; + case "USE_CASE_LIVE": + return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE; + case "USE_CASE_RECORD": + return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD; + default: + return INVALID_USE_CASE; + } + } + + private static boolean isPredefinedUseCase(int useCase) { + switch (useCase) { + case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND: + case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN: + case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK: + case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE: + case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD: + return true; + default: + return false; + } + } +} diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index e3b7c0aae507..fe34e86a27ae 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -739,6 +739,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { final UriPermission perm = findOrCreateUriPermission( pi.packageName, targetPkg, targetUid, grantUri); perm.grantModes(modeFlags, owner); + getPmInternal().grantImplicitAccess(UserHandle.getUserId(targetUid), null, + UserHandle.getAppId(targetUid), pi.applicationInfo.uid, false /*direct*/); } /** Like grantUriPermissionUnchecked, but takes an Intent. */ diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java index 8130546e2699..9bbeb728ae33 100644 --- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java +++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java @@ -211,8 +211,9 @@ public class WebViewUpdateService extends SystemService { PackageManagerInternal.class); final int webviewUid = pmInternal.getPackageUidInternal( webViewPackageName, 0, UserHandle.getUserId(callingUid)); - pmInternal.grantImplicitAccess(UserHandle.getUserId(callingUid), null, webviewUid, - UserHandle.getAppId(callingUid)); + pmInternal.grantImplicitAccess(UserHandle.getUserId(callingUid), null, + UserHandle.getAppId(callingUid), webviewUid, + true /*direct*/); } /** diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 68a71881b898..4fea36c040f9 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -275,8 +275,9 @@ class ActivityMetricsLogger { } /** @return {@code true} if the activity matches a launched activity in this transition. */ - boolean contains(ActivityRecord r) { - return r == mLastLaunchedActivity || mPendingDrawActivities.contains(r); + boolean contains(WindowContainer wc) { + final ActivityRecord r = AppTransitionController.getAppFromContainer(wc); + return r != null && (r == mLastLaunchedActivity || mPendingDrawActivities.contains(r)); } /** Called when the activity is drawn or won't be drawn. */ @@ -435,10 +436,10 @@ class ActivityMetricsLogger { /** @return Non-null {@link TransitionInfo} if the activity is found in an active transition. */ @Nullable - private TransitionInfo getActiveTransitionInfo(ActivityRecord r) { + private TransitionInfo getActiveTransitionInfo(WindowContainer wc) { for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) { final TransitionInfo info = mTransitionInfoList.get(i); - if (info.contains(r)) { + if (info.contains(wc)) { return info; } } @@ -623,19 +624,19 @@ class ActivityMetricsLogger { * @param activityToReason A map from activity to a reason integer, which must be on of * ActivityTaskManagerInternal.APP_TRANSITION_* reasons. */ - void notifyTransitionStarting(ArrayMap<ActivityRecord, Integer> activityToReason) { + void notifyTransitionStarting(ArrayMap<WindowContainer, Integer> activityToReason) { if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting"); final long timestampNs = SystemClock.elapsedRealtimeNanos(); for (int index = activityToReason.size() - 1; index >= 0; index--) { - final ActivityRecord r = activityToReason.keyAt(index); - final TransitionInfo info = getActiveTransitionInfo(r); + final WindowContainer wc = activityToReason.keyAt(index); + final TransitionInfo info = getActiveTransitionInfo(wc); if (info == null || info.mLoggedTransitionStarting) { // Ignore any subsequent notifyTransitionStarting. continue; } if (DEBUG_METRICS) { - Slog.i(TAG, "notifyTransitionStarting activity=" + r + " info=" + info); + Slog.i(TAG, "notifyTransitionStarting activity=" + wc + " info=" + info); } info.mCurrentTransitionDelayMs = info.calculateDelay(timestampNs); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 190af7a06c8d..76bc36620c85 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -40,7 +40,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.ROTATION_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.activityTypeToString; @@ -106,7 +105,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS; -import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE; import static android.view.WindowManager.TRANSIT_TASK_CLOSE; import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND; import static android.view.WindowManager.TRANSIT_UNSET; @@ -284,7 +282,6 @@ import android.view.DisplayInfo; import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IApplicationToken; import android.view.InputApplicationHandle; -import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; @@ -577,12 +574,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ private boolean mCurrentLaunchCanTurnScreenOn = true; - /** - * This leash is used to "freeze" the app surface in place after the state change, but before - * the animation is ready to start. - */ - private SurfaceControl mTransitChangeLeash = null; - /** Whether our surface was set to be showing in the last call to {@link #prepareSurfaces} */ private boolean mLastSurfaceShowing = true; @@ -1329,15 +1320,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mDisplayContent.executeAppTransition(); } - if (prevDc.mChangingApps.remove(this)) { - // This gets called *after* the ActivityRecord has been reparented to the new display. - // That reparenting resulted in this window changing modes (eg. FREEFORM -> FULLSCREEN), - // so this token is now "frozen" while waiting for the animation to start on prevDc - // (which will be cancelled since the window is no-longer a child). However, since this - // is no longer a child of prevDc, this won't be notified of the cancelled animation, - // so we need to cancel the change transition here. - clearChangeLeash(getPendingTransaction(), true /* cancel */); - } prevDc.mClosingApps.remove(this); if (prevDc.mFocusedApp == this) { @@ -3092,7 +3074,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A commitVisibility(false /* visible */, true /* performLayout */); getDisplayContent().mOpeningApps.remove(this); - getDisplayContent().mChangingApps.remove(this); + getDisplayContent().mChangingContainers.remove(this); getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this); mWmService.mTaskSnapshotController.onAppRemoved(this); mStackSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this); @@ -3845,13 +3827,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mDisplayContent.setLayoutNeeded(); } mWmService.mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, token).sendToTarget(); - - // Notify the pinned stack upon all windows drawn. If there was an animation in - // progress then this signal will resume that animation. - final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask(); - if (pinnedStack != null) { - pinnedStack.onAllWindowsDrawn(); - } } } } @@ -4002,13 +3977,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A appToken, visible, appTransition, isVisible(), mVisibleRequested, Debug.getCallers(6)); + onChildVisibilityRequested(visible); + final DisplayContent displayContent = getDisplayContent(); displayContent.mOpeningApps.remove(this); displayContent.mClosingApps.remove(this); - if (isInChangeTransition()) { - clearChangeLeash(getPendingTransaction(), true /* cancel */); - } - displayContent.mChangingApps.remove(this); waitingToShow = false; mVisibleRequested = visible; mLastDeferHidingClient = deferHidingClient; @@ -4205,13 +4178,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final DisplayContent displayContent = getDisplayContent(); if (!displayContent.mClosingApps.contains(this) && !displayContent.mOpeningApps.contains(this)) { - // The token is not closing nor opening, so even if there is an animation set, that - // doesn't mean that it goes through the normal app transition cycle so we have - // to inform the docked controller about visibility change. - // TODO(multi-display): notify docked divider on all displays where visibility was - // affected. - displayContent.getDockedDividerController().notifyAppVisibilityChanged(); - // Take the screenshot before possibly hiding the WSA, otherwise the screenshot // will not be taken. mWmService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible); @@ -5812,11 +5778,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return !isSplitScreenPrimary || allowSplitScreenPrimaryAnimation; } - @Override - boolean isChangingAppTransition() { - return task != null ? task.isChangingAppTransition() : super.isChangingAppTransition(); - } - /** * Creates a layer to apply crop to an animation. */ @@ -5837,84 +5798,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A this, endDeferFinishCallback); } - private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) { - if (mWmService.mDisableTransitionAnimation - || !isVisible() - || getDisplayContent().mAppTransition.isTransitionSet() - || getSurfaceControl() == null) { - return false; - } - // Only do an animation into and out-of freeform mode for now. Other mode - // transition animations are currently handled by system-ui. - return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM); - } - @Override boolean isWaitingForTransitionStart() { final DisplayContent dc = getDisplayContent(); return dc != null && dc.mAppTransition.isTransitionSet() && (dc.mOpeningApps.contains(this) || dc.mClosingApps.contains(this) - || dc.mChangingApps.contains(this)); - } - - /** - * Initializes a change transition. Because the app is visible already, there is a small period - * of time where the user can see the app content/window update before the transition starts. - * To prevent this, we immediately take a snapshot and place the app/snapshot into a leash which - * "freezes" the location/crop until the transition starts. - * <p> - * Here's a walk-through of the process: - * 1. Create a temporary leash ("interim-change-leash") and reparent the app to it. - * 2. Set the temporary leash's position/crop to the current state. - * 3. Create a snapshot and place that at the top of the leash to cover up content changes. - * 4. Once the transition is ready, it will reparent the app to the animation leash. - * 5. Detach the interim-change-leash. - */ - private void initializeChangeTransition(Rect startBounds) { - mDisplayContent.prepareAppTransition(TRANSIT_TASK_CHANGE_WINDOWING_MODE, - false /* alwaysKeepCurrent */, 0, false /* forceOverride */); - mDisplayContent.mChangingApps.add(this); - mTransitStartRect.set(startBounds); - - final SurfaceControl.Builder builder = makeAnimationLeash() - .setParent(getAnimationLeashParent()) - .setName(getSurfaceControl() + " - interim-change-leash"); - mTransitChangeLeash = builder.build(); - Transaction t = getPendingTransaction(); - t.setWindowCrop(mTransitChangeLeash, startBounds.width(), startBounds.height()); - t.setPosition(mTransitChangeLeash, startBounds.left, startBounds.top); - t.show(mTransitChangeLeash); - t.reparent(getSurfaceControl(), mTransitChangeLeash); - onAnimationLeashCreated(t, mTransitChangeLeash); - - // Skip creating snapshot if this transition is controlled by a remote animator which - // doesn't need it. - ArraySet<Integer> activityTypes = new ArraySet<>(); - activityTypes.add(getActivityType()); - RemoteAnimationAdapter adapter = - mDisplayContent.mAppTransitionController.getRemoteAnimationOverride( - this, TRANSIT_TASK_CHANGE_WINDOWING_MODE, activityTypes); - if (adapter != null && !adapter.getChangeNeedsSnapshot()) { - return; - } - - if (mThumbnail == null && task != null && !hasCommittedReparentToAnimationLeash()) { - SurfaceControl.ScreenshotGraphicBuffer snapshot = - mWmService.mTaskSnapshotController.createTaskSnapshot( - task, 1 /* scaleFraction */); - if (snapshot != null) { - mThumbnail = new WindowContainerThumbnail(mWmService.mSurfaceFactory, t, this, - snapshot.getGraphicBuffer(), true /* relative */); - } - } + || dc.mChangingContainers.contains(this)); } - @Override - public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) { + private int getAnimationLayer() { // The leash is parented to the animation layer. We need to preserve the z-order by using // the prefix order index, but we boost if necessary. - int layer = 0; + int layer; if (!inPinnedWindowingMode()) { layer = getPrefixOrderIndex(); } else { @@ -5927,21 +5823,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (mNeedsZBoost) { layer += Z_BOOST_BASE; } - if (!mNeedsAnimationBoundsLayer) { - t.setLayer(leash, layer); - } - - final DisplayContent dc = getDisplayContent(); - dc.assignStackOrdering(); + return layer; + } - if (leash == mTransitChangeLeash) { - // This is a temporary state so skip any animation notifications - return; - } else if (mTransitChangeLeash != null) { - // unparent mTransitChangeLeash for clean-up - clearChangeLeash(t, false /* cancel */); - } + @Override + public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) { + t.setLayer(leash, getAnimationLayer()); + getDisplayContent().assignStackOrdering(); + } + @Override + public void onLeashAnimationStarting(Transaction t, SurfaceControl leash) { if (mAnimatingActivityRegistry != null) { mAnimatingActivityRegistry.notifyStarting(this); } @@ -5969,7 +5861,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // surface size has already same as the animating container. t.setWindowCrop(mAnimationBoundsLayer, mTmpRect); } - t.setLayer(mAnimationBoundsLayer, layer); + t.setLayer(leash, 0); + t.setLayer(mAnimationBoundsLayer, getAnimationLayer()); // Reparent leash to animation bounds layer. t.reparent(leash, mAnimationBoundsLayer); @@ -6001,10 +5894,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return mLastSurfaceShowing; } - boolean isInChangeTransition() { - return mTransitChangeLeash != null || AppTransition.isChangeTransit(mTransit); - } - void attachThumbnailAnimation() { if (!isAnimating(PARENTS)) { return; @@ -6141,31 +6030,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } - /** - * @param cancel {@code true} if clearing the leash due to cancelling instead of transferring - * to another leash. - */ - private void clearChangeLeash(Transaction t, boolean cancel) { - if (mTransitChangeLeash == null) { - return; - } - if (cancel) { - clearThumbnail(); - SurfaceControl sc = getSurfaceControl(); - SurfaceControl parentSc = getParentSurfaceControl(); - // Don't reparent if surface is getting destroyed - if (parentSc != null && sc != null) { - t.reparent(sc, getParentSurfaceControl()); - } - } - t.hide(mTransitChangeLeash); - t.remove(mTransitChangeLeash); - mTransitChangeLeash = null; - if (cancel) { - onAnimationLeashLost(t); - } - } - void clearAnimatingFlags() { boolean wallpaperMightChange = false; for (int i = mChildren.size() - 1; i >= 0; i--) { @@ -6181,7 +6045,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A void cancelAnimation() { cancelAnimationOnly(); clearThumbnail(); - clearChangeLeash(getPendingTransaction(), true /* cancel */); + mSurfaceFreezer.unfreeze(getPendingTransaction()); } /** @@ -6226,6 +6090,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mRemoteAnimationDefinition = null; } + @Override RemoteAnimationDefinition getRemoteAnimationDefinition() { return mRemoteAnimationDefinition; } @@ -6686,8 +6551,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; } } - final int prevWinMode = getWindowingMode(); - mTmpPrevBounds.set(getBounds()); super.onConfigurationChanged(newParentConfig); if (shouldUseSizeCompatMode()) { @@ -6712,12 +6575,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } - final int newWinMode = getWindowingMode(); - if ((prevWinMode != newWinMode) && (mDisplayContent != null) - && shouldStartChangeTransition(prevWinMode, newWinMode)) { - initializeChangeTransition(mTmpPrevBounds); - } - // Configuration's equality doesn't consider seq so if only seq number changes in resolved // override configuration. Therefore ConfigurationContainer doesn't change merged override // configuration, but it's used to push configuration changes so explicitly update that. @@ -6755,18 +6612,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // for the next re-entry into PiP (assuming the activity is not hidden or destroyed) final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask(); if (pinnedStack == null) return; - final Rect stackBounds; - if (pinnedStack.lastAnimatingBoundsWasToFullscreen()) { - // We are animating the bounds, use the pre-animation bounds to save the snap - // fraction - stackBounds = pinnedStack.mPreAnimationBounds; - } else { - // We skip the animation if the fullscreen configuration is not compatible, so - // use the current bounds to calculate the saved snap fraction instead - // (see PinnedActivityStack.skipResizeAnimation()) - stackBounds = mTmpRect; - pinnedStack.getBounds(stackBounds); - } + final Rect stackBounds = mTmpRect; + pinnedStack.getBounds(stackBounds); mDisplayContent.mPinnedStackControllerLocked.saveReentryBounds( mActivityComponent, stackBounds); } @@ -7612,6 +7459,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + @Override void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(HASH_CODE, System.identityHashCode(this)); diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 2f1cc0532ccc..598389b1ba2b 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -17,8 +17,6 @@ package com.android.server.wm; import static android.app.ActivityTaskManager.INVALID_TASK_ID; -import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT; -import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; @@ -39,16 +37,10 @@ import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING; import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; -import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; import static android.view.Display.INVALID_DISPLAY; -import static android.view.WindowManager.DOCKED_BOTTOM; -import static android.view.WindowManager.DOCKED_INVALID; -import static android.view.WindowManager.DOCKED_LEFT; -import static android.view.WindowManager.DOCKED_RIGHT; -import static android.view.WindowManager.DOCKED_TOP; import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE; @@ -98,17 +90,8 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; -import static com.android.server.wm.BoundsAnimationController.FADE_IN; -import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; -import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; -import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; -import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; import static com.android.server.wm.TaskProto.ACTIVITIES; import static com.android.server.wm.TaskProto.ACTIVITY_TYPE; -import static com.android.server.wm.TaskProto.ADJUSTED_BOUNDS; -import static com.android.server.wm.TaskProto.ADJUSTED_FOR_IME; -import static com.android.server.wm.TaskProto.ADJUST_DIVIDER_AMOUNT; -import static com.android.server.wm.TaskProto.ADJUST_IME_AMOUNT; import static com.android.server.wm.TaskProto.ANIMATING_BOUNDS; import static com.android.server.wm.TaskProto.BOUNDS; import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER; @@ -117,7 +100,6 @@ import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS; import static com.android.server.wm.TaskProto.DISPLAY_ID; import static com.android.server.wm.TaskProto.FILLS_PARENT; import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS; -import static com.android.server.wm.TaskProto.MINIMIZE_AMOUNT; import static com.android.server.wm.TaskProto.MIN_HEIGHT; import static com.android.server.wm.TaskProto.MIN_WIDTH; import static com.android.server.wm.TaskProto.ORIG_ACTIVITY; @@ -172,7 +154,6 @@ import android.util.Log; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.Display; -import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.ITaskOrganizer; import android.view.SurfaceControl; @@ -181,8 +162,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.logging.MetricsLoggerWrapper; -import com.android.internal.policy.DividerSnapAlgorithm; -import com.android.internal.policy.DockedDividerUtils; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledFunction; import com.android.internal.util.function.pooled.PooledLambda; @@ -202,7 +181,7 @@ import java.util.function.Consumer; /** * State and management of a single stack of activities. */ -class ActivityStack extends Task implements BoundsAnimationTarget { +class ActivityStack extends Task { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_ATM; static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE; private static final String TAG_APP = TAG + POSTFIX_APP; @@ -242,13 +221,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { /** Stack is completely invisible. */ static final int STACK_VISIBILITY_INVISIBLE = 2; - /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to - * restrict IME adjustment so that a min portion of top stack remains visible.*/ - private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f; - - /** Dimming amount for non-focused stack when stacks are IME-adjusted. */ - private static final float IME_ADJUST_DIM_AMOUNT = 0.25f; - enum ActivityState { INITIALIZING, STARTED, @@ -298,28 +270,10 @@ class ActivityStack extends Task implements BoundsAnimationTarget { /** For Pinned stack controlling. */ private Rect mTmpToBounds = new Rect(); - /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */ - private final Rect mAdjustedBounds = new Rect(); - - /** - * Fully adjusted IME bounds. These are different from {@link #mAdjustedBounds} because they - * represent the state when the animation has ended. - */ - private final Rect mFullyAdjustedImeBounds = new Rect(); - /** Detach this stack from its display when animation completes. */ // TODO: maybe tie this to WindowContainer#removeChild some how... private boolean mDeferRemoval; - private final Rect mTmpAdjustedBounds = new Rect(); - private boolean mAdjustedForIme; - private boolean mImeGoingAway; - private WindowState mImeWin; - private float mMinimizeAmount; - private float mAdjustImeAmount; - private float mAdjustDividerAmount; - private final int mDockedStackMinimizeThickness; - // If this is true, we are in the bounds animating mode. The task will be down or upscaled to // perfectly fit the region it would have been cropped to. We may also avoid certain logic we // would otherwise apply while resizing, while resizing in the bounds animating mode. @@ -327,11 +281,8 @@ class ActivityStack extends Task implements BoundsAnimationTarget { // Set when an animation has been requested but has not yet started from the UI thread. This is // cleared when the animation actually starts. private boolean mBoundsAnimatingRequested = false; - private boolean mBoundsAnimatingToFullscreen = false; - private boolean mCancelCurrentBoundsAnimation = false; private Rect mBoundsAnimationTarget = new Rect(); private Rect mBoundsAnimationSourceHintBounds = new Rect(); - private @BoundsAnimationController.AnimationType int mAnimationType; Rect mPreAnimationBounds = new Rect(); @@ -650,8 +601,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { _realActivitySuspended, userSetupComplete, minWidth, minHeight, info, _voiceSession, _voiceInteractor, stack); - mDockedStackMinimizeThickness = mWmService.mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.docked_stack_minimize_thickness); EventLogTags.writeWmStackCreated(id); mHandler = new ActivityStackHandler(mStackSupervisor.mLooper); mCurrentUser = mAtmService.mAmInternal.getCurrentUserId(); @@ -790,17 +739,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { setWindowingMode(windowingMode, false /* animate */, false /* showRecents */, false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */, false /* creating */); - if (windowingMode == WINDOWING_MODE_PINNED) { - // This stack will be visible before SystemUI requests PiP animation to start, and if - // the selected animation type is fade in, this stack will be close first. If the - // animation does not start early, user may see this full screen window appear again - // after the closing transition finish, which could cause screen to flicker. - // Check if animation should start here in advance. - final BoundsAnimationController controller = getDisplay().mBoundsAnimationController; - if (controller.isAnimationTypeFadeIn()) { - setPinnedStackAlpha(0f); - } - } } /** @@ -905,6 +843,9 @@ class ActivityStack extends Task implements BoundsAnimationTarget { likelyResolvedMode = parent != null ? parent.getWindowingMode() : WINDOWING_MODE_FULLSCREEN; } + if (currentMode == WINDOWING_MODE_PINNED) { + mAtmService.getTaskChangeNotificationController().notifyActivityUnpinned(); + } if (sendNonResizeableNotification && likelyResolvedMode != WINDOWING_MODE_FULLSCREEN && topActivity != null && !topActivity.noDisplay && topActivity.isNonResizableOrForcedResizable(likelyResolvedMode)) { @@ -1217,8 +1158,8 @@ class ActivityStack extends Task implements BoundsAnimationTarget { @Override boolean isFocusable() { - return super.isFocusable() && !(inSplitScreenPrimaryWindowingMode() - && mRootWindowContainer.mIsDockMinimized); + // Special check for tile which isn't really in the hierarchy + return mTile != null ? mTile.isFocusable() : super.isFocusable(); } boolean isTopActivityFocusable() { @@ -3480,91 +3421,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { } } - void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds, int animationDuration, - boolean fromFullscreen) { - if (!inPinnedWindowingMode()) return; - - /** - * TODO(b/146594635): Remove all PIP animation code from WM once SysUI handles animation. - * If this PIP Task is controlled by a TaskOrganizer, the animation occurs entirely - * on the TaskOrganizer side, so we just hand over the leash without doing any animation. - * We have to be careful to not schedule the enter-pip callback as the TaskOrganizer - * needs to have flexibility to schedule that at an appropriate point in the animation. - */ - if (isControlledByTaskOrganizer()) return; - if (toBounds == null /* toFullscreen */) { - final Configuration parentConfig = getParent().getConfiguration(); - final ActivityRecord top = topRunningNonOverlayTaskActivity(); - if (top != null && !top.isConfigurationCompatible(parentConfig)) { - // The final orientation of this activity will change after moving to full screen. - // Start freezing screen here to prevent showing a temporary full screen window. - top.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT); - dismissPip(); - return; - } - } - - // Get the from-bounds - final Rect fromBounds = new Rect(); - getBounds(fromBounds); - - // Get non-null fullscreen to-bounds for animating if the bounds are null - @BoundsAnimationController.SchedulePipModeChangedState int schedulePipModeChangedState = - NO_PIP_MODE_CHANGED_CALLBACKS; - final boolean toFullscreen = toBounds == null; - if (toFullscreen) { - if (fromFullscreen) { - throw new IllegalArgumentException("Should not defer scheduling PiP mode" - + " change on animation to fullscreen."); - } - schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START; - - mWmService.getStackBounds( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds); - if (!mTmpToBounds.isEmpty()) { - // If there is a fullscreen bounds, use that - toBounds = new Rect(mTmpToBounds); - } else { - // Otherwise, use the display bounds - toBounds = new Rect(); - getDisplayContent().getBounds(toBounds); - } - } else if (fromFullscreen) { - schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; - } - - setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen); - - final Rect finalToBounds = toBounds; - final @BoundsAnimationController.SchedulePipModeChangedState int - finalSchedulePipModeChangedState = schedulePipModeChangedState; - final DisplayContent displayContent = getDisplayContent(); - @BoundsAnimationController.AnimationType int intendedAnimationType = - displayContent.mBoundsAnimationController.getAnimationType(); - if (intendedAnimationType == FADE_IN) { - if (fromFullscreen) { - setPinnedStackAlpha(0f); - } - if (toBounds.width() == fromBounds.width() - && toBounds.height() == fromBounds.height()) { - intendedAnimationType = BoundsAnimationController.BOUNDS; - } else if (!fromFullscreen && !toBounds.equals(fromBounds)) { - // intendedAnimationType may have been reset at the end of RecentsAnimation, - // force it to BOUNDS type if we know for certain we're animating to - // a different bounds, especially for expand and collapse of PiP window. - intendedAnimationType = BoundsAnimationController.BOUNDS; - } - } - - final @BoundsAnimationController.AnimationType int animationType = intendedAnimationType; - mCancelCurrentBoundsAnimation = false; - displayContent.mBoundsAnimationController.getHandler().post(() -> { - displayContent.mBoundsAnimationController.animateBounds(this, fromBounds, - finalToBounds, animationDuration, finalSchedulePipModeChangedState, - fromFullscreen, toFullscreen, animationType); - }); - } - void dismissPip() { if (!isActivityTypeStandardOrUndefined()) { throw new IllegalArgumentException( @@ -3593,62 +3449,10 @@ class ActivityStack extends Task implements BoundsAnimationTarget { }); } - private void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds, - boolean forceUpdate) { - // It is guaranteed that the activities requiring the update will be in the pinned stack at - // this point (either reparented before the animation into PiP, or before reparenting after - // the animation out of PiP) - if (!isAttached()) { - return; - } - final PooledConsumer c = PooledLambda.obtainConsumer( - ActivityStackSupervisor::updatePictureInPictureMode, mStackSupervisor, - PooledLambda.__(Task.class), targetStackBounds, forceUpdate); - forAllLeafTasks(c, true /* traverseTopToBottom */); - c.recycle(); - } - void prepareFreezingTaskBounds() { forAllLeafTasks(Task::prepareFreezingBounds, true /* traverseTopToBottom */); } - /** - * Overrides the adjusted bounds, i.e. sets temporary layout bounds which are different from - * the normal task bounds. - * - * @param bounds The adjusted bounds. - */ - private void setAdjustedBounds(Rect bounds) { - if (mAdjustedBounds.equals(bounds) && !isAnimatingForIme()) { - return; - } - - mAdjustedBounds.set(bounds); - final boolean adjusted = !mAdjustedBounds.isEmpty(); - Rect insetBounds = null; - if (adjusted && isAdjustedForMinimizedDockedStack()) { - insetBounds = getRawBounds(); - } else if (adjusted && mAdjustedForIme) { - if (mImeGoingAway) { - insetBounds = getRawBounds(); - } else { - insetBounds = mFullyAdjustedImeBounds; - } - } - - if (!matchParentBounds()) { - final boolean alignBottom = mAdjustedForIme && getDockSide() == DOCKED_TOP; - final PooledConsumer c = PooledLambda.obtainConsumer(Task::alignToAdjustedBounds, - PooledLambda.__(Task.class), adjusted ? mAdjustedBounds : getRawBounds(), - insetBounds, alignBottom); - forAllLeafTasks(c, true /* traverseTopToBottom */); - c.recycle(); - } - - mDisplayContent.setLayoutNeeded(); - updateSurfaceBounds(); - } - @Override public int setBounds(Rect bounds) { // Calling Task#setBounds() for leaf task since this is the a specialization of @@ -3667,8 +3471,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { final int result = super.setBounds(!inMultiWindowMode() ? null : bounds); - updateAdjustedBounds(); - updateSurfaceBounds(); return result; } @@ -3690,47 +3492,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { bounds.set(getBounds()); } - @Override - public Rect getBounds() { - // If we're currently adjusting for IME or minimized docked stack, we use the adjusted - // bounds; otherwise, no need to adjust the output bounds if fullscreen or the docked - // stack is visible since it is already what we want to represent to the rest of the - // system. - if (!mAdjustedBounds.isEmpty()) { - return mAdjustedBounds; - } else { - return super.getBounds(); - } - } - - /** - * Sets the bounds animation target bounds ahead of an animation. This can't currently be done - * in onAnimationStart() since that is started on the UiThread. - */ - private void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds, - boolean toFullscreen) { - if (mAnimationType == BoundsAnimationController.BOUNDS) { - mBoundsAnimatingRequested = true; - } - mBoundsAnimatingToFullscreen = toFullscreen; - if (destBounds != null) { - mBoundsAnimationTarget.set(destBounds); - } else { - mBoundsAnimationTarget.setEmpty(); - } - if (sourceHintBounds != null) { - mBoundsAnimationSourceHintBounds.set(sourceHintBounds); - } else if (!mBoundsAnimating) { - // If the bounds are already animating, we don't want to reset the source hint. This is - // because the source hint is sent when starting the animation from the client that - // requested to enter pip. Other requests can adjust the pip bounds during an animation, - // but could accidentally reset the source hint bounds. - mBoundsAnimationSourceHintBounds.setEmpty(); - } - - mPreAnimationBounds.set(getRawBounds()); - } - /** * @return the final bounds for the bounds animation. */ @@ -3763,137 +3524,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { } /** - * Reset the current animation running on {@link #mBoundsAnimationTarget}. - * - * @param destinationBounds the final destination bounds - */ - void resetCurrentBoundsAnimation(Rect destinationBounds) { - boolean animating = (mBoundsAnimatingRequested || mBoundsAnimating) - && !mBoundsAnimationTarget.isEmpty(); - - // The final boundary is updated while there is an existing boundary animation. Let's - // cancel this animation to prevent the obsolete animation overwritten updated bounds. - if (animating && !destinationBounds.equals(mBoundsAnimationTarget)) { - final BoundsAnimationController controller = - getDisplayContent().mBoundsAnimationController; - controller.getHandler().post(() -> controller.cancel(this)); - } - // Once we've set the bounds based on the rotation of the old bounds in the new - // orientation, clear the animation target bounds since they are obsolete, and - // cancel any currently running animations - mBoundsAnimationTarget.setEmpty(); - mBoundsAnimationSourceHintBounds.setEmpty(); - mCancelCurrentBoundsAnimation = true; - } - - /** - * Updates the passed-in {@code inOutBounds} based on the current state of the - * docked controller. This gets run *after* the override configuration is updated, so it's - * safe to rely on the controller's state in here (though eventually this dependence should - * be removed). - * - * This does NOT modify this TaskStack's configuration. However, it does, for the time-being, - * update docked controller state. - * - * @param parentConfig the parent configuration for reference. - * @param inOutBounds the bounds to update (both input and output). - */ - void calculateDockedBoundsForConfigChange(Configuration parentConfig, Rect inOutBounds) { - final boolean primary = - getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; - repositionSplitScreenStackAfterRotation(parentConfig, primary, inOutBounds); - final DisplayCutout cutout = mDisplayContent.getDisplayInfo().displayCutout; - snapDockedStackAfterRotation(parentConfig, cutout, inOutBounds); - if (primary) { - final int newDockSide = getDockSide(parentConfig, inOutBounds); - // Update the dock create mode and clear the dock create bounds, these - // might change after a rotation and the original values will be invalid. - mWmService.setDockedStackCreateStateLocked( - (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP) - ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT - : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, - null); - mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide); - } - } - - /** - * Some primary split screen sides are not allowed by the policy. This method queries the policy - * and moves the primary stack around if needed. - * - * @param parentConfig the configuration of the stack's parent. - * @param primary true if adjusting the primary docked stack, false for secondary. - * @param inOutBounds the bounds of the stack to adjust. - */ - void repositionSplitScreenStackAfterRotation(Configuration parentConfig, boolean primary, - Rect inOutBounds) { - final int dockSide = getDockSide(mDisplayContent, parentConfig, inOutBounds); - final int otherDockSide = DockedDividerUtils.invertDockSide(dockSide); - final int primaryDockSide = primary ? dockSide : otherDockSide; - if (mDisplayContent.getDockedDividerController() - .canPrimaryStackDockTo(primaryDockSide, - parentConfig.windowConfiguration.getBounds(), - parentConfig.windowConfiguration.getRotation())) { - return; - } - final Rect parentBounds = parentConfig.windowConfiguration.getBounds(); - switch (otherDockSide) { - case DOCKED_LEFT: - int movement = inOutBounds.left; - inOutBounds.left -= movement; - inOutBounds.right -= movement; - break; - case DOCKED_RIGHT: - movement = parentBounds.right - inOutBounds.right; - inOutBounds.left += movement; - inOutBounds.right += movement; - break; - case DOCKED_TOP: - movement = inOutBounds.top; - inOutBounds.top -= movement; - inOutBounds.bottom -= movement; - break; - case DOCKED_BOTTOM: - movement = parentBounds.bottom - inOutBounds.bottom; - inOutBounds.top += movement; - inOutBounds.bottom += movement; - break; - } - } - - /** - * Snaps the bounds after rotation to the closest snap target for the docked stack. - */ - void snapDockedStackAfterRotation(Configuration parentConfig, DisplayCutout displayCutout, - Rect outBounds) { - - // Calculate the current position. - final int dividerSize = mDisplayContent.getDockedDividerController().getContentWidth(); - final int dockSide = getDockSide(parentConfig, outBounds); - final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds, - dockSide, dividerSize); - final int displayWidth = parentConfig.windowConfiguration.getBounds().width(); - final int displayHeight = parentConfig.windowConfiguration.getBounds().height(); - - // Snap the position to a target. - final int rotation = parentConfig.windowConfiguration.getRotation(); - final int orientation = parentConfig.orientation; - mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, displayWidth, displayHeight, - displayCutout, outBounds); - final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm( - mWmService.mContext.getResources(), displayWidth, displayHeight, - dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds, - getDockSide(), isMinimizedDockAndHomeStackResizable()); - final DividerSnapAlgorithm.SnapTarget target = - algorithm.calculateNonDismissingSnapTarget(dividerPosition); - - // Recalculate the bounds based on the position of the target. - DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide, - outBounds, displayWidth, displayHeight, - dividerSize); - } - - /** * Put a Task in this stack. Used for adding only. * When task is added to top of the stack, the entire branch of the hierarchy (including stack * and display) will be brought to top. @@ -4083,445 +3713,8 @@ class ActivityStack extends Task implements BoundsAnimationTarget { } } - /** - * Determines the stack and task bounds of the other stack when in docked mode. The current task - * bounds is passed in but depending on the stack, the task and stack must match. Only in - * minimized mode with resizable launcher, the other stack ignores calculating the stack bounds - * and uses the task bounds passed in as the stack and task bounds, otherwise the stack bounds - * is calculated and is also used for its task bounds. - * If any of the out bounds are empty, it represents default bounds - * - * @param currentTempTaskBounds the current task bounds of the other stack - * @param outStackBounds the calculated stack bounds of the other stack - * @param outTempTaskBounds the calculated task bounds of the other stack - */ - void getStackDockedModeBounds(Rect dockedBounds, - Rect currentTempTaskBounds, Rect outStackBounds, Rect outTempTaskBounds) { - final Configuration parentConfig = getParent().getConfiguration(); - outTempTaskBounds.setEmpty(); - - if (dockedBounds == null || dockedBounds.isEmpty()) { - // Calculate the primary docked bounds. - final boolean dockedOnTopOrLeft = mWmService.mDockedStackCreateMode - == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; - getStackDockedModeBounds(parentConfig, - true /* primary */, outStackBounds, dockedBounds, - mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft); - return; - } - final int dockedSide = getDockSide(parentConfig, dockedBounds); - - // When the home stack is resizable, should always have the same stack and task bounds - if (isActivityTypeHome()) { - final Task homeTask = getTopMostTask(); - if (homeTask == null || homeTask.isResizeable()) { - // Calculate the home stack bounds when in docked mode and the home stack is - // resizeable. - getDisplayContent().mDividerControllerLocked - .getHomeStackBoundsInDockedMode(parentConfig, - dockedSide, outStackBounds); - } else { - // Home stack isn't resizeable, so don't specify stack bounds. - outStackBounds.setEmpty(); - } - - outTempTaskBounds.set(outStackBounds); - return; - } - - // When minimized state, the stack bounds for all non-home and docked stack bounds should - // match the passed task bounds - if (isMinimizedDockAndHomeStackResizable() && currentTempTaskBounds != null) { - outStackBounds.set(currentTempTaskBounds); - return; - } - - if (dockedSide == DOCKED_INVALID) { - // Not sure how you got here...Only thing we can do is return current bounds. - Slog.e(TAG_WM, "Failed to get valid docked side for docked stack"); - outStackBounds.set(getRawBounds()); - return; - } - - final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT; - getStackDockedModeBounds(parentConfig, - false /* primary */, outStackBounds, dockedBounds, - mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft); - } - - /** - * Outputs the bounds a stack should be given the presence of a docked stack on the display. - * @param parentConfig The parent configuration. - * @param primary {@code true} if getting the primary stack bounds. - * @param outBounds Output bounds that should be used for the stack. - * @param dockedBounds Bounds of the docked stack. - * @param dockDividerWidth We need to know the width of the divider make to the output bounds - * close to the side of the dock. - * @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen. - */ - private void getStackDockedModeBounds(Configuration parentConfig, boolean primary, - Rect outBounds, Rect dockedBounds, int dockDividerWidth, - boolean dockOnTopOrLeft) { - final Rect displayRect = parentConfig.windowConfiguration.getBounds(); - final boolean splitHorizontally = displayRect.width() > displayRect.height(); - - outBounds.set(displayRect); - if (primary) { - if (mWmService.mDockedStackCreateBounds != null) { - outBounds.set(mWmService.mDockedStackCreateBounds); - return; - } - - // The initial bounds of the docked stack when it is created about half the screen space - // and its bounds can be adjusted after that. The bounds of all other stacks are - // adjusted to occupy whatever screen space the docked stack isn't occupying. - final DisplayCutout displayCutout = mDisplayContent.getDisplayInfo().displayCutout; - mDisplayContent.getDisplayPolicy().getStableInsetsLw( - parentConfig.windowConfiguration.getRotation(), - displayRect.width(), displayRect.height(), displayCutout, mTmpRect2); - final int position = new DividerSnapAlgorithm(mWmService.mContext.getResources(), - displayRect.width(), - displayRect.height(), - dockDividerWidth, - parentConfig.orientation == ORIENTATION_PORTRAIT, - mTmpRect2).getMiddleTarget().position; - - if (dockOnTopOrLeft) { - if (splitHorizontally) { - outBounds.right = position; - } else { - outBounds.bottom = position; - } - } else { - if (splitHorizontally) { - outBounds.left = position + dockDividerWidth; - } else { - outBounds.top = position + dockDividerWidth; - } - } - return; - } - - // Other stacks occupy whatever space is left by the docked stack. - if (!dockOnTopOrLeft) { - if (splitHorizontally) { - outBounds.right = dockedBounds.left - dockDividerWidth; - } else { - outBounds.bottom = dockedBounds.top - dockDividerWidth; - } - } else { - if (splitHorizontally) { - outBounds.left = dockedBounds.right + dockDividerWidth; - } else { - outBounds.top = dockedBounds.bottom + dockDividerWidth; - } - } - DockedDividerUtils.sanitizeStackBounds(outBounds, !dockOnTopOrLeft); - } - - void resetDockedStackToMiddle() { - if (!inSplitScreenPrimaryWindowingMode()) { - throw new IllegalStateException("Not a docked stack=" + this); - } - - mWmService.mDockedStackCreateBounds = null; - - final Rect bounds = new Rect(); - final Rect tempBounds = new Rect(); - getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */, - bounds, tempBounds); - mStackSupervisor.resizeDockedStackLocked(bounds, null /* tempTaskBounds */, - null /* tempTaskInsetBounds */, null /* tempOtherTaskBounds */, - null /* tempOtherTaskInsetBounds */, false /* preserveWindows */, - false /* deferResume */); - } - - /** - * Adjusts the stack bounds if the IME is visible. - * - * @param imeWin The IME window. - * @param keepLastAmount Use {@code true} to keep the last adjusted amount from - * {@link DockedStackDividerController} for adjusting the stack bounds, - * Use {@code false} to reset adjusted amount as 0. - * @see #updateAdjustForIme(float, float, boolean) - */ - void setAdjustedForIme(WindowState imeWin, boolean keepLastAmount) { - mImeWin = imeWin; - mImeGoingAway = false; - if (!mAdjustedForIme || keepLastAmount) { - mAdjustedForIme = true; - DockedStackDividerController controller = getDisplayContent().mDividerControllerLocked; - final float adjustImeAmount = keepLastAmount ? controller.mLastAnimationProgress : 0f; - final float adjustDividerAmount = keepLastAmount ? controller.mLastDividerProgress : 0f; - updateAdjustForIme(adjustImeAmount, adjustDividerAmount, true /* force */); - } - } - - boolean isAdjustedForIme() { - return mAdjustedForIme; - } - - boolean isAnimatingForIme() { - return mImeWin != null && mImeWin.isAnimatingLw(); - } - - /** - * Update the stack's bounds (crop or position) according to the IME window's - * current position. When IME window is animated, the bottom stack is animated - * together to track the IME window's current position, and the top stack is - * cropped as necessary. - * - * @return true if a traversal should be performed after the adjustment. - */ - boolean updateAdjustForIme(float adjustAmount, float adjustDividerAmount, boolean force) { - if (adjustAmount != mAdjustImeAmount - || adjustDividerAmount != mAdjustDividerAmount || force) { - mAdjustImeAmount = adjustAmount; - mAdjustDividerAmount = adjustDividerAmount; - updateAdjustedBounds(); - return isVisible(); - } else { - return false; - } - } - - /** - * Resets the adjustment after it got adjusted for the IME. - * @param adjustBoundsNow if true, reset and update the bounds immediately and forget about - * animations; otherwise, set flag and animates the window away together - * with IME window. - */ - void resetAdjustedForIme(boolean adjustBoundsNow) { - if (adjustBoundsNow) { - mImeWin = null; - mImeGoingAway = false; - mAdjustImeAmount = 0f; - mAdjustDividerAmount = 0f; - if (!mAdjustedForIme) { - return; - } - mAdjustedForIme = false; - updateAdjustedBounds(); - mWmService.setResizeDimLayer(false, getWindowingMode(), 1.0f); - } else { - mImeGoingAway |= mAdjustedForIme; - } - } - - /** - * Sets the amount how much we currently minimize our stack. - * - * @param minimizeAmount The amount, between 0 and 1. - * @return Whether the amount has changed and a layout is needed. - */ - boolean setAdjustedForMinimizedDock(float minimizeAmount) { - if (minimizeAmount != mMinimizeAmount) { - mMinimizeAmount = minimizeAmount; - updateAdjustedBounds(); - return isVisible(); - } else { - return false; - } - } - boolean shouldIgnoreInput() { - return isAdjustedForMinimizedDockedStack() - || (inSplitScreenPrimaryWindowingMode() && isMinimizedDockAndHomeStackResizable()); - } - - /** - * Puts all visible tasks that are adjusted for IME into resizing mode and adds the windows - * to the list of to be drawn windows the service is waiting for. - */ - void beginImeAdjustAnimation() { - forAllLeafTasks((t) -> { - if (t.hasContentToDisplay()) { - t.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER); - t.setWaitingForDrawnIfResizingChanged(); - } - }, true /* traverseTopToBottom */); - } - - /** Resets the resizing state of all windows. */ - void endImeAdjustAnimation() { - forAllLeafTasks((t) -> { - t.setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER); - }, true /* traverseTopToBottom */); - } - - private int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) { - return displayContentRect.top + (int) - ((originalStackBottom - displayContentRect.top) * ADJUSTED_STACK_FRACTION_MIN); - } - - private boolean adjustForIME(final WindowState imeWin) { - // To prevent task stack resize animation may flicking when playing app transition - // animation & IME window enter animation in parallel, we need to make sure app - // transition is done and then adjust task size for IME, skip the new adjusted frame when - // app transition is still running. - if (getDisplayContent().mAppTransition.isRunning()) { - return false; - } - - final int dockedSide = getDockSide(); - final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM; - if (imeWin == null || !dockedTopOrBottom) { - return false; - } - - final Rect displayStableRect = mTmpRect; - final Rect contentBounds = mTmpRect2; - - // Calculate the content bounds excluding the area occupied by IME - getDisplayContent().getStableRect(displayStableRect); - contentBounds.set(displayStableRect); - int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top); - - imeTop += imeWin.getGivenContentInsetsLw().top; - if (contentBounds.bottom > imeTop) { - contentBounds.bottom = imeTop; - } - - final int yOffset = displayStableRect.bottom - contentBounds.bottom; - - final int dividerWidth = - getDisplayContent().mDividerControllerLocked.getContentWidth(); - final int dividerWidthInactive = - getDisplayContent().mDividerControllerLocked.getContentWidthInactive(); - - if (dockedSide == DOCKED_TOP) { - // If this stack is docked on top, we make it smaller so the bottom stack is not - // occluded by IME. We shift its bottom up by the height of the IME, but - // leaves at least 30% of the top stack visible. - final int minTopStackBottom = - getMinTopStackBottom(displayStableRect, getRawBounds().bottom); - final int bottom = Math.max( - getRawBounds().bottom - yOffset + dividerWidth - dividerWidthInactive, - minTopStackBottom); - mTmpAdjustedBounds.set(getRawBounds()); - mTmpAdjustedBounds.bottom = (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount) - * getRawBounds().bottom); - mFullyAdjustedImeBounds.set(getRawBounds()); - } else { - // When the stack is on bottom and has no focus, it's only adjusted for divider width. - final int dividerWidthDelta = dividerWidthInactive - dividerWidth; - - // When the stack is on bottom and has focus, it needs to be moved up so as to - // not occluded by IME, and at the same time adjusted for divider width. - // We try to move it up by the height of the IME window, but only to the extent - // that leaves at least 30% of the top stack visible. - // 'top' is where the top of bottom stack will move to in this case. - final int topBeforeImeAdjust = - getRawBounds().top - dividerWidth + dividerWidthInactive; - final int minTopStackBottom = - getMinTopStackBottom(displayStableRect, - getRawBounds().top - dividerWidth); - final int top = Math.max( - getRawBounds().top - yOffset, minTopStackBottom + dividerWidthInactive); - - mTmpAdjustedBounds.set(getRawBounds()); - // Account for the adjustment for IME and divider width separately. - // (top - topBeforeImeAdjust) is the amount of movement due to IME only, - // and dividerWidthDelta is due to divider width change only. - mTmpAdjustedBounds.top = - getRawBounds().top + (int) (mAdjustImeAmount * (top - topBeforeImeAdjust) - + mAdjustDividerAmount * dividerWidthDelta); - mFullyAdjustedImeBounds.set(getRawBounds()); - mFullyAdjustedImeBounds.top = top; - mFullyAdjustedImeBounds.bottom = top + getRawBounds().height(); - } - return true; - } - - private boolean adjustForMinimizedDockedStack(float minimizeAmount) { - final int dockSide = getDockSide(); - if (dockSide == DOCKED_INVALID && !mTmpAdjustedBounds.isEmpty()) { - return false; - } - - if (dockSide == DOCKED_TOP) { - mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect); - int topInset = mTmpRect.top; - mTmpAdjustedBounds.set(getRawBounds()); - mTmpAdjustedBounds.bottom = (int) (minimizeAmount * topInset + (1 - minimizeAmount) - * getRawBounds().bottom); - } else if (dockSide == DOCKED_LEFT) { - mTmpAdjustedBounds.set(getRawBounds()); - final int width = getRawBounds().width(); - mTmpAdjustedBounds.right = - (int) (minimizeAmount * mDockedStackMinimizeThickness - + (1 - minimizeAmount) * getRawBounds().right); - mTmpAdjustedBounds.left = mTmpAdjustedBounds.right - width; - } else if (dockSide == DOCKED_RIGHT) { - mTmpAdjustedBounds.set(getRawBounds()); - mTmpAdjustedBounds.left = - (int) (minimizeAmount * (getRawBounds().right - mDockedStackMinimizeThickness) - + (1 - minimizeAmount) * getRawBounds().left); - } - return true; - } - - boolean isMinimizedDockAndHomeStackResizable() { - return mDisplayContent.mDividerControllerLocked.isMinimizedDock() - && mDisplayContent.mDividerControllerLocked.isHomeStackResizable(); - } - - /** - * @return the distance in pixels how much the stack gets minimized from it's original size - */ - int getMinimizeDistance() { - final int dockSide = getDockSide(); - if (dockSide == DOCKED_INVALID) { - return 0; - } - - if (dockSide == DOCKED_TOP) { - mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect); - int topInset = mTmpRect.top; - return getRawBounds().bottom - topInset; - } else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) { - return getRawBounds().width() - mDockedStackMinimizeThickness; - } else { - return 0; - } - } - - /** - * Updates the adjustment depending on it's current state. - */ - private void updateAdjustedBounds() { - boolean adjust = false; - if (mMinimizeAmount != 0f) { - adjust = adjustForMinimizedDockedStack(mMinimizeAmount); - } else if (mAdjustedForIme) { - adjust = adjustForIME(mImeWin); - } - if (!adjust) { - mTmpAdjustedBounds.setEmpty(); - } - setAdjustedBounds(mTmpAdjustedBounds); - - final boolean isImeTarget = (mWmService.getImeFocusStackLocked() == this); - if (mAdjustedForIme && adjust && !isImeTarget) { - final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount) - * IME_ADJUST_DIM_AMOUNT; - mWmService.setResizeDimLayer(true, getWindowingMode(), alpha); - } - } - - void applyAdjustForImeIfNeeded(Task task) { - if (mMinimizeAmount != 0f || !mAdjustedForIme || mAdjustedBounds.isEmpty()) { - return; - } - - final Rect insetBounds = mImeGoingAway ? getRawBounds() : mFullyAdjustedImeBounds; - task.alignToAdjustedBounds(mAdjustedBounds, insetBounds, getDockSide() == DOCKED_TOP); - mDisplayContent.setLayoutNeeded(); - } - - - boolean isAdjustedForMinimizedDockedStack() { - return mMinimizeAmount != 0f; + return inSplitScreenPrimaryWindowingMode() && !isFocusable(); } @Override @@ -4529,17 +3722,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { pw.println(prefix + "mStackId=" + getRootTaskId()); pw.println(prefix + "mDeferRemoval=" + mDeferRemoval); pw.println(prefix + "mBounds=" + getRawBounds().toShortString()); - if (mMinimizeAmount != 0f) { - pw.println(prefix + "mMinimizeAmount=" + mMinimizeAmount); - } - if (mAdjustedForIme) { - pw.println(prefix + "mAdjustedForIme=true"); - pw.println(prefix + "mAdjustImeAmount=" + mAdjustImeAmount); - pw.println(prefix + "mAdjustDividerAmount=" + mAdjustDividerAmount); - } - if (!mAdjustedBounds.isEmpty()) { - pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString()); - } for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) { mChildren.get(taskNdx).dump(pw, prefix + " ", dumpAll); } @@ -4558,143 +3740,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { } /** - * For docked workspace (or workspace that's side-by-side to the docked), provides - * information which side of the screen was the dock anchored. - */ - int getDockSide() { - return getDockSide(mDisplayContent.getConfiguration(), getRawBounds()); - } - - int getDockSideForDisplay(DisplayContent dc) { - return getDockSide(dc, dc.getConfiguration(), getRawBounds()); - } - - int getDockSide(Configuration parentConfig, Rect bounds) { - if (mDisplayContent == null) { - return DOCKED_INVALID; - } - return getDockSide(mDisplayContent, parentConfig, bounds); - } - - private int getDockSide(DisplayContent dc, Configuration parentConfig, Rect bounds) { - return dc.getDockedDividerController().getDockSide(bounds, - parentConfig.windowConfiguration.getBounds(), - parentConfig.orientation, parentConfig.windowConfiguration.getRotation()); - } - - boolean hasTaskForUser(int userId) { - final PooledPredicate p = PooledLambda.obtainPredicate( - Task::isTaskForUser, PooledLambda.__(Task.class), userId); - final Task task = getTask(p); - p.recycle(); - return task != null; - } - - public boolean setPinnedStackSize(Rect displayedBounds, Rect configBounds) { - // Hold the lock since this is called from the BoundsAnimator running on the UiThread - synchronized (mWmService.mGlobalLock) { - if (mCancelCurrentBoundsAnimation) { - return false; - } - mStackSupervisor.resizePinnedStack(displayedBounds, configBounds); - } - - return true; - } - - void onAllWindowsDrawn() { - if (!mBoundsAnimating && !mBoundsAnimatingRequested) { - return; - } - - getDisplayContent().mBoundsAnimationController.onAllWindowsDrawn(); - } - - @Override // AnimatesBounds - public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate, - @BoundsAnimationController.AnimationType int animationType) { - // Hold the lock since this is called from the BoundsAnimator running on the UiThread - synchronized (mWmService.mGlobalLock) { - if (!isAttached()) { - // Don't run the animation if the stack is already detached - return false; - } - - if (animationType == BoundsAnimationController.BOUNDS) { - mBoundsAnimatingRequested = false; - mBoundsAnimating = true; - } - mAnimationType = animationType; - - // If we are changing UI mode, as in the PiP to fullscreen - // transition, then we need to wait for the window to draw. - if (schedulePipModeChangedCallback) { - forAllWindows((w) -> { - w.mWinAnimator.resetDrawState(); - }, false /* traverseTopToBottom */); - } - } - - if (inPinnedWindowingMode()) { - try { - mWmService.mActivityTaskManager.notifyPinnedStackAnimationStarted(); - } catch (RemoteException e) { - // I don't believe you... - } - - if ((schedulePipModeChangedCallback || animationType == FADE_IN)) { - // We need to schedule the PiP mode change before the animation up. It is possible - // in this case for the animation down to not have been completed, so always - // force-schedule and update to the client to ensure that it is notified that it - // is no longer in picture-in-picture mode - updatePictureInPictureModeForPinnedStackAnimation(null, forceUpdate); - } - } - return true; - } - - @Override // AnimatesBounds - public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize, - boolean moveToFullscreen) { - synchronized (mWmService.mGlobalLock) { - if (inPinnedWindowingMode()) { - // Update to the final bounds if requested. This is done here instead of in the - // bounds animator to allow us to coordinate this after we notify the PiP mode - // changed - - if (schedulePipModeChangedCallback) { - // We need to schedule the PiP mode change after the animation down, so use the - // final bounds - updatePictureInPictureModeForPinnedStackAnimation(mBoundsAnimationTarget, - false /* forceUpdate */); - } - - if (mAnimationType == BoundsAnimationController.FADE_IN) { - setPinnedStackAlpha(1f); - mWmService.mAtmService.notifyPinnedStackAnimationEnded(); - return; - } - - if (finalStackSize != null && !mCancelCurrentBoundsAnimation) { - setPinnedStackSize(finalStackSize, null); - } else { - // We have been canceled, so the final stack size is null, still run the - // animation-end logic - onPipAnimationEndResize(); - } - - mWmService.mAtmService.notifyPinnedStackAnimationEnded(); - if (moveToFullscreen) { - ((ActivityStack) this).dismissPip(); - } - } else { - // No PiP animation, just run the normal animation-end logic - onPipAnimationEndResize(); - } - } - } - - /** * Sets the current picture-in-picture aspect ratio. */ void setPictureInPictureAspectRatio(float aspectRatio) { @@ -4741,42 +3786,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { getDisplayContent().getPinnedStackController().setActions(actions); } - /** Called immediately prior to resizing the tasks at the end of the pinned stack animation. */ - void onPipAnimationEndResize() { - mBoundsAnimating = false; - forAllLeafTasks(Task::clearPreserveNonFloatingState, false /* traverseTopToBottom */); - mWmService.requestTraversal(); - } - - @Override - public boolean shouldDeferStartOnMoveToFullscreen() { - synchronized (mWmService.mGlobalLock) { - if (!isAttached()) { - // Unnecessary to pause the animation because the stack is detached. - return false; - } - - // Workaround for the recents animation -- normally we need to wait for the new activity - // to show before starting the PiP animation, but because we start and show the home - // activity early for the recents animation prior to the PiP animation starting, there - // is no subsequent all-drawn signal. In this case, we can skip the pause when the home - // stack is already visible and drawn. - final ActivityStack homeStack = mDisplayContent.getRootHomeTask(); - if (homeStack == null) { - return true; - } - final Task homeTask = homeStack.getTopMostTask(); - if (homeTask == null) { - return true; - } - final ActivityRecord homeApp = homeTask.getTopVisibleActivity(); - if (!homeTask.isVisible() || homeApp == null) { - return true; - } - return !homeApp.allDrawn; - } - } - /** * @return True if we are currently animating the pinned stack from fullscreen to non-fullscreen * bounds and we have a deferred PiP mode changed callback set with the animation. @@ -4798,25 +3807,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { return mBoundsAnimating; } - public boolean isAnimatingBounds() { - return mBoundsAnimating; - } - - public boolean lastAnimatingBoundsWasToFullscreen() { - return mBoundsAnimatingToFullscreen; - } - - public boolean isAnimatingBoundsToFullscreen() { - return isAnimatingBounds() && lastAnimatingBoundsWasToFullscreen(); - } - - public boolean pinnedStackResizeDisallowed() { - if (mBoundsAnimating && mCancelCurrentBoundsAnimation) { - return true; - } - return false; - } - /** Returns true if a removal action is still being deferred. */ boolean checkCompleteDeferredRemoval() { if (isAnimating(TRANSITION | CHILDREN)) { @@ -4843,21 +3833,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { || activityType == ACTIVITY_TYPE_ASSISTANT; } - @Override - public boolean setPinnedStackAlpha(float alpha) { - // Hold the lock since this is called from the BoundsAnimator running on the UiThread - synchronized (mWmService.mGlobalLock) { - final SurfaceControl sc = getSurfaceControl(); - if (sc == null || !sc.isValid()) { - // If the stack is already removed, don't bother updating any stack animation - return false; - } - getPendingTransaction().setAlpha(sc, mCancelCurrentBoundsAnimation ? 1 : alpha); - scheduleAnimation(); - return !mCancelCurrentBoundsAnimation; - } - } - public DisplayInfo getDisplayInfo() { return mDisplayContent.getDisplayInfo(); } @@ -4980,16 +3955,11 @@ class ActivityStack extends Task implements BoundsAnimationTarget { bounds.dumpDebug(proto, BOUNDS); } getOverrideDisplayedBounds().dumpDebug(proto, DISPLAYED_BOUNDS); - mAdjustedBounds.dumpDebug(proto, ADJUSTED_BOUNDS); if (mLastNonFullscreenBounds != null) { mLastNonFullscreenBounds.dumpDebug(proto, LAST_NON_FULLSCREEN_BOUNDS); } proto.write(DEFER_REMOVAL, mDeferRemoval); - proto.write(MINIMIZE_AMOUNT, mMinimizeAmount); - proto.write(ADJUSTED_FOR_IME, mAdjustedForIme); - proto.write(ADJUST_IME_AMOUNT, mAdjustImeAmount); - proto.write(ADJUST_DIVIDER_AMOUNT, mAdjustDividerAmount); proto.write(ANIMATING_BOUNDS, mBoundsAnimating); if (mSurfaceControl != null) { diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 97b638820a8a..6d7f8fb6adde 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -35,12 +35,10 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.pm.PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY; import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.graphics.Rect.copyOrNull; import static android.os.PowerManager.PARTIAL_WAKE_LOCK; import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; @@ -210,17 +208,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { /** True if the docked stack is currently being resized. */ private boolean mDockedStackResizing; - /** - * True if there are pending docked bounds that need to be applied after - * {@link #mDockedStackResizing} is reset to false. - */ - private boolean mHasPendingDockedBounds; - private Rect mPendingDockedBounds; - private Rect mPendingTempDockedTaskBounds; - private Rect mPendingTempDockedTaskInsetBounds; - private Rect mPendingTempOtherTaskBounds; - private Rect mPendingTempOtherTaskInsetBounds; - // Activity actions an app cannot start if it uses a permission which is not granted. private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION = new ArrayMap<>(); @@ -387,15 +374,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { */ private final ArraySet<Integer> mResizingTasksDuringAnimation = new ArraySet<>(); - - /** - * If set to {@code false} all calls to resize the docked stack {@link #resizeDockedStackLocked} - * will be ignored. Useful for the case where the caller is handling resizing of other stack and - * moving tasks around and doesn't want dock stack to be resized due to an automatic trigger - * like the docked stack going empty. - */ - private boolean mAllowDockedStackResize = true; - private KeyguardController mKeyguardController; private PowerManager mPowerManager; @@ -1541,12 +1519,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } otherStack.setWindowingMode(WINDOWING_MODE_UNDEFINED); } - - // Also disable docked stack resizing since we have manually adjusted the - // size of other stacks above and we don't want to trigger a docked stack - // resize when we remove task from it below and it is detached from the - // display because it no longer contains any tasks. - mAllowDockedStackResize = false; } // If we are moving from the pinned stack, then the animation takes care of updating @@ -1563,7 +1535,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); mRootWindowContainer.resumeFocusedStacksTopActivities(); } finally { - mAllowDockedStackResize = true; mService.continueWindowLayout(); } } @@ -1580,154 +1551,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { mDockedStackResizing = resizing; mWindowManager.setDockedStackResizing(resizing); - - if (!resizing && mHasPendingDockedBounds) { - resizeDockedStackLocked(mPendingDockedBounds, mPendingTempDockedTaskBounds, - mPendingTempDockedTaskInsetBounds, mPendingTempOtherTaskBounds, - mPendingTempOtherTaskInsetBounds, PRESERVE_WINDOWS); - - mHasPendingDockedBounds = false; - mPendingDockedBounds = null; - mPendingTempDockedTaskBounds = null; - mPendingTempDockedTaskInsetBounds = null; - mPendingTempOtherTaskBounds = null; - mPendingTempOtherTaskInsetBounds = null; - } - } - - void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds, - Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds, - boolean preserveWindows) { - resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds, tempDockedTaskInsetBounds, - tempOtherTaskBounds, tempOtherTaskInsetBounds, preserveWindows, - false /* deferResume */); - } - - void resizeDockedStackLocked(Rect displayedBounds, Rect tempDockedTaskBounds, - Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds, - boolean preserveWindows, boolean deferResume) { - - if (!mAllowDockedStackResize) { - // Docked stack resize currently disabled. - return; - } - - final ActivityStack stack = - mRootWindowContainer.getDefaultDisplay().getRootSplitScreenPrimaryTask(); - if (stack == null) { - Slog.w(TAG, "resizeDockedStackLocked: docked stack not found"); - return; - } - - if (mDockedStackResizing) { - mHasPendingDockedBounds = true; - mPendingDockedBounds = copyOrNull(displayedBounds); - mPendingTempDockedTaskBounds = copyOrNull(tempDockedTaskBounds); - mPendingTempDockedTaskInsetBounds = copyOrNull(tempDockedTaskInsetBounds); - mPendingTempOtherTaskBounds = copyOrNull(tempOtherTaskBounds); - mPendingTempOtherTaskInsetBounds = copyOrNull(tempOtherTaskInsetBounds); - } - - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizeDockedStack"); - mService.deferWindowLayout(); - try { - // Don't allow re-entry while resizing. E.g. due to docked stack detaching. - mAllowDockedStackResize = false; - ActivityRecord r = stack.topRunningActivity(); - stack.resize(displayedBounds, tempDockedTaskBounds, - !PRESERVE_WINDOWS, DEFER_RESUME); - - // TODO: Checking for isAttached might not be needed as if the user passes in null - // dockedBounds then they want the docked stack to be dismissed. - if (stack.getWindowingMode() == WINDOWING_MODE_FULLSCREEN - || (displayedBounds == null && !stack.isAttached())) { - // The dock stack either was dismissed or went fullscreen, which is kinda the same. - // In this case we make all other static stacks fullscreen and move all - // docked stack tasks to the fullscreen stack. - moveTasksToFullscreenStackLocked(stack, ON_TOP); - - // stack shouldn't contain anymore activities, so nothing to resume. - r = null; - } else { - // Docked stacks occupy a dedicated region on screen so the size of all other - // static stacks need to be adjusted so they don't overlap with the docked stack. - // We get the bounds to use from window manager which has been adjusted for any - // screen controls and is also the same for all stacks. - final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); - final Rect otherTaskRect = new Rect(); - for (int i = display.getStackCount() - 1; i >= 0; --i) { - final ActivityStack current = display.getStackAt(i); - if (!current.inSplitScreenSecondaryWindowingMode()) { - continue; - } - if (!current.affectedBySplitScreenResize()) { - continue; - } - if (mDockedStackResizing && !current.isTopActivityVisible()) { - // Non-visible stacks get resized once we're done with the resize - // interaction. - continue; - } - current.getStackDockedModeBounds(displayedBounds, - tempOtherTaskBounds /* currentTempTaskBounds */, - tempRect /* outStackBounds */, - otherTaskRect /* outTempTaskBounds */); - - if (tempRect.isEmpty()) { - // If this scenario is hit, it means something is not working right. - // Empty/null bounds implies fullscreen. In the event that this stack - // *should* be fullscreen, its mode should be set explicitly in a form - // of setWindowingMode so that other parts of the system are updated - // properly. - throw new IllegalArgumentException("Trying to set null bounds on a" - + " non-fullscreen stack"); - } - - current.resize(tempRect, tempOtherTaskBounds, preserveWindows, deferResume); - } - } - if (!deferResume) { - stack.ensureVisibleActivitiesConfiguration(r, preserveWindows); - } - } finally { - mAllowDockedStackResize = true; - mService.continueWindowLayout(); - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - } - } - - void resizePinnedStack(Rect displayedBounds, Rect inConfigBounds) { - // TODO(multi-display): The display containing the stack should be passed in. - final ActivityStack stack = - mRootWindowContainer.getDefaultDisplay().getRootPinnedTask(); - if (stack == null) { - Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found"); - return; - } - - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizePinnedStack"); - mService.deferWindowLayout(); - try { - Rect configBounds = null; - if (inConfigBounds != null) { - // Use 0,0 as the position for the inset rect because we are headed for fullscreen. - configBounds = tempRect; - configBounds.top = 0; - configBounds.left = 0; - configBounds.right = inConfigBounds.width(); - configBounds.bottom = inConfigBounds.height(); - } - if (displayedBounds != null && inConfigBounds == null) { - // We have finished the animation into PiP, and are resizing the tasks to match the - // stack bounds, while layouts are deferred, update any task state as a part of - // transitioning it from fullscreen into a floating state. - stack.onPipAnimationEndResize(); - } - stack.resize(displayedBounds, configBounds, !PRESERVE_WINDOWS, !DEFER_RESUME); - } finally { - mService.continueWindowLayout(); - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - } } private void removeStackInSurfaceTransaction(ActivityStack stack) { @@ -2757,9 +2580,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { mService.deferWindowLayout(); try { if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - mWindowManager.setDockedStackCreateStateLocked( - activityOptions.getSplitScreenCreateMode(), null /* initialBounds */); - // Defer updating the stack in which recents is until the app transition is done, to // not run into issues where we still need to draw the task in recents but the // docked stack is already created. @@ -2835,24 +2655,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // the window renders full-screen with the background filling the void. Also only // call this at the end to make sure that tasks exists on the window manager side. setResizingDuringAnimation(task); - - final DisplayContent display = task.getStack().getDisplay(); - final ActivityStack topSecondaryStack = - display.getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); - if (topSecondaryStack != null && topSecondaryStack.isActivityTypeHome()) { - // If the home activity is the top split-screen secondary stack, then the - // primary split-screen stack is in the minimized mode which means it can't - // receive input keys, so we should move the focused app to the home app so that - // window manager can correctly calculate the focus window that can receive - // input keys. - display.moveHomeActivityToTop( - "startActivityFromRecents: homeVisibleInSplitScreen"); - - // Immediately update the minimized docked stack mode, the upcoming animation - // for the docked activity (WMS.overridePendingAppTransitionMultiThumbFuture) - // will do the animation to the target bounds - mWindowManager.checkSplitScreenMinimizedChanged(false /* animate */); - } } mService.continueWindowLayout(); } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 600a125cdf43..f52c7f29ac8a 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -97,7 +97,6 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; -import android.graphics.Rect; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -336,6 +335,7 @@ class ActivityStarter { int filterCallingUid; PendingIntentRecord originatingPendingIntent; boolean allowBackgroundActivityStart; + boolean isDream; /** * If set to {@code true}, allows this activity start to look into @@ -387,6 +387,7 @@ class ActivityStarter { filterCallingUid = UserHandle.USER_NULL; originatingPendingIntent = null; allowBackgroundActivityStart = false; + isDream = false; } /** @@ -427,6 +428,7 @@ class ActivityStarter { filterCallingUid = request.filterCallingUid; originatingPendingIntent = request.originatingPendingIntent; allowBackgroundActivityStart = request.allowBackgroundActivityStart; + isDream = request.isDream; } /** @@ -970,7 +972,7 @@ class ActivityStarter { restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, realCallingUid, realCallingPid, callerApp, request.originatingPendingIntent, request.allowBackgroundActivityStart, - intent); + request.isDream, intent); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } @@ -1180,13 +1182,18 @@ class ActivityStarter { boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid, final String callingPackage, int realCallingUid, int realCallingPid, WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent, - boolean allowBackgroundActivityStart, Intent intent) { + boolean allowBackgroundActivityStart, boolean isDream, Intent intent) { // don't abort for the most important UIDs final int callingAppId = UserHandle.getAppId(callingUid); if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID || callingAppId == Process.NFC_UID) { return false; } + + // don't abort if this is the dream activity + if (isDream) { + return false; + } // don't abort if the callingUid has a visible window or is a persistent system process final int callingUidProcState = mService.getUidState(callingUid); final boolean callingUidHasAnyVisibleWindow = @@ -1558,9 +1565,8 @@ class ActivityStarter { mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.mUserId); mService.getPackageManagerInternalLocked().grantImplicitAccess( mStartActivity.mUserId, mIntent, - mCallingUid, - UserHandle.getAppId(mStartActivity.info.applicationInfo.uid) - ); + UserHandle.getAppId(mStartActivity.info.applicationInfo.uid), mCallingUid, + true /*direct*/); if (newTask) { EventLogTags.writeWmCreateTask(mStartActivity.mUserId, mStartActivity.getTask().mTaskId); @@ -2401,7 +2407,6 @@ class ActivityStarter { mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession, mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions); addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask"); - updateBounds(mStartActivity.getTask(), mLaunchParams.mBounds); if (DEBUG_TASKS) { Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity @@ -2424,21 +2429,6 @@ class ActivityStarter { mIntentDelivered = true; } - @VisibleForTesting - void updateBounds(Task task, Rect bounds) { - if (bounds.isEmpty()) { - return; - } - - final Task rootTask = task.getRootTask(); - if (rootTask != null && rootTask.inPinnedWindowingMode()) { - mService.animateResizePinnedStack(rootTask.mTaskId, bounds, -1); - } else { - // TODO: I don't believe it is possible to reach this else condition anymore... - task.setBounds(bounds); - } - } - private void addOrReparentStartingActivity(Task parent, String reason) { if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) { parent.addChild(mStartActivity); @@ -2686,6 +2676,11 @@ class ActivityStarter { return this; } + ActivityStarter setIsDream(boolean isDream) { + mRequest.isDream = isDream; + return this; + } + void dump(PrintWriter pw, String prefix) { prefix = prefix + " "; pw.print(prefix); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 510072d3831b..ca856ca7c1e6 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -169,12 +169,6 @@ public abstract class ActivityTaskManagerInternal { public abstract List<IBinder> getTopVisibleActivities(); /** - * Callback for window manager to let activity manager know that docked stack changes its - * minimized state. - */ - public abstract void notifyDockedStackMinimizedChanged(boolean minimized); - - /** * Notify listeners that contents are drawn for the first time on a single task display. * * @param displayId An ID of the display on which contents are drawn. diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 5392257ae0b0..770dabf4f5ca 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -31,12 +31,12 @@ import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW; -import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; @@ -212,6 +212,8 @@ import android.os.WorkSource; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; import android.provider.Settings; +import android.service.dreams.DreamActivity; +import android.service.dreams.DreamManagerInternal; import android.service.voice.IVoiceInteractionSession; import android.service.voice.VoiceInteractionManagerInternal; import android.sysprop.DisplayProperties; @@ -1231,6 +1233,52 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override + public boolean startDreamActivity(Intent intent) { + final WindowProcessController process = mProcessMap.getProcess(Binder.getCallingPid()); + final long origId = Binder.clearCallingIdentity(); + + // The dream activity is only called for non-doze dreams. + final ComponentName currentDream = LocalServices.getService(DreamManagerInternal.class) + .getActiveDreamComponent(/* doze= */ false); + + if (currentDream == null || currentDream.getPackageName() == null + || !currentDream.getPackageName().equals(process.mInfo.packageName)) { + Slog.e(TAG, "Calling package is not the current dream package. " + + "Aborting startDreamActivity..."); + return false; + } + + final ActivityInfo a = new ActivityInfo(); + a.theme = com.android.internal.R.style.Theme_Dream; + a.exported = true; + a.name = DreamActivity.class.getName(); + + + a.packageName = process.mInfo.packageName; + a.applicationInfo = process.mInfo; + a.processName = process.mInfo.processName; + a.uiOptions = process.mInfo.uiOptions; + a.taskAffinity = "android:" + a.packageName + "/dream"; + a.enabled = true; + a.launchMode = ActivityInfo.LAUNCH_SINGLE_INSTANCE; + + a.persistableMode = ActivityInfo.PERSIST_NEVER; + a.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + a.colorMode = ActivityInfo.COLOR_MODE_DEFAULT; + a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS; + + try { + getActivityStartController().obtainStarter(intent, "dream") + .setActivityInfo(a) + .setIsDream(true) + .execute(); + return true; + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage, String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, @@ -2261,9 +2309,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) { if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - return setTaskWindowingModeSplitScreenPrimary(taskId, - SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, - toTop, ANIMATE, null /* initialBounds */, true /* showRecents */); + return setTaskWindowingModeSplitScreenPrimary(taskId, toTop); } enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()"); synchronized (mGlobalLock) { @@ -2403,7 +2449,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final ActivityStarter starter = getActivityStartController().obtainStarter( null /* intent */, "moveTaskToFront"); if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, -1, - -1, callerApp, null, false, null)) { + -1, callerApp, null, false, false, null)) { if (!isBackgroundActivityStartsEnabled()) { return; } @@ -2633,10 +2679,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { throw new IllegalArgumentException("moveTaskToStack: Attempt to move task " + taskId + " to stack " + stackId); } - if (stack.inSplitScreenPrimaryWindowingMode()) { - mWindowManager.setDockedStackCreateStateLocked( - SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */); - } task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, "moveTaskToStack"); } finally { @@ -2645,84 +2687,15 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - @Override - public void animateResizePinnedStack(int stackId, Rect destBounds, int animationDuration) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "animateResizePinnedStack()"); - - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - final ActivityStack stack = mRootWindowContainer.getStack(stackId); - if (stack == null) { - Slog.w(TAG, "resizeStack: stackId " + stackId + " not found."); - return; - } - if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) { - throw new IllegalArgumentException("Stack: " + stackId - + " doesn't support animated resize."); - } - stack.animateResizePinnedStack(destBounds, null /* sourceHintBounds */, - animationDuration, false /* fromFullscreen */); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public void offsetPinnedStackBounds(int stackId, Rect compareBounds, int xOffset, int yOffset, - int animationDuration) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "offsetPinnedStackBounds()"); - - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - if (xOffset == 0 && yOffset == 0) { - return; - } - final ActivityStack stack = mRootWindowContainer.getStack(stackId); - if (stack == null) { - Slog.w(TAG, "offsetPinnedStackBounds: stackId " + stackId + " not found."); - return; - } - if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) { - throw new IllegalArgumentException("Stack: " + stackId - + " doesn't support animated resize."); - } - final Rect destBounds = new Rect(); - stack.getAnimationOrCurrentBounds(destBounds); - if (destBounds.isEmpty() || !destBounds.equals(compareBounds)) { - Slog.w(TAG, "The current stack bounds does not matched! It may be obsolete."); - return; - } - destBounds.offset(xOffset, yOffset); - stack.animateResizePinnedStack(destBounds, null /* sourceHintBounds */, - animationDuration, false /* fromFullscreen */); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } /** * Moves the specified task to the primary-split-screen stack. * * @param taskId Id of task to move. - * @param createMode The mode the primary split screen stack should be created in if it doesn't - * exist already. See - * {@link android.app.ActivityTaskManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT} - * and - * {@link android.app.ActivityTaskManager#SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT} * @param toTop If the task and stack should be moved to the top. - * @param animate Whether we should play an animation for the moving the task. - * @param initialBounds If the primary stack gets created, it will use these bounds for the - * stack. Pass {@code null} to use default bounds. - * @param showRecents If the recents activity should be shown on the other side of the task - * going into split-screen mode. * @return Whether the task was successfully put into splitscreen. */ @Override - public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, - boolean toTop, boolean animate, Rect initialBounds, boolean showRecents) { + public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, boolean toTop) { enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingModeSplitScreenPrimary()"); synchronized (mGlobalLock) { @@ -4016,17 +3989,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { throw new IllegalArgumentException("Stack: " + stack + " doesn't support animated resize."); } - /** - * TODO(b/146594635): Remove all PIP animation code from WM - * once SysUI handles animation. Don't even try to animate TaskOrganized tasks. - */ - if (animate && !stack.isControlledByTaskOrganizer()) { - stack.animateResizePinnedStack(null /* destBounds */, - null /* sourceHintBounds */, animationDuration, - false /* fromFullscreen */); - } else { - stack.dismissPip(); - } + stack.dismissPip(); } } finally { Binder.restoreCallingIdentity(ident); @@ -4121,15 +4084,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } private boolean isInPictureInPictureMode(ActivityRecord r) { - if (r == null || r.getRootTask() == null || !r.inPinnedWindowingMode() - || r.getRootTask().isInStackLocked(r) == null) { - return false; - } - - // If we are animating to fullscreen then we have already dispatched the PIP mode - // changed, so we should reflect that check here as well. - final ActivityStack taskStack = r.getRootTask(); - return !taskStack.isAnimatingBoundsToFullscreen(); + return r != null + && r.getRootTask() != null + && r.inPinnedWindowingMode() + && r.getRootTask().isInStackLocked(r) != null; } @Override @@ -4213,11 +4171,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // if it is not already expanding to fullscreen. Otherwise, the arguments will // be used the next time the activity enters PiP final ActivityStack stack = r.getRootTask(); - if (!stack.isAnimatingBoundsToFullscreen()) { - stack.setPictureInPictureAspectRatio( - r.pictureInPictureArgs.getAspectRatio()); - stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions()); - } + stack.setPictureInPictureAspectRatio( + r.pictureInPictureArgs.getAspectRatio()); + stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions()); } logPictureInPictureArgs(params); } @@ -4300,6 +4256,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + // TODO(b/149338177): remove when CTS no-longer requires it @Override public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds, Rect tempDockedTaskInsetBounds, @@ -4308,9 +4265,42 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - mStackSupervisor.resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds, - tempDockedTaskInsetBounds, tempOtherTaskBounds, tempOtherTaskInsetBounds, - PRESERVE_WINDOWS); + final DisplayContent dc = mRootWindowContainer.getDefaultDisplay(); + TaskTile primary = null; + TaskTile secondary = null; + for (int i = dc.getStackCount() - 1; i >= 0; --i) { + final TaskTile t = dc.getStackAt(i).asTile(); + if (t == null) { + continue; + } + if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + primary = t; + } else if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) { + secondary = t; + } + } + if (primary == null || secondary == null) { + return; + } + final WindowContainerTransaction wct = new WindowContainerTransaction(); + final Rect primaryRect = + tempDockedTaskInsetBounds != null ? tempDockedTaskInsetBounds + : (tempDockedTaskBounds != null ? tempDockedTaskBounds + : dockedBounds); + wct.setBounds(primary.mRemoteToken, primaryRect); + Rect otherRect = tempOtherTaskInsetBounds != null ? tempOtherTaskInsetBounds + : tempOtherTaskBounds; + if (otherRect == null) { + // Temporary estimation... again this is just for tests. + otherRect = new Rect(secondary.getBounds()); + if (dc.getBounds().width() > dc.getBounds().height()) { + otherRect.left = primaryRect.right + 6; + } else { + otherRect.top = primaryRect.bottom + 6; + } + } + wct.setBounds(secondary.mRemoteToken, otherRect); + mTaskOrganizerController.applyContainerTransaction(wct, null /* organizer */); } } finally { Binder.restoreCallingIdentity(ident); @@ -4421,31 +4411,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { .supportsLocalVoiceInteraction(); } - /** Notifies all listeners when the pinned stack animation starts. */ - @Override - public void notifyPinnedStackAnimationStarted() { - mTaskChangeNotificationController.notifyPinnedStackAnimationStarted(); - } - - /** Notifies all listeners when the pinned stack animation ends. */ - @Override - public void notifyPinnedStackAnimationEnded() { - mTaskChangeNotificationController.notifyPinnedStackAnimationEnded(); - } - - @Override - public void resizePinnedStack(Rect displayedBounds, Rect configBounds) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()"); - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - mStackSupervisor.resizePinnedStack(displayedBounds, configBounds); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - @Override public boolean updateConfiguration(Configuration values) { mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()"); @@ -5920,8 +5885,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { * Return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} to resolve secondary home * activities. * - * @param preferredPackage Specify a preferred package name, otherwise use secondary home - * component defined in config_secondaryHomeComponent. + * @param preferredPackage Specify a preferred package name, otherwise use the package name + * defined in config_secondaryHomePackage. * @return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} */ Intent getSecondaryHomeIntent(String preferredPackage) { @@ -5929,10 +5894,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final boolean useSystemProvidedLauncher = mContext.getResources().getBoolean( com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary); if (preferredPackage == null || useSystemProvidedLauncher) { - // Using the component stored in config if no package name or forced. - final String secondaryHomeComponent = mContext.getResources().getString( - com.android.internal.R.string.config_secondaryHomeComponent); - intent.setComponent(ComponentName.unflattenFromString(secondaryHomeComponent)); + // Using the package name stored in config if no preferred package name or forced. + final String secondaryHomePackage = mContext.getResources().getString( + com.android.internal.R.string.config_secondaryHomePackage); + intent.setPackage(secondaryHomePackage); } else { intent.setPackage(preferredPackage); } @@ -6188,13 +6153,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void notifyDockedStackMinimizedChanged(boolean minimized) { - synchronized (mGlobalLock) { - mRootWindowContainer.setDockedStackMinimized(minimized); - } - } - - @Override public int startActivitiesAsPackage(String packageName, @Nullable String featureId, int userId, Intent[] intents, Bundle bOptions) { Objects.requireNonNull(intents, "intents"); diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index 8fa811915fc2..4cce212cb779 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -111,7 +111,7 @@ class AppTaskImpl extends IAppTask.Stub { final ActivityStarter starter = mService.getActivityStartController().obtainStarter( null /* intent */, "moveToFront"); if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, - callingPackage, -1, -1, callerApp, null, false, null)) { + callingPackage, -1, -1, callerApp, null, false, false, null)) { if (!mService.isBackgroundActivityStartsEnabled()) { return; } diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 8cf08814096e..4916c31f2878 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -446,8 +446,6 @@ public class AppTransition implements Dump { ? topOpeningAnim.getStatusBarTransitionsStartTime() : SystemClock.uptimeMillis(), AnimationAdapter.STATUS_BAR_TRANSITION_DURATION); - mDisplayContent.getDockedDividerController() - .notifyAppTransitionStarting(openingApps, transit); if (mRemoteAnimationController != null) { mRemoteAnimationController.goodToGo(); @@ -2308,14 +2306,14 @@ public class AppTransition implements Dump { } notifyAppTransitionTimeoutLocked(); if (isTransitionSet() || !dc.mOpeningApps.isEmpty() || !dc.mClosingApps.isEmpty() - || !dc.mChangingApps.isEmpty()) { + || !dc.mChangingContainers.isEmpty()) { ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "*** APP TRANSITION TIMEOUT. displayId=%d isTransitionSet()=%b " + "mOpeningApps.size()=%d mClosingApps.size()=%d " + "mChangingApps.size()=%d", dc.getDisplayId(), dc.mAppTransition.isTransitionSet(), dc.mOpeningApps.size(), dc.mClosingApps.size(), - dc.mChangingApps.size()); + dc.mChangingContainers.size()); setTimeout(); mService.mWindowPlacerLocked.performSurfacePlacement(); diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 3f4e79162ed9..0912b2e8b52f 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -55,6 +55,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACT import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.annotation.NonNull; import android.os.Trace; import android.util.ArrayMap; import android.util.ArraySet; @@ -86,7 +87,7 @@ public class AppTransitionController { private final WallpaperController mWallpaperControllerLocked; private RemoteAnimationDefinition mRemoteAnimationDefinition = null; - private final ArrayMap<ActivityRecord, Integer> mTempTransitionReasons = new ArrayMap<>(); + private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>(); AppTransitionController(WindowManagerService service, DisplayContent displayContent) { mService = service; @@ -104,7 +105,8 @@ public class AppTransitionController { void handleAppTransitionReady() { mTempTransitionReasons.clear(); if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons) - || !transitionGoodToGo(mDisplayContent.mChangingApps, mTempTransitionReasons)) { + || !transitionGoodToGo(mDisplayContent.mChangingContainers, + mTempTransitionReasons)) { return; } Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady"); @@ -130,17 +132,21 @@ public class AppTransitionController { // transition selection depends on wallpaper target visibility. mDisplayContent.mOpeningApps.valueAtUnchecked(i).clearAnimatingFlags(); } - appCount = mDisplayContent.mChangingApps.size(); + appCount = mDisplayContent.mChangingContainers.size(); for (int i = 0; i < appCount; ++i) { // Clearing for same reason as above. - mDisplayContent.mChangingApps.valueAtUnchecked(i).clearAnimatingFlags(); + final ActivityRecord activity = getAppFromContainer( + mDisplayContent.mChangingContainers.valueAtUnchecked(i)); + if (activity != null) { + activity.clearAnimatingFlags(); + } } // Adjust wallpaper before we pull the lower/upper target, since pending changes // (like the clearAnimatingFlags() above) might affect wallpaper target result. // Or, the opening app window should be a wallpaper target. mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded( - mDisplayContent.mOpeningApps, mDisplayContent.mChangingApps); + mDisplayContent.mOpeningApps); // Determine if closing and opening app token sets are wallpaper targets, in which case // special animations are needed. @@ -159,7 +165,7 @@ public class AppTransitionController { // no need to do an animation. This is the case, for example, when this transition is being // done behind a dream window. final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps, - mDisplayContent.mClosingApps, mDisplayContent.mChangingApps); + mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers); final boolean allowAnimations = mDisplayContent.getDisplayPolicy().allowAppAnimationsLw(); final ActivityRecord animLpActivity = allowAnimations ? findAnimLayoutParamsToken(transit, activityTypes) @@ -171,14 +177,13 @@ public class AppTransitionController { ? getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */) : null; final ActivityRecord topChangingApp = allowAnimations - ? getTopApp(mDisplayContent.mChangingApps, false /* ignoreHidden */) + ? getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */) : null; final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity); overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes); final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps) - || containsVoiceInteraction(mDisplayContent.mOpeningApps) - || containsVoiceInteraction(mDisplayContent.mChangingApps); + || containsVoiceInteraction(mDisplayContent.mOpeningApps); final int layoutRedo; mService.mSurfaceAnimationRunner.deferStartingAnimations(); @@ -206,7 +211,7 @@ public class AppTransitionController { mDisplayContent.mOpeningApps.clear(); mDisplayContent.mClosingApps.clear(); - mDisplayContent.mChangingApps.clear(); + mDisplayContent.mChangingContainers.clear(); mDisplayContent.mUnknownAppVisibilityController.clear(); // This has changed the visibility of windows, so perform @@ -235,9 +240,9 @@ public class AppTransitionController { return mainWindow != null ? mainWindow.mAttrs : null; } - RemoteAnimationAdapter getRemoteAnimationOverride(ActivityRecord animLpActivity, + RemoteAnimationAdapter getRemoteAnimationOverride(@NonNull WindowContainer container, @TransitionType int transit, ArraySet<Integer> activityTypes) { - final RemoteAnimationDefinition definition = animLpActivity.getRemoteAnimationDefinition(); + final RemoteAnimationDefinition definition = container.getRemoteAnimationDefinition(); if (definition != null) { final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes); if (adapter != null) { @@ -271,6 +276,11 @@ public class AppTransitionController { } } + static ActivityRecord getAppFromContainer(WindowContainer wc) { + return wc.asTask() != null ? wc.asTask().getTopNonFinishingActivity() + : wc.asActivityRecord(); + } + /** * @return The window token that determines the animation theme. */ @@ -279,14 +289,14 @@ public class AppTransitionController { ActivityRecord result; final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps; final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps; - final ArraySet<ActivityRecord> changingApps = mDisplayContent.mChangingApps; + final ArraySet<WindowContainer> changingApps = mDisplayContent.mChangingContainers; // Remote animations always win, but fullscreen tokens override non-fullscreen tokens. result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps, w -> w.getRemoteAnimationDefinition() != null && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes)); if (result != null) { - return result; + return getAppFromContainer(result); } result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps, w -> w.fillsParent() && w.findMainWindow() != null); @@ -302,7 +312,7 @@ public class AppTransitionController { * of apps in {@code array1}, {@code array2}, and {@code array3}. */ private static ArraySet<Integer> collectActivityTypes(ArraySet<ActivityRecord> array1, - ArraySet<ActivityRecord> array2, ArraySet<ActivityRecord> array3) { + ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3) { final ArraySet<Integer> result = new ArraySet<>(); for (int i = array1.size() - 1; i >= 0; i--) { result.add(array1.valueAt(i).getActivityType()); @@ -317,7 +327,7 @@ public class AppTransitionController { } private static ActivityRecord lookForHighestTokenWithFilter(ArraySet<ActivityRecord> array1, - ArraySet<ActivityRecord> array2, ArraySet<ActivityRecord> array3, + ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3, Predicate<ActivityRecord> filter) { final int array2base = array1.size(); final int array3base = array2.size() + array2base; @@ -325,15 +335,16 @@ public class AppTransitionController { int bestPrefixOrderIndex = Integer.MIN_VALUE; ActivityRecord bestToken = null; for (int i = 0; i < count; i++) { - final ActivityRecord wtoken = i < array2base + final WindowContainer wtoken = i < array2base ? array1.valueAt(i) : (i < array3base ? array2.valueAt(i - array2base) : array3.valueAt(i - array3base)); final int prefixOrderIndex = wtoken.getPrefixOrderIndex(); - if (filter.test(wtoken) && prefixOrderIndex > bestPrefixOrderIndex) { + final ActivityRecord r = getAppFromContainer(wtoken); + if (r != null && filter.test(r) && prefixOrderIndex > bestPrefixOrderIndex) { bestPrefixOrderIndex = prefixOrderIndex; - bestToken = wtoken; + bestToken = r; } } return bestToken; @@ -589,21 +600,13 @@ public class AppTransitionController { } private void handleChangingApps(@TransitionType int transit) { - final ArraySet<ActivityRecord> apps = mDisplayContent.mChangingApps; + final ArraySet<WindowContainer> apps = mDisplayContent.mChangingContainers; final int appsCount = apps.size(); for (int i = 0; i < appsCount; i++) { - ActivityRecord activity = apps.valueAt(i); - ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now changing app %s", activity); - activity.cancelAnimationOnly(); - activity.applyAnimation(null, transit, true, false, + WindowContainer wc = apps.valueAt(i); + ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now changing app %s", wc); + wc.applyAnimation(null, transit, true, false, null /* animationFinishedCallback */); - activity.updateReportedVisibilityLocked(); - mService.openSurfaceTransaction(); - try { - activity.showAllWindowsLocked(); - } finally { - mService.closeSurfaceTransaction("handleChangingApps"); - } } } @@ -628,8 +631,8 @@ public class AppTransitionController { } } - private boolean transitionGoodToGo(ArraySet<ActivityRecord> apps, - ArrayMap<ActivityRecord, Integer> outReasons) { + private boolean transitionGoodToGo(ArraySet<? extends WindowContainer> apps, + ArrayMap<WindowContainer, Integer> outReasons) { ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(), mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout()); @@ -652,13 +655,17 @@ public class AppTransitionController { return false; } for (int i = 0; i < apps.size(); i++) { - ActivityRecord activity = apps.valueAt(i); + WindowContainer wc = apps.valueAt(i); + final ActivityRecord activity = getAppFromContainer(wc); + if (activity == null) { + continue; + } ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, - "Check opening app=%s: allDrawn=%b startingDisplayed=%b " - + "startingMoved=%b isRelaunching()=%b startingWindow=%s", - activity, activity.allDrawn, activity.startingDisplayed, - activity.startingMoved, activity.isRelaunching(), - activity.startingWindow); + "Check opening app=%s: allDrawn=%b startingDisplayed=%b " + + "startingMoved=%b isRelaunching()=%b startingWindow=%s", + activity, activity.allDrawn, activity.startingDisplayed, + activity.startingMoved, activity.isRelaunching(), + activity.startingWindow); final boolean allDrawn = activity.allDrawn && !activity.isRelaunching(); @@ -838,7 +845,7 @@ public class AppTransitionController { @VisibleForTesting boolean isTransitWithinTask(@TransitionType int transit, Task task) { if (task == null - || !mDisplayContent.mChangingApps.isEmpty()) { + || !mDisplayContent.mChangingContainers.isEmpty()) { // if there is no task, then we can't constrain to the task. // if anything is changing, it can animate outside its task. return false; @@ -882,12 +889,13 @@ public class AppTransitionController { * {@link ActivityRecord#isVisible}. * @return The top {@link ActivityRecord}. */ - private ActivityRecord getTopApp(ArraySet<ActivityRecord> apps, boolean ignoreInvisible) { + private ActivityRecord getTopApp(ArraySet<? extends WindowContainer> apps, + boolean ignoreInvisible) { int topPrefixOrderIndex = Integer.MIN_VALUE; ActivityRecord topApp = null; for (int i = apps.size() - 1; i >= 0; i--) { - final ActivityRecord app = apps.valueAt(i); - if (ignoreInvisible && !app.isVisible()) { + final ActivityRecord app = getAppFromContainer(apps.valueAt(i)); + if (app == null || ignoreInvisible && !app.isVisible()) { continue; } final int prefixOrderIndex = app.getPrefixOrderIndex(); diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java deleted file mode 100644 index 5385e2fce008..000000000000 --- a/services/core/java/com/android/server/wm/BoundsAnimationController.java +++ /dev/null @@ -1,584 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; - -import android.animation.AnimationHandler; -import android.animation.Animator; -import android.animation.ValueAnimator; -import android.annotation.IntDef; -import android.content.Context; -import android.graphics.Rect; -import android.os.Debug; -import android.os.Handler; -import android.os.IBinder; -import android.util.ArrayMap; -import android.util.Slog; -import android.view.Choreographer; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.graphics.SfVsyncFrameCallbackProvider; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Enables animating bounds of objects. - * - * In multi-window world bounds of both stack and tasks can change. When we need these bounds to - * change smoothly and not require the app to relaunch (e.g. because it handles resizes and - * relaunching it would cause poorer experience), these class provides a way to directly animate - * the bounds of the resized object. - * - * The object that is resized needs to implement {@link BoundsAnimationTarget} interface. - * - * NOTE: All calls to methods in this class should be done on the Animation thread - */ -public class BoundsAnimationController { - private static final boolean DEBUG_LOCAL = false; - private static final boolean DEBUG = DEBUG_LOCAL || DEBUG_ANIM; - private static final String TAG = TAG_WITH_CLASS_NAME || DEBUG_LOCAL - ? "BoundsAnimationController" : TAG_WM; - private static final int DEBUG_ANIMATION_SLOW_DOWN_FACTOR = 1; - - private static final int DEFAULT_TRANSITION_DURATION = 425; - - @Retention(RetentionPolicy.SOURCE) - @IntDef({NO_PIP_MODE_CHANGED_CALLBACKS, SCHEDULE_PIP_MODE_CHANGED_ON_START, - SCHEDULE_PIP_MODE_CHANGED_ON_END}) - public @interface SchedulePipModeChangedState {} - /** Do not schedule any PiP mode changed callbacks as a part of this animation. */ - public static final int NO_PIP_MODE_CHANGED_CALLBACKS = 0; - /** Schedule a PiP mode changed callback when this animation starts. */ - public static final int SCHEDULE_PIP_MODE_CHANGED_ON_START = 1; - /** Schedule a PiP mode changed callback when this animation ends. */ - public static final int SCHEDULE_PIP_MODE_CHANGED_ON_END = 2; - - public static final int BOUNDS = 0; - public static final int FADE_IN = 1; - - @IntDef({BOUNDS, FADE_IN}) public @interface AnimationType {} - - private static final int FADE_IN_DURATION = 500; - - // Only accessed on UI thread. - private ArrayMap<BoundsAnimationTarget, BoundsAnimator> mRunningAnimations = new ArrayMap<>(); - - private final class AppTransitionNotifier - extends WindowManagerInternal.AppTransitionListener implements Runnable { - - public void onAppTransitionCancelledLocked() { - if (DEBUG) Slog.d(TAG, "onAppTransitionCancelledLocked:" - + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition); - animationFinished(); - } - public void onAppTransitionFinishedLocked(IBinder token) { - if (DEBUG) Slog.d(TAG, "onAppTransitionFinishedLocked:" - + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition); - animationFinished(); - } - private void animationFinished() { - if (mFinishAnimationAfterTransition) { - mHandler.removeCallbacks(this); - // This might end up calling into activity manager which will be bad since we have - // the window manager lock held at this point. Post a message to take care of the - // processing so we don't deadlock. - mHandler.post(this); - } - } - - @Override - public void run() { - for (int i = 0; i < mRunningAnimations.size(); i++) { - final BoundsAnimator b = mRunningAnimations.valueAt(i); - b.onAnimationEnd(null); - } - } - } - - private final Handler mHandler; - private final AppTransition mAppTransition; - private final AppTransitionNotifier mAppTransitionNotifier = new AppTransitionNotifier(); - private final Interpolator mFastOutSlowInInterpolator; - private boolean mFinishAnimationAfterTransition = false; - private final AnimationHandler mAnimationHandler; - private Choreographer mChoreographer; - private @AnimationType int mAnimationType; - - private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000; - - BoundsAnimationController(Context context, AppTransition transition, Handler handler, - AnimationHandler animationHandler) { - mHandler = handler; - mAppTransition = transition; - mAppTransition.registerListenerLocked(mAppTransitionNotifier); - mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, - com.android.internal.R.interpolator.fast_out_slow_in); - mAnimationHandler = animationHandler; - if (animationHandler != null) { - // If an animation handler is provided, then ensure that it runs on the sf vsync tick - handler.post(() -> { - mChoreographer = Choreographer.getSfInstance(); - animationHandler.setProvider(new SfVsyncFrameCallbackProvider(mChoreographer)); - }); - } - } - - @VisibleForTesting - final class BoundsAnimator extends ValueAnimator - implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener { - - private final BoundsAnimationTarget mTarget; - private final @AnimationType int mAnimationType; - private final Rect mFrom = new Rect(); - private final Rect mTo = new Rect(); - private final Rect mTmpRect = new Rect(); - private final Rect mTmpTaskBounds = new Rect(); - - // True if this this animation was canceled and will be replaced the another animation from - // the same {@link #BoundsAnimationTarget} target. - private boolean mSkipFinalResize; - // True if this animation was canceled by the user, not as a part of a replacing animation - private boolean mSkipAnimationEnd; - - // True if the animation target is animating from the fullscreen. Only one of - // {@link mMoveToFullscreen} or {@link mMoveFromFullscreen} can be true at any time in the - // animation. - private boolean mMoveFromFullscreen; - // True if the animation target should be moved to the fullscreen stack at the end of this - // animation. Only one of {@link mMoveToFullscreen} or {@link mMoveFromFullscreen} can be - // true at any time in the animation. - private boolean mMoveToFullscreen; - - // Whether to schedule PiP mode changes on animation start/end - private @SchedulePipModeChangedState int mSchedulePipModeChangedState; - private @SchedulePipModeChangedState int mPrevSchedulePipModeChangedState; - - // Depending on whether we are animating from - // a smaller to a larger size - private int mFrozenTaskWidth; - private int mFrozenTaskHeight; - - // Timeout callback to ensure we continue the animation if waiting for resuming or app - // windows drawn fails - private final Runnable mResumeRunnable = () -> { - if (DEBUG) Slog.d(TAG, "pause: timed out waiting for windows drawn"); - resume(); - }; - - // If this animator is explicitly cancelled when it's in paused state, we should not - // attempt to resume the animation. Use this flag to avoid such behavior. - private boolean mIsCancelled; - - BoundsAnimator(BoundsAnimationTarget target, @AnimationType int animationType, Rect from, - Rect to, @SchedulePipModeChangedState int schedulePipModeChangedState, - @SchedulePipModeChangedState int prevShedulePipModeChangedState, - boolean moveFromFullscreen, boolean moveToFullscreen, Rect frozenTask) { - super(); - mTarget = target; - mAnimationType = animationType; - mFrom.set(from); - mTo.set(to); - mSchedulePipModeChangedState = schedulePipModeChangedState; - mPrevSchedulePipModeChangedState = prevShedulePipModeChangedState; - mMoveFromFullscreen = moveFromFullscreen; - mMoveToFullscreen = moveToFullscreen; - addUpdateListener(this); - addListener(this); - - // If we are animating from smaller to larger, we want to change the task bounds - // to their final size immediately so we can use scaling to make the window - // larger. Likewise if we are going from bigger to smaller, we want to wait until - // the end so we don't have to upscale from the smaller finished size. - if (mAnimationType == BOUNDS) { - if (animatingToLargerSize()) { - mFrozenTaskWidth = mTo.width(); - mFrozenTaskHeight = mTo.height(); - } else { - mFrozenTaskWidth = frozenTask.isEmpty() ? mFrom.width() : frozenTask.width(); - mFrozenTaskHeight = frozenTask.isEmpty() ? mFrom.height() : frozenTask.height(); - } - } - } - - @Override - public void onAnimationStart(Animator animation) { - if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget - + " mPrevSchedulePipModeChangedState=" + mPrevSchedulePipModeChangedState - + " mSchedulePipModeChangedState=" + mSchedulePipModeChangedState); - mIsCancelled = false; - mFinishAnimationAfterTransition = false; - mTmpRect.set(mFrom.left, mFrom.top, mFrom.left + mFrozenTaskWidth, - mFrom.top + mFrozenTaskHeight); - - // Boost the thread priority of the animation thread while the bounds animation is - // running - updateBooster(); - - // Ensure that we have prepared the target for animation before we trigger any size - // changes, so it can swap surfaces in to appropriate modes, or do as it wishes - // otherwise. - boolean continueAnimation; - if (mPrevSchedulePipModeChangedState == NO_PIP_MODE_CHANGED_CALLBACKS) { - continueAnimation = mTarget.onAnimationStart( - mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START, - false /* forceUpdate */, mAnimationType); - - // When starting an animation from fullscreen, pause here and wait for the - // windows-drawn signal before we start the rest of the transition down into PiP. - if (continueAnimation && mMoveFromFullscreen - && mTarget.shouldDeferStartOnMoveToFullscreen()) { - pause(); - } - } else if (mPrevSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END && - mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { - // We are replacing a running animation into PiP, but since it hasn't completed, the - // client will not currently receive any picture-in-picture mode change callbacks. - // However, we still need to report to them that they are leaving PiP, so this will - // force an update via a mode changed callback. - continueAnimation = mTarget.onAnimationStart( - true /* schedulePipModeChangedCallback */, true /* forceUpdate */, - mAnimationType); - } else { - // The animation is already running, but we should check that the TaskStack is still - // valid before continuing with the animation - continueAnimation = mTarget.isAttached(); - } - - if (!continueAnimation) { - // No point of trying to animate something that isn't attached to the hierarchy - // anymore. - cancel(); - return; - } - - // Immediately update the task bounds if they have to become larger, but preserve - // the starting position so we don't jump at the beginning of the animation. - if (animatingToLargerSize()) { - mTarget.setPinnedStackSize(mFrom, mTmpRect); - - // We pause the animation until the app has drawn at the new size. - // The target will notify us via BoundsAnimationController#resume. - // We do this here and pause the animation, rather than just defer starting it - // so we can enter the animating state and have WindowStateAnimator apply the - // correct logic to make this resize seamless. - if (mMoveToFullscreen) { - pause(); - } - } - } - - @Override - public void pause() { - if (DEBUG) Slog.d(TAG, "pause: waiting for windows drawn"); - super.pause(); - mHandler.postDelayed(mResumeRunnable, WAIT_FOR_DRAW_TIMEOUT_MS); - } - - @Override - public void resume() { - if (DEBUG) Slog.d(TAG, "resume:"); - mHandler.removeCallbacks(mResumeRunnable); - if (!mIsCancelled) super.resume(); - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - final float value = (Float) animation.getAnimatedValue(); - if (mAnimationType == FADE_IN) { - if (!mTarget.setPinnedStackAlpha(value)) { - cancelAndCallAnimationEnd(); - } - return; - } - - final float remains = 1 - value; - mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value + 0.5f); - mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value + 0.5f); - mTmpRect.right = (int) (mFrom.right * remains + mTo.right * value + 0.5f); - mTmpRect.bottom = (int) (mFrom.bottom * remains + mTo.bottom * value + 0.5f); - if (DEBUG) Slog.d(TAG, "animateUpdate: mTarget=" + mTarget + " mBounds=" - + mTmpRect + " from=" + mFrom + " mTo=" + mTo + " value=" + value - + " remains=" + remains); - - mTmpTaskBounds.set(mTmpRect.left, mTmpRect.top, - mTmpRect.left + mFrozenTaskWidth, mTmpRect.top + mFrozenTaskHeight); - - if (!mTarget.setPinnedStackSize(mTmpRect, mTmpTaskBounds)) { - // Whoops, the target doesn't feel like animating anymore. Let's immediately finish - // any further animation. - if (DEBUG) Slog.d(TAG, "animateUpdate: cancelled"); - - // If we have already scheduled a PiP mode changed at the start of the animation, - // then we need to clean up and schedule one at the end, since we have canceled the - // animation to the final state. - if (mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { - mSchedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; - } - - // Since we are cancelling immediately without a replacement animation, send the - // animation end to maintain callback parity, but also skip any further resizes - cancelAndCallAnimationEnd(); - } - } - - @Override - public void onAnimationEnd(Animator animation) { - if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget - + " mSkipFinalResize=" + mSkipFinalResize - + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition - + " mAppTransitionIsRunning=" + mAppTransition.isRunning() - + " callers=" + Debug.getCallers(2)); - - // There could be another animation running. For example in the - // move to fullscreen case, recents will also be closing while the - // previous task will be taking its place in the fullscreen stack. - // we have to ensure this is completed before we finish the animation - // and take our place in the fullscreen stack. - if (mAppTransition.isRunning() && !mFinishAnimationAfterTransition) { - mFinishAnimationAfterTransition = true; - return; - } - - if (!mSkipAnimationEnd) { - // If this animation has already scheduled the picture-in-picture mode on start, and - // we are not skipping the final resize due to being canceled, then move the PiP to - // fullscreen once the animation ends - if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget - + " moveToFullscreen=" + mMoveToFullscreen); - mTarget.onAnimationEnd(mSchedulePipModeChangedState == - SCHEDULE_PIP_MODE_CHANGED_ON_END, !mSkipFinalResize ? mTo : null, - mMoveToFullscreen); - } - - // Clean up this animation - removeListener(this); - removeUpdateListener(this); - mRunningAnimations.remove(mTarget); - - // Reset the thread priority of the animation thread after the bounds animation is done - updateBooster(); - } - - @Override - public void onAnimationCancel(Animator animation) { - mIsCancelled = true; - // Always skip the final resize when the animation is canceled - mSkipFinalResize = true; - mMoveToFullscreen = false; - } - - private void cancelAndCallAnimationEnd() { - if (DEBUG) Slog.d(TAG, "cancelAndCallAnimationEnd: mTarget=" + mTarget); - mSkipAnimationEnd = false; - super.cancel(); - } - - @Override - public void cancel() { - if (DEBUG) Slog.d(TAG, "cancel: mTarget=" + mTarget); - mSkipAnimationEnd = true; - super.cancel(); - - // Reset the thread priority of the animation thread if the bounds animation is canceled - updateBooster(); - } - - /** - * @return true if the animation target is the same as the input bounds. - */ - boolean isAnimatingTo(Rect bounds) { - return mTo.equals(bounds); - } - - /** - * @return true if we are animating to a larger surface size - */ - @VisibleForTesting - boolean animatingToLargerSize() { - // TODO: Fix this check for aspect ratio changes - return (mFrom.width() * mFrom.height() < mTo.width() * mTo.height()); - } - - @Override - public void onAnimationRepeat(Animator animation) { - // Do nothing - } - - @Override - public AnimationHandler getAnimationHandler() { - if (mAnimationHandler != null) { - return mAnimationHandler; - } - return super.getAnimationHandler(); - } - } - - public void animateBounds(final BoundsAnimationTarget target, Rect from, Rect to, - int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState, - boolean moveFromFullscreen, boolean moveToFullscreen, - @AnimationType int animationType) { - animateBoundsImpl(target, from, to, animationDuration, schedulePipModeChangedState, - moveFromFullscreen, moveToFullscreen, animationType); - } - - /** - * Cancel existing animation if the destination was modified. - */ - void cancel(final BoundsAnimationTarget target) { - final BoundsAnimator existing = mRunningAnimations.get(target); - if (existing != null) { - // Cancel animation. Since its already started, send animation end to client. - if (DEBUG) Slog.d(TAG, "cancel: mTarget= " + target); - existing.cancelAndCallAnimationEnd(); - } - } - - @VisibleForTesting - BoundsAnimator animateBoundsImpl(final BoundsAnimationTarget target, Rect from, Rect to, - int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState, - boolean moveFromFullscreen, boolean moveToFullscreen, - @AnimationType int animationType) { - final BoundsAnimator existing = mRunningAnimations.get(target); - - if (isRunningFadeInAnimation(target) && from.width() == to.width() - && from.height() == to.height()) { - animationType = FADE_IN; - } - final boolean replacing = existing != null; - @SchedulePipModeChangedState int prevSchedulePipModeChangedState = - NO_PIP_MODE_CHANGED_CALLBACKS; - - if (DEBUG) Slog.d(TAG, "animateBounds: target=" + target + " from=" + from + " to=" + to - + " schedulePipModeChangedState=" + schedulePipModeChangedState - + " replacing=" + replacing); - - Rect frozenTask = new Rect(); - if (replacing) { - if (existing.isAnimatingTo(to) && (!moveToFullscreen || existing.mMoveToFullscreen) - && (!moveFromFullscreen || existing.mMoveFromFullscreen)) { - // Just let the current animation complete if it has the same destination as the - // one we are trying to start, and, if moveTo/FromFullscreen was requested, already - // has that flag set. - if (DEBUG) Slog.d(TAG, "animateBounds: same destination and moveTo/From flags as " - + "existing=" + existing + ", ignoring..."); - return existing; - } - - // Save the previous state - prevSchedulePipModeChangedState = existing.mSchedulePipModeChangedState; - - // Update the PiP callback states if we are replacing the animation - if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { - if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { - if (DEBUG) Slog.d(TAG, "animateBounds: still animating to fullscreen, keep" - + " existing deferred state"); - } else { - if (DEBUG) Slog.d(TAG, "animateBounds: fullscreen animation canceled, callback" - + " on start already processed, schedule deferred update on end"); - schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; - } - } else if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END) { - if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { - if (DEBUG) Slog.d(TAG, "animateBounds: non-fullscreen animation canceled," - + " callback on start will be processed"); - } else { - if (DEBUG) Slog.d(TAG, "animateBounds: still animating from fullscreen, keep" - + " existing deferred state"); - schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; - } - } - - // We need to keep the previous moveTo/FromFullscreen flag, unless the new animation - // specifies a direction. - if (!moveFromFullscreen && !moveToFullscreen) { - moveToFullscreen = existing.mMoveToFullscreen; - moveFromFullscreen = existing.mMoveFromFullscreen; - } - - // We are in the middle of an existing animation, so that this new animation may - // start from an interpolated bounds. We should keep using the existing frozen task - // width/height for consistent configurations. - frozenTask.set(0, 0, existing.mFrozenTaskWidth, existing.mFrozenTaskHeight); - - // Since we are replacing, we skip both animation start and end callbacks - existing.cancel(); - } - if (animationType == FADE_IN) { - target.setPinnedStackSize(to, null); - } - - final BoundsAnimator animator = new BoundsAnimator(target, animationType, from, to, - schedulePipModeChangedState, prevSchedulePipModeChangedState, - moveFromFullscreen, moveToFullscreen, frozenTask); - mRunningAnimations.put(target, animator); - animator.setFloatValues(0f, 1f); - animator.setDuration(animationType == FADE_IN ? FADE_IN_DURATION - : (animationDuration != -1 ? animationDuration : DEFAULT_TRANSITION_DURATION) - * DEBUG_ANIMATION_SLOW_DOWN_FACTOR); - animator.setInterpolator(mFastOutSlowInInterpolator); - animator.start(); - return animator; - } - - public void setAnimationType(@AnimationType int animationType) { - mAnimationType = animationType; - } - - /** return the current animation type. */ - public @AnimationType int getAnimationType() { - @AnimationType int animationType = mAnimationType; - // Default to BOUNDS. - mAnimationType = BOUNDS; - return animationType; - } - - boolean isAnimationTypeFadeIn() { - return mAnimationType == FADE_IN; - } - - public Handler getHandler() { - return mHandler; - } - - public void onAllWindowsDrawn() { - if (DEBUG) Slog.d(TAG, "onAllWindowsDrawn:"); - mHandler.post(this::resume); - } - - private boolean isRunningFadeInAnimation(final BoundsAnimationTarget target) { - final BoundsAnimator existing = mRunningAnimations.get(target); - return existing != null && existing.mAnimationType == FADE_IN && existing.isStarted(); - } - - private void resume() { - for (int i = 0; i < mRunningAnimations.size(); i++) { - final BoundsAnimator b = mRunningAnimations.valueAt(i); - b.resume(); - } - } - - private void updateBooster() { - WindowManagerService.sThreadPriorityBooster.setBoundsAnimationRunning( - !mRunningAnimations.isEmpty()); - } -} diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java deleted file mode 100644 index b1d535904fab..000000000000 --- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import android.graphics.Rect; - -/** - * The target for a BoundsAnimation. - * @see BoundsAnimationController - */ -interface BoundsAnimationTarget { - - /** - * Callback for the target to inform it that the animation has started, so it can do some - * necessary preparation. - * - * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed - * callbacks - * @return whether to continue the animation - */ - boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate, - @BoundsAnimationController.AnimationType int animationType); - - /** - * @return Whether the animation should be paused waiting for the windows to draw before - * entering PiP. - */ - boolean shouldDeferStartOnMoveToFullscreen(); - - /** - * Sets the size of the target (without any intermediate steps, like scheduling animation) - * but freezes the bounds of any tasks in the target at taskBounds, to allow for more - * flexibility during resizing. Only works for the pinned stack at the moment. This will - * only be called between onAnimationStart() and onAnimationEnd(). - * - * @return Whether the target should continue to be animated and this call was successful. - * If false, the animation will be cancelled because the user has determined that the - * animation is now invalid and not required. In such a case, the cancel will trigger the - * animation end callback as well, but will not send any further size changes. - */ - boolean setPinnedStackSize(Rect displayedBounds, Rect configBounds); - - /** Sets the alpha of the animation target */ - boolean setPinnedStackAlpha(float alpha); - - /** - * Callback for the target to inform it that the animation has ended, so it can do some - * necessary cleanup. - * - * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed - * callbacks - * @param finalStackSize the final stack bounds to set on the target (can be to indicate that - * the animation was cancelled and the target does not need to update to the final stack bounds) - * @param moveToFullscreen whether or the target should reparent itself to the fullscreen stack - * when the animation completes - */ - void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize, - boolean moveToFullscreen); - - /** @return True if the target is attached to the window hierarchy. */ - default boolean isAttached() { - return true; - } -} diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 0029dc8c3c25..5cd29301c733 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -91,11 +91,9 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK; import static com.android.server.wm.DisplayContentProto.APP_TRANSITION; -import static com.android.server.wm.DisplayContentProto.CHANGING_APPS; import static com.android.server.wm.DisplayContentProto.CLOSING_APPS; import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES; import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO; -import static com.android.server.wm.DisplayContentProto.DOCKED_STACK_DIVIDER_CONTROLLER; import static com.android.server.wm.DisplayContentProto.DPI; import static com.android.server.wm.DisplayContentProto.FOCUSED_APP; import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID; @@ -132,7 +130,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.H.REPORT_FOCUS_CHANGE; import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE; import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS; -import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER; +import static com.android.server.wm.WindowManagerService.H.UPDATE_MULTI_WINDOW_STACKS; import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT; import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD; import static com.android.server.wm.WindowManagerService.SEAMLESS_ROTATION_TIMEOUT_DURATION; @@ -150,7 +148,6 @@ import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW; import static com.android.server.wm.utils.RegionUtils.forEachRectReverse; import static com.android.server.wm.utils.RegionUtils.rectListToRegion; -import android.animation.AnimationHandler; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -315,9 +312,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final ArraySet<ActivityRecord> mOpeningApps = new ArraySet<>(); final ArraySet<ActivityRecord> mClosingApps = new ArraySet<>(); - final ArraySet<ActivityRecord> mChangingApps = new ArraySet<>(); + final ArraySet<WindowContainer> mChangingContainers = new ArraySet<>(); final UnknownAppVisibilityController mUnknownAppVisibilityController; - BoundsAnimationController mBoundsAnimationController; private MetricsLogger mMetricsLogger; @@ -977,10 +973,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mAppTransitionController = new AppTransitionController(mWmService, this); mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this); - AnimationHandler animationHandler = new AnimationHandler(); - mBoundsAnimationController = new BoundsAnimationController(mWmService.mContext, - mAppTransition, mWmService.mAnimationHandler, animationHandler); - final InputChannel inputChannel = mWmService.mInputManager.monitorInput( "PointerEventDispatcher" + mDisplayId, mDisplayId); mPointerEventDispatcher = new PointerEventDispatcher(inputChannel); @@ -1012,7 +1004,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mDisplayPolicy.systemReady(); } mWindowCornerRadius = mDisplayPolicy.getWindowCornerRadius(); - mDividerControllerLocked = new DockedStackDividerController(mWmService, this); + mDividerControllerLocked = new DockedStackDividerController(this); mPinnedStackControllerLocked = new PinnedStackController(mWmService, this); final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession) @@ -2239,12 +2231,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * for bounds calculations. */ void preOnConfigurationChanged() { - final DockedStackDividerController dividerController = getDockedDividerController(); - - if (dividerController != null) { - getDockedDividerController().onConfigurationChanged(); - } - final PinnedStackController pinnedStackController = getPinnedStackController(); if (pinnedStackController != null) { @@ -2628,10 +2614,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // We also remove the outside touch area for resizing for all freeform // tasks (including the focused). // We save the focused task region once we find it, and add it back at the end. - // If the task is home stack and it is resizable in the minimized state, we want to - // exclude the docked stack from touch so we need the entire screen area and not just a + // If the task is home stack and it is resizable and visible (top of its root task), we want + // to exclude the docked stack from touch so we need the entire screen area and not just a // small portion which the home stack currently is resized to. - if (task.isActivityTypeHome() && task.getStack().isMinimizedDockAndHomeStackResizable()) { + if (task.isActivityTypeHome() && task.isVisible() && task.getStack().getTile() != null + && task.isResizeable()) { mDisplayContent.getBounds(mTmpRect); } else { task.getDimBounds(mTmpRect); @@ -2640,6 +2627,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (task == focusedTask) { // Add the focused task rect back into the exclude region once we are done // processing stacks. + // NOTE: this *looks* like a no-op, but this usage of mTmpRect2 is expected by + // updateTouchExcludeRegion. mTmpRect2.set(mTmpRect); } @@ -2701,7 +2690,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // Clear all transitions & screen frozen states when removing display. mOpeningApps.clear(); mClosingApps.clear(); - mChangingApps.clear(); + mChangingContainers.clear(); mUnknownAppVisibilityController.clear(); mAppTransition.removeAppTransitionTimeoutCallbacks(); handleAnimatingStoppedAndTransition(); @@ -2718,6 +2707,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mRemovingDisplay = false; } + // Apply the pending transaction here since we may not be able to reach the DisplayContent + // on the next traversal if it's removed from RootWindowContainer child list. + getPendingTransaction().apply(); mWmService.mWindowPlacerLocked.requestTraversal(); } @@ -2750,58 +2742,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mDeferredRemoval; } - boolean animateForIme(float interpolatedValue, float animationTarget, - float dividerAnimationTarget) { - boolean updated = false; - - for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) { - final ActivityStack stack = mTaskContainers.getChildAt(i); - if (stack == null || !stack.isAdjustedForIme()) { - continue; - } - - if (interpolatedValue >= 1f && animationTarget == 0f && dividerAnimationTarget == 0f) { - stack.resetAdjustedForIme(true /* adjustBoundsNow */); - updated = true; - } else { - mDividerControllerLocked.mLastAnimationProgress = - mDividerControllerLocked.getInterpolatedAnimationValue(interpolatedValue); - mDividerControllerLocked.mLastDividerProgress = - mDividerControllerLocked.getInterpolatedDividerValue(interpolatedValue); - updated |= stack.updateAdjustForIme( - mDividerControllerLocked.mLastAnimationProgress, - mDividerControllerLocked.mLastDividerProgress, - false /* force */); - } - if (interpolatedValue >= 1f) { - stack.endImeAdjustAnimation(); - } - } - - return updated; - } - - boolean clearImeAdjustAnimation() { - boolean changed = false; - for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) { - final ActivityStack stack = mTaskContainers.getChildAt(i); - if (stack != null && stack.isAdjustedForIme()) { - stack.resetAdjustedForIme(true /* adjustBoundsNow */); - changed = true; - } - } - return changed; - } - - void beginImeAdjustAnimation() { - for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) { - final ActivityStack stack = mTaskContainers.getChildAt(i); - if (stack.isVisible() && stack.isAdjustedForIme()) { - stack.beginImeAdjustAnimation(); - } - } - } - void adjustForImeIfNeeded() { final WindowState imeWin = mInputMethodWindow; final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() @@ -2896,7 +2836,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final ActivityStack stack = mTaskContainers.getChildAt(i); stack.dumpDebug(proto, TASKS, logLevel); } - mDividerControllerLocked.dumpDebug(proto, DOCKED_STACK_DIVIDER_CONTROLLER); for (int i = mOverlayContainers.getChildCount() - 1; i >= 0; --i) { final WindowToken windowToken = mOverlayContainers.getChildAt(i); windowToken.dumpDebug(proto, OVERLAY_WINDOWS, logLevel); @@ -2919,9 +2858,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo for (int i = mClosingApps.size() - 1; i >= 0; i--) { mClosingApps.valueAt(i).writeIdentifierToProto(proto, CLOSING_APPS); } - for (int i = mChangingApps.size() - 1; i >= 0; i--) { - mChangingApps.valueAt(i).writeIdentifierToProto(proto, CHANGING_APPS); - } proto.write(SINGLE_TASK_INSTANCE, mSingleTaskInstance); final ActivityStack focusedStack = getFocusedStack(); @@ -3069,8 +3005,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } pw.println(); - mDividerControllerLocked.dump(prefix, pw); - pw.println(); mPinnedStackControllerLocked.dump(prefix, pw); pw.println(); @@ -3673,7 +3607,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } - if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty() || !mChangingApps.isEmpty()) { + if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty() || !mChangingContainers.isEmpty()) { pw.println(); if (mOpeningApps.size() > 0) { pw.print(" mOpeningApps="); pw.println(mOpeningApps); @@ -3681,8 +3615,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (mClosingApps.size() > 0) { pw.print(" mClosingApps="); pw.println(mClosingApps); } - if (mChangingApps.size() > 0) { - pw.print(" mChangingApps="); pw.println(mChangingApps); + if (mChangingContainers.size() > 0) { + pw.print(" mChangingApps="); pw.println(mChangingContainers); } } @@ -4091,7 +4025,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mInputMonitor.updateInputWindowsLw(false /*force*/); } - mWmService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER); + mWmService.mH.sendEmptyMessage(UPDATE_MULTI_WINDOW_STACKS); } /** @@ -4699,17 +4633,18 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override int getOrientation(int candidate) { if (isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) { - // Apps and their containers are not allowed to specify an orientation while the - // docked stack is visible...except for the home stack if the docked stack is - // minimized and it actually set something and the bounds is different from the - // display. + // Apps and their containers are not allowed to specify an orientation while using + // root tasks...except for the home stack if it is not resizable and currently + // visible (top of) its root task. if (mRootHomeTask != null && mRootHomeTask.isVisible() - && mDividerControllerLocked.isMinimizedDock() - && !(mDividerControllerLocked.isHomeStackResizable() - && mRootHomeTask.matchParentBounds())) { - final int orientation = mRootHomeTask.getOrientation(); - if (orientation != SCREEN_ORIENTATION_UNSET) { - return orientation; + && mRootHomeTask.getTile() != null) { + final Task topMost = mRootHomeTask.getTopMostTask(); + final boolean resizable = topMost == null && topMost.isResizeable(); + if (!(resizable && mRootHomeTask.matchParentBounds())) { + final int orientation = mRootHomeTask.getOrientation(); + if (orientation != SCREEN_ORIENTATION_UNSET) { + return orientation; + } } } return SCREEN_ORIENTATION_UNSPECIFIED; @@ -6174,8 +6109,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo t.removeAllChildren(); } } - mDividerControllerLocked.setMinimizedDockedStack(false /* minimized */, - false /* animate */); } finally { final ActivityStack topFullscreenStack = getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index f02a9dd61107..497c4733ab58 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -1178,8 +1178,6 @@ public class DisplayPolicy { return R.anim.dock_left_enter; } } - } else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) { - return selectDockedDividerAnimation(win, transit); } if (transit == TRANSIT_PREVIEW_DONE) { @@ -1204,36 +1202,6 @@ public class DisplayPolicy { return ANIMATION_STYLEABLE; } - private int selectDockedDividerAnimation(WindowState win, int transit) { - int insets = mDisplayContent.getDockedDividerController().getContentInsets(); - - // If the divider is behind the navigation bar, don't animate. - final Rect frame = win.getFrameLw(); - final boolean behindNavBar = mNavigationBar != null - && ((mNavigationBarPosition == NAV_BAR_BOTTOM - && frame.top + insets >= mNavigationBar.getFrameLw().top) - || (mNavigationBarPosition == NAV_BAR_RIGHT - && frame.left + insets >= mNavigationBar.getFrameLw().left) - || (mNavigationBarPosition == NAV_BAR_LEFT - && frame.right - insets <= mNavigationBar.getFrameLw().right)); - final boolean landscape = frame.height() > frame.width(); - final boolean offscreenLandscape = landscape && (frame.right - insets <= 0 - || frame.left + insets >= win.getDisplayFrameLw().right); - final boolean offscreenPortrait = !landscape && (frame.top - insets <= 0 - || frame.bottom + insets >= win.getDisplayFrameLw().bottom); - final boolean offscreen = offscreenLandscape || offscreenPortrait; - if (behindNavBar || offscreen) { - return ANIMATION_STYLEABLE; - } - if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) { - return R.anim.fade_in; - } else if (transit == TRANSIT_EXIT) { - return R.anim.fade_out; - } else { - return ANIMATION_STYLEABLE; - } - } - /** * Called when a new system UI visibility is being reported, allowing * the policy to adjust what is actually reported. diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index 6431e117c4e6..20738ed29470 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -16,329 +16,26 @@ package com.android.server.wm; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; -import static android.content.res.Configuration.ORIENTATION_PORTRAIT; -import static android.view.Surface.ROTATION_270; -import static android.view.Surface.ROTATION_90; -import static android.view.WindowManager.DOCKED_BOTTOM; -import static android.view.WindowManager.DOCKED_INVALID; -import static android.view.WindowManager.DOCKED_LEFT; -import static android.view.WindowManager.DOCKED_RIGHT; -import static android.view.WindowManager.DOCKED_TOP; -import static android.view.WindowManager.TRANSIT_NONE; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; - -import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION; -import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR; -import static com.android.server.wm.DockedStackDividerControllerProto.MINIMIZED_DOCK; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; - -import android.content.Context; -import android.content.res.Configuration; import android.graphics.Rect; -import android.os.RemoteCallbackList; -import android.os.RemoteException; -import android.util.ArraySet; -import android.util.Slog; -import android.util.proto.ProtoOutputStream; -import android.view.DisplayCutout; -import android.view.DisplayInfo; -import android.view.IDockedStackListener; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; -import android.view.animation.PathInterpolator; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.policy.DividerSnapAlgorithm; -import com.android.internal.policy.DockedDividerUtils; -import com.android.server.LocalServices; -import com.android.server.inputmethod.InputMethodManagerInternal; -import com.android.server.wm.WindowManagerService.H; - -import java.io.PrintWriter; /** * Keeps information about the docked stack divider. */ public class DockedStackDividerController { - private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM; - - /** - * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip - * revealing surface at the earliest. - */ - private static final float CLIP_REVEAL_MEET_EARLIEST = 0.6f; - - /** - * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip - * revealing surface at the latest. - */ - private static final float CLIP_REVEAL_MEET_LAST = 1f; - - /** - * If the app translates at least CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance, we start - * meet somewhere between {@link #CLIP_REVEAL_MEET_LAST} and {@link #CLIP_REVEAL_MEET_EARLIEST}. - */ - private static final float CLIP_REVEAL_MEET_FRACTION_MIN = 0.4f; - - /** - * If the app translates equals or more than CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance, - * we meet at {@link #CLIP_REVEAL_MEET_EARLIEST}. - */ - private static final float CLIP_REVEAL_MEET_FRACTION_MAX = 0.8f; - - private static final Interpolator IME_ADJUST_ENTRY_INTERPOLATOR = - new PathInterpolator(0.2f, 0f, 0.1f, 1f); - - private static final long IME_ADJUST_ANIM_DURATION = 280; - - private static final long IME_ADJUST_DRAWN_TIMEOUT = 200; - - private static final int DIVIDER_WIDTH_INACTIVE_DP = 4; - - private final WindowManagerService mService; private final DisplayContent mDisplayContent; - private int mDividerWindowWidth; - private int mDividerWindowWidthInactive; - private int mDividerInsets; - private int mTaskHeightInMinimizedMode; private boolean mResizing; - private WindowState mWindow; - private final Rect mTmpRect = new Rect(); - private final Rect mTmpRect2 = new Rect(); - private final Rect mTmpRect3 = new Rect(); - private final Rect mLastRect = new Rect(); - private boolean mLastVisibility = false; - private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners - = new RemoteCallbackList<>(); - private boolean mMinimizedDock; - private int mOriginalDockedSide = DOCKED_INVALID; - private boolean mAnimatingForMinimizedDockedStack; - private boolean mAnimationStarted; - private long mAnimationStartTime; - private float mAnimationStart; - private float mAnimationTarget; - private long mAnimationDuration; - private boolean mAnimationStartDelayed; - private final Interpolator mMinimizedDockInterpolator; - private float mMaximizeMeetFraction; private final Rect mTouchRegion = new Rect(); - private boolean mAnimatingForIme; - private boolean mAdjustedForIme; - private int mImeHeight; - private WindowState mDelayedImeWin; - private boolean mAdjustedForDivider; - private float mDividerAnimationStart; - private float mDividerAnimationTarget; - float mLastAnimationProgress; - float mLastDividerProgress; - private final DividerSnapAlgorithm[] mSnapAlgorithmForRotation = new DividerSnapAlgorithm[4]; - private boolean mImeHideRequested; - private ActivityStack mDimmedStack; - DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) { - mService = service; + DockedStackDividerController(DisplayContent displayContent) { mDisplayContent = displayContent; - final Context context = service.mContext; - mMinimizedDockInterpolator = AnimationUtils.loadInterpolator( - context, android.R.interpolator.fast_out_slow_in); - loadDimens(); - } - - int getSmallestWidthDpForBounds(Rect bounds) { - final DisplayInfo di = mDisplayContent.getDisplayInfo(); - - final int baseDisplayWidth = mDisplayContent.mBaseDisplayWidth; - final int baseDisplayHeight = mDisplayContent.mBaseDisplayHeight; - int minWidth = Integer.MAX_VALUE; - - // Go through all screen orientations and find the orientation in which the task has the - // smallest width. - for (int rotation = 0; rotation < 4; rotation++) { - mTmpRect.set(bounds); - mDisplayContent.rotateBounds(di.rotation, rotation, mTmpRect); - final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); - mTmpRect2.set(0, 0, - rotated ? baseDisplayHeight : baseDisplayWidth, - rotated ? baseDisplayWidth : baseDisplayHeight); - final int orientation = mTmpRect2.width() <= mTmpRect2.height() - ? ORIENTATION_PORTRAIT - : ORIENTATION_LANDSCAPE; - final int dockSide = getDockSide(mTmpRect, mTmpRect2, orientation, rotation); - final int position = DockedDividerUtils.calculatePositionForBounds(mTmpRect, dockSide, - getContentWidth()); - - final DisplayCutout displayCutout = mDisplayContent.calculateDisplayCutoutForRotation( - rotation).getDisplayCutout(); - - // Since we only care about feasible states, snap to the closest snap target, like it - // would happen when actually rotating the screen. - final int snappedPosition = mSnapAlgorithmForRotation[rotation] - .calculateNonDismissingSnapTarget(position).position; - DockedDividerUtils.calculateBoundsForPosition(snappedPosition, dockSide, mTmpRect, - mTmpRect2.width(), mTmpRect2.height(), getContentWidth()); - mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, mTmpRect2.width(), - mTmpRect2.height(), displayCutout, mTmpRect3); - mService.intersectDisplayInsetBounds(mTmpRect2, mTmpRect3, mTmpRect); - minWidth = Math.min(mTmpRect.width(), minWidth); - } - return (int) (minWidth / mDisplayContent.getDisplayMetrics().density); - } - - /** - * Get the current docked side. Determined by its location of {@param bounds} within - * {@param displayRect} but if both are the same, it will try to dock to each side and determine - * if allowed in its respected {@param orientation}. - * - * @param bounds bounds of the docked task to get which side is docked - * @param displayRect bounds of the display that contains the docked task - * @param orientation the origination of device - * @return current docked side - */ - int getDockSide(Rect bounds, Rect displayRect, int orientation, int rotation) { - if (orientation == Configuration.ORIENTATION_PORTRAIT) { - // Portrait mode, docked either at the top or the bottom. - final int diff = (displayRect.bottom - bounds.bottom) - (bounds.top - displayRect.top); - if (diff > 0) { - return DOCKED_TOP; - } else if (diff < 0) { - return DOCKED_BOTTOM; - } - return canPrimaryStackDockTo(DOCKED_TOP, displayRect, rotation) - ? DOCKED_TOP : DOCKED_BOTTOM; - } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) { - // Landscape mode, docked either on the left or on the right. - final int diff = (displayRect.right - bounds.right) - (bounds.left - displayRect.left); - if (diff > 0) { - return DOCKED_LEFT; - } else if (diff < 0) { - return DOCKED_RIGHT; - } - return canPrimaryStackDockTo(DOCKED_LEFT, displayRect, rotation) - ? DOCKED_LEFT : DOCKED_RIGHT; - } - return DOCKED_INVALID; - } - - void getHomeStackBoundsInDockedMode(Configuration parentConfig, int dockSide, Rect outBounds) { - final DisplayCutout displayCutout = mDisplayContent.getDisplayInfo().displayCutout; - final int displayWidth = parentConfig.windowConfiguration.getBounds().width(); - final int displayHeight = parentConfig.windowConfiguration.getBounds().height(); - mDisplayContent.getDisplayPolicy().getStableInsetsLw( - parentConfig.windowConfiguration.getRotation(), displayWidth, displayHeight, - displayCutout, mTmpRect); - int dividerSize = mDividerWindowWidth - 2 * mDividerInsets; - // The offset in the left (landscape)/top (portrait) is calculated with the minimized - // offset value with the divider size and any system insets in that direction. - if (parentConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { - outBounds.set(0, mTaskHeightInMinimizedMode + dividerSize + mTmpRect.top, - displayWidth, displayHeight); - } else { - // In landscape also inset the left/right side with the status bar height to match the - // minimized size height in portrait mode. - final int primaryTaskWidth = mTaskHeightInMinimizedMode + dividerSize + mTmpRect.top; - int left = mTmpRect.left; - int right = displayWidth - mTmpRect.right; - if (dockSide == DOCKED_LEFT) { - left += primaryTaskWidth; - } else if (dockSide == DOCKED_RIGHT) { - right -= primaryTaskWidth; - } - outBounds.set(left, 0, right, displayHeight); - } - } - - boolean isHomeStackResizable() { - final ActivityStack homeStack = mDisplayContent.getRootHomeTask(); - if (homeStack == null) { - return false; - } - final Task homeTask = homeStack.getTopMostTask(); - return homeTask != null && homeTask.isResizeable(); - } - - private void initSnapAlgorithmForRotations() { - final Configuration baseConfig = mDisplayContent.getConfiguration(); - - // Initialize the snap algorithms for all 4 screen orientations. - final Configuration config = new Configuration(); - for (int rotation = 0; rotation < 4; rotation++) { - final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); - final int dw = rotated - ? mDisplayContent.mBaseDisplayHeight - : mDisplayContent.mBaseDisplayWidth; - final int dh = rotated - ? mDisplayContent.mBaseDisplayWidth - : mDisplayContent.mBaseDisplayHeight; - final DisplayCutout displayCutout = - mDisplayContent.calculateDisplayCutoutForRotation(rotation).getDisplayCutout(); - final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); - displayPolicy.getStableInsetsLw(rotation, dw, dh, displayCutout, mTmpRect); - config.unset(); - config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; - - final int appWidth = displayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, - baseConfig.uiMode, displayCutout); - final int appHeight = displayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, - baseConfig.uiMode, displayCutout); - displayPolicy.getNonDecorInsetsLw(rotation, dw, dh, displayCutout, mTmpRect); - final int leftInset = mTmpRect.left; - final int topInset = mTmpRect.top; - - config.windowConfiguration.setAppBounds(leftInset /*left*/, topInset /*top*/, - leftInset + appWidth /*right*/, topInset + appHeight /*bottom*/); - - final float density = mDisplayContent.getDisplayMetrics().density; - config.screenWidthDp = (int) (displayPolicy.getConfigDisplayWidth(dw, dh, rotation, - baseConfig.uiMode, displayCutout) / density); - config.screenHeightDp = (int) (displayPolicy.getConfigDisplayHeight(dw, dh, rotation, - baseConfig.uiMode, displayCutout) / density); - final Context rotationContext = mService.mContext.createConfigurationContext(config); - mSnapAlgorithmForRotation[rotation] = new DividerSnapAlgorithm( - rotationContext.getResources(), dw, dh, getContentWidth(), - config.orientation == ORIENTATION_PORTRAIT, mTmpRect); - } - } - - private void loadDimens() { - final Context context = mService.mContext; - mDividerWindowWidth = context.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.docked_stack_divider_thickness); - mDividerInsets = context.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.docked_stack_divider_insets); - mDividerWindowWidthInactive = WindowManagerService.dipToPixel( - DIVIDER_WIDTH_INACTIVE_DP, mDisplayContent.getDisplayMetrics()); - mTaskHeightInMinimizedMode = context.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.task_height_of_minimized_mode); - initSnapAlgorithmForRotations(); - } - - void onConfigurationChanged() { - loadDimens(); } boolean isResizing() { return mResizing; } - int getContentWidth() { - return mDividerWindowWidth - 2 * mDividerInsets; - } - - int getContentInsets() { - return mDividerInsets; - } - - int getContentWidthInactive() { - return mDividerWindowWidthInactive; - } - void setResizing(boolean resizing) { if (mResizing != resizing) { mResizing = resizing; @@ -352,675 +49,10 @@ public class DockedStackDividerController { void getTouchRegion(Rect outRegion) { outRegion.set(mTouchRegion); - if (mWindow != null) { - outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top); - } } private void resetDragResizingChangeReported() { mDisplayContent.forAllWindows(WindowState::resetDragResizingChangeReported, true /* traverseTopToBottom */ ); } - - void setWindow(WindowState window) { - mWindow = window; - reevaluateVisibility(false); - } - - void reevaluateVisibility(boolean force) { - if (mWindow == null) { - return; - } - ActivityStack stack = mDisplayContent.getRootSplitScreenPrimaryTask(); - - // If the stack is invisible, we policy force hide it in WindowAnimator.shouldForceHide - final boolean visible = stack != null; - if (mLastVisibility == visible && !force) { - return; - } - mLastVisibility = visible; - notifyDockedDividerVisibilityChanged(visible); - if (!visible) { - setResizeDimLayer(false, WINDOWING_MODE_UNDEFINED, 0f); - } - } - - private boolean wasVisible() { - return mLastVisibility; - } - - void setAdjustedForIme( - boolean adjustedForIme, boolean adjustedForDivider, - boolean animate, WindowState imeWin, int imeHeight) { - if (mAdjustedForIme != adjustedForIme || (adjustedForIme && mImeHeight != imeHeight) - || mAdjustedForDivider != adjustedForDivider) { - if (animate && !mAnimatingForMinimizedDockedStack) { - // Notify SystemUI to set the target docked stack size according current docked - // state without animation when calling startImeAdjustAnimation. - notifyDockedStackMinimizedChanged(mMinimizedDock, false /* animate */, - isHomeStackResizable()); - startImeAdjustAnimation(adjustedForIme, adjustedForDivider, imeWin); - } else { - // Animation might be delayed, so only notify if we don't run an animation. - notifyAdjustedForImeChanged(adjustedForIme || adjustedForDivider, 0 /* duration */); - } - mAdjustedForIme = adjustedForIme; - mImeHeight = imeHeight; - mAdjustedForDivider = adjustedForDivider; - } - } - - int getImeHeightAdjustedFor() { - return mImeHeight; - } - - void positionDockedStackedDivider(Rect frame) { - ActivityStack stack = mDisplayContent.getRootSplitScreenPrimaryTask(); - if (stack == null) { - // Unfortunately we might end up with still having a divider, even though the underlying - // stack was already removed. This is because we are on AM thread and the removal of the - // divider was deferred to WM thread and hasn't happened yet. In that case let's just - // keep putting it in the same place it was before the stack was removed to have - // continuity and prevent it from jumping to the center. It will get hidden soon. - frame.set(mLastRect); - return; - } else { - stack.getDimBounds(mTmpRect); - } - int side = stack.getDockSide(); - switch (side) { - case DOCKED_LEFT: - frame.set(mTmpRect.right - mDividerInsets, frame.top, - mTmpRect.right + frame.width() - mDividerInsets, frame.bottom); - break; - case DOCKED_TOP: - frame.set(frame.left, mTmpRect.bottom - mDividerInsets, - mTmpRect.right, mTmpRect.bottom + frame.height() - mDividerInsets); - break; - case DOCKED_RIGHT: - frame.set(mTmpRect.left - frame.width() + mDividerInsets, frame.top, - mTmpRect.left + mDividerInsets, frame.bottom); - break; - case DOCKED_BOTTOM: - frame.set(frame.left, mTmpRect.top - frame.height() + mDividerInsets, - frame.right, mTmpRect.top + mDividerInsets); - break; - } - mLastRect.set(frame); - } - - private void notifyDockedDividerVisibilityChanged(boolean visible) { - final int size = mDockedStackListeners.beginBroadcast(); - for (int i = 0; i < size; ++i) { - final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); - try { - listener.onDividerVisibilityChanged(visible); - } catch (RemoteException e) { - Slog.e(TAG_WM, "Error delivering divider visibility changed event.", e); - } - } - mDockedStackListeners.finishBroadcast(); - } - - /** - * Checks if the primary stack is allowed to dock to a specific side based on its original dock - * side. - * - * @param dockSide the side to see if it is valid - * @return true if the side provided is valid - */ - boolean canPrimaryStackDockTo(int dockSide, Rect parentRect, int rotation) { - final DisplayPolicy policy = mDisplayContent.getDisplayPolicy(); - return isDockSideAllowed(dockSide, mOriginalDockedSide, - policy.navigationBarPosition(parentRect.width(), parentRect.height(), rotation), - policy.navigationBarCanMove()); - } - - @VisibleForTesting - static boolean isDockSideAllowed(int dockSide, int originalDockSide, int navBarPosition, - boolean navigationBarCanMove) { - if (dockSide == DOCKED_TOP) { - return true; - } - - if (navigationBarCanMove) { - // Only allow the dockside opposite to the nav bar position in landscape - return dockSide == DOCKED_LEFT && navBarPosition == NAV_BAR_RIGHT - || dockSide == DOCKED_RIGHT && navBarPosition == NAV_BAR_LEFT; - } - - // Side is the same as original side - if (dockSide == originalDockSide) { - return true; - } - - // Only if original docked side was top in portrait will allow left for landscape - return dockSide == DOCKED_LEFT && originalDockSide == DOCKED_TOP; - } - - void notifyDockedStackExistsChanged(boolean exists) { - // TODO(multi-display): Perform all actions only for current display. - final int size = mDockedStackListeners.beginBroadcast(); - for (int i = 0; i < size; ++i) { - final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); - try { - listener.onDockedStackExistsChanged(exists); - } catch (RemoteException e) { - Slog.e(TAG_WM, "Error delivering docked stack exists changed event.", e); - } - } - mDockedStackListeners.finishBroadcast(); - if (exists) { - InputMethodManagerInternal inputMethodManagerInternal = - LocalServices.getService(InputMethodManagerInternal.class); - if (inputMethodManagerInternal != null) { - - // Hide the current IME to avoid problems with animations from IME adjustment when - // attaching the docked stack. - inputMethodManagerInternal.hideCurrentInputMethod(); - mImeHideRequested = true; - } - - // If a primary stack was just created, it will not have access to display content at - // this point so pass it from here to get a valid dock side. - final ActivityStack stack = - mDisplayContent.getRootSplitScreenPrimaryTask(); - mOriginalDockedSide = stack.getDockSideForDisplay(mDisplayContent); - return; - } - mOriginalDockedSide = DOCKED_INVALID; - setMinimizedDockedStack(false /* minimizedDock */, false /* animate */); - - if (mDimmedStack != null) { - mDimmedStack.stopDimming(); - mDimmedStack = null; - } - } - - /** - * Resets the state that IME hide has been requested. See {@link #isImeHideRequested}. - */ - void resetImeHideRequested() { - mImeHideRequested = false; - } - - /** - * The docked stack divider controller makes sure the IME gets hidden when attaching the docked - * stack, to avoid animation problems. This flag indicates whether the request to hide the IME - * has been sent in an asynchronous manner, and the IME should be treated as hidden already. - * - * @return whether IME hide request has been sent - */ - boolean isImeHideRequested() { - return mImeHideRequested; - } - - private void notifyDockedStackMinimizedChanged(boolean minimizedDock, boolean animate, - boolean isHomeStackResizable) { - long animDuration = 0; - if (animate) { - final ActivityStack stack = - mDisplayContent.getRootSplitScreenPrimaryTask(); - final long transitionDuration = isAnimationMaximizing() - ? mDisplayContent.mAppTransition.getLastClipRevealTransitionDuration() - : DEFAULT_APP_TRANSITION_DURATION; - mAnimationDuration = (long) - (transitionDuration * mService.getTransitionAnimationScaleLocked()); - mMaximizeMeetFraction = getClipRevealMeetFraction(stack); - animDuration = (long) (mAnimationDuration * mMaximizeMeetFraction); - } - final int size = mDockedStackListeners.beginBroadcast(); - for (int i = 0; i < size; ++i) { - final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); - try { - listener.onDockedStackMinimizedChanged(minimizedDock, animDuration, - isHomeStackResizable); - } catch (RemoteException e) { - Slog.e(TAG_WM, "Error delivering minimized dock changed event.", e); - } - } - mDockedStackListeners.finishBroadcast(); - // Only notify ATM after we update the remote listeners, otherwise it may trigger another - // minimize change, which would lead to an inversion of states send to the listeners - mService.mAtmInternal.notifyDockedStackMinimizedChanged(minimizedDock); - } - - void notifyDockSideChanged(int newDockSide) { - final int size = mDockedStackListeners.beginBroadcast(); - for (int i = 0; i < size; ++i) { - final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); - try { - listener.onDockSideChanged(newDockSide); - } catch (RemoteException e) { - Slog.e(TAG_WM, "Error delivering dock side changed event.", e); - } - } - mDockedStackListeners.finishBroadcast(); - } - - private void notifyAdjustedForImeChanged(boolean adjustedForIme, long animDuration) { - final int size = mDockedStackListeners.beginBroadcast(); - for (int i = 0; i < size; ++i) { - final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); - try { - listener.onAdjustedForImeChanged(adjustedForIme, animDuration); - } catch (RemoteException e) { - Slog.e(TAG_WM, "Error delivering adjusted for ime changed event.", e); - } - } - mDockedStackListeners.finishBroadcast(); - } - - void registerDockedStackListener(IDockedStackListener listener) { - mDockedStackListeners.register(listener); - notifyDockedDividerVisibilityChanged(wasVisible()); - notifyDockedStackExistsChanged( - mDisplayContent.getRootSplitScreenPrimaryTask() != null); - notifyDockedStackMinimizedChanged(mMinimizedDock, false /* animate */, - isHomeStackResizable()); - notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */); - - } - - /** - * Shows a dim layer with {@param alpha} if {@param visible} is true and - * {@param targetWindowingMode} isn't - * {@link android.app.WindowConfiguration#WINDOWING_MODE_UNDEFINED} and there is a stack on the - * display in that windowing mode. - */ - void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) { - // TODO: Maybe only allow split-screen windowing modes? - final ActivityStack stack = targetWindowingMode != WINDOWING_MODE_UNDEFINED - ? mDisplayContent.getTopStackInWindowingMode(targetWindowingMode) - : null; - final ActivityStack dockedStack = mDisplayContent.getRootSplitScreenPrimaryTask(); - boolean visibleAndValid = visible && stack != null && dockedStack != null; - - // Ensure an old dim that was shown for the docked stack divider is removed so we don't end - // up with dim layers that can no longer be removed. - if (mDimmedStack != null && mDimmedStack != stack) { - mDimmedStack.stopDimming(); - mDimmedStack = null; - } - - if (visibleAndValid) { - mDimmedStack = stack; - stack.dim(alpha); - } - if (!visibleAndValid && stack != null) { - mDimmedStack = null; - stack.stopDimming(); - } - } - - /** - * Notifies the docked stack divider controller of a visibility change that happens without - * an animation. - */ - void notifyAppVisibilityChanged() { - checkMinimizeChanged(false /* animate */); - } - - void notifyAppTransitionStarting(ArraySet<ActivityRecord> openingApps, int appTransition) { - final boolean wasMinimized = mMinimizedDock; - checkMinimizeChanged(true /* animate */); - - // We were minimized, and now we are still minimized, but somebody is trying to launch an - // app in docked stack, better show recent apps so we actually get unminimized! However do - // not do this if keyguard is dismissed such as when the device is unlocking. This catches - // any case that was missed in ActivityStarter.postStartActivityUncheckedProcessing because - // we couldn't retrace the launch of the app in the docked stack to the launch from - // homescreen. - if (wasMinimized && mMinimizedDock && containsAppInDockedStack(openingApps) - && appTransition != TRANSIT_NONE && - !AppTransition.isKeyguardGoingAwayTransit(appTransition)) { - if (mService.mAtmInternal.isRecentsComponentHomeActivity(mService.mCurrentUserId)) { - // When the home activity is the recents component and we are already minimized, - // then there is nothing to do here since home is already visible - } else { - mService.showRecentApps(); - } - } - } - - /** - * @return true if {@param apps} contains an activity in the docked stack, false otherwise. - */ - private boolean containsAppInDockedStack(ArraySet<ActivityRecord> apps) { - for (int i = apps.size() - 1; i >= 0; i--) { - final ActivityRecord activity = apps.valueAt(i); - if (activity.getTask() != null && activity.inSplitScreenPrimaryWindowingMode()) { - return true; - } - } - return false; - } - - boolean isMinimizedDock() { - return mMinimizedDock; - } - - void checkMinimizeChanged(boolean animate) { - if (mDisplayContent.getRootSplitScreenPrimaryTask() == null) { - return; - } - final ActivityStack homeStack = mDisplayContent.getRootHomeTask(); - if (homeStack == null) { - return; - } - final Task homeTask = homeStack.getTopMostTask(); - if (homeTask == null || !isWithinDisplay(homeTask)) { - return; - } - - // Do not minimize when dock is already minimized while keyguard is showing and not - // occluded such as unlocking the screen - if (mMinimizedDock && mService.mKeyguardOrAodShowingOnDefaultDisplay) { - return; - } - final ActivityStack topSecondaryStack = mDisplayContent.getTopStackInWindowingMode( - WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); - final RecentsAnimationController recentsAnim = mService.getRecentsAnimationController(); - final boolean minimizedForRecentsAnimation = recentsAnim != null && - recentsAnim.isSplitScreenMinimized(); - boolean homeVisible = homeTask.getTopVisibleActivity() != null; - if (homeVisible && topSecondaryStack != null) { - // Home should only be considered visible if it is greater or equal to the top secondary - // stack in terms of z-order. - homeVisible = homeStack.compareTo(topSecondaryStack) >= 0; - } - setMinimizedDockedStack(homeVisible || minimizedForRecentsAnimation, animate); - } - - private boolean isWithinDisplay(Task task) { - task.getBounds(mTmpRect); - mDisplayContent.getBounds(mTmpRect2); - return mTmpRect.intersect(mTmpRect2); - } - - /** - * Sets whether the docked stack is currently in a minimized state, i.e. all the tasks in the - * docked stack are heavily clipped so you can only see a minimal peek state. - * - * @param minimizedDock Whether the docked stack is currently minimized. - * @param animate Whether to animate the change. - */ - void setMinimizedDockedStack(boolean minimizedDock, boolean animate) { - final boolean wasMinimized = mMinimizedDock; - mMinimizedDock = minimizedDock; - if (minimizedDock == wasMinimized) { - return; - } - - final boolean imeChanged = clearImeAdjustAnimation(); - boolean minimizedChange = false; - if (isHomeStackResizable()) { - notifyDockedStackMinimizedChanged(minimizedDock, animate, - true /* isHomeStackResizable */); - minimizedChange = true; - } else { - if (minimizedDock) { - if (animate) { - startAdjustAnimation(0f, 1f); - } else { - minimizedChange |= setMinimizedDockedStack(true); - } - } else { - if (animate) { - startAdjustAnimation(1f, 0f); - } else { - minimizedChange |= setMinimizedDockedStack(false); - } - } - } - if (imeChanged || minimizedChange) { - if (imeChanged && !minimizedChange) { - Slog.d(TAG, "setMinimizedDockedStack: IME adjust changed due to minimizing," - + " minimizedDock=" + minimizedDock - + " minimizedChange=" + minimizedChange); - } - mService.mWindowPlacerLocked.performSurfacePlacement(); - } - } - - private boolean clearImeAdjustAnimation() { - final boolean changed = mDisplayContent.clearImeAdjustAnimation(); - mAnimatingForIme = false; - return changed; - } - - private void startAdjustAnimation(float from, float to) { - mAnimatingForMinimizedDockedStack = true; - mAnimationStarted = false; - mAnimationStart = from; - mAnimationTarget = to; - } - - private void startImeAdjustAnimation( - boolean adjustedForIme, boolean adjustedForDivider, WindowState imeWin) { - - // If we're not in an animation, the starting point depends on whether we're adjusted - // or not. If we're already in an animation, we start from where the current animation - // left off, so that the motion doesn't look discontinuous. - if (!mAnimatingForIme) { - mAnimationStart = mAdjustedForIme ? 1 : 0; - mDividerAnimationStart = mAdjustedForDivider ? 1 : 0; - mLastAnimationProgress = mAnimationStart; - mLastDividerProgress = mDividerAnimationStart; - } else { - mAnimationStart = mLastAnimationProgress; - mDividerAnimationStart = mLastDividerProgress; - } - mAnimatingForIme = true; - mAnimationStarted = false; - mAnimationTarget = adjustedForIme ? 1 : 0; - mDividerAnimationTarget = adjustedForDivider ? 1 : 0; - - mDisplayContent.beginImeAdjustAnimation(); - - // We put all tasks into drag resizing mode - wait until all of them have completed the - // drag resizing switch. - final Runnable existingWaitingForDrwanCallback = - mService.mWaitingForDrawnCallbacks.get(mService.mRoot); - if (existingWaitingForDrwanCallback != null) { - mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, mService.mRoot); - mService.mH.sendMessageDelayed(mService.mH.obtainMessage(H.WAITING_FOR_DRAWN_TIMEOUT, - mService.mRoot), - IME_ADJUST_DRAWN_TIMEOUT); - mAnimationStartDelayed = true; - if (imeWin != null) { - - // There might be an old window delaying the animation start - clear it. - if (mDelayedImeWin != null) { - mDelayedImeWin.endDelayingAnimationStart(); - } - mDelayedImeWin = imeWin; - imeWin.startDelayingAnimationStart(); - } - - // If we are already waiting for something to be drawn, clear out the old one so it - // still gets executed. - // TODO: Have a real system where we can wait on different windows to be drawn with - // different callbacks. - existingWaitingForDrwanCallback.run(); - mService.mWaitingForDrawnCallbacks.put(mService.mRoot, () -> { - synchronized (mService.mGlobalLock) { - mAnimationStartDelayed = false; - if (mDelayedImeWin != null) { - mDelayedImeWin.endDelayingAnimationStart(); - } - // If the adjust status changed since this was posted, only notify - // the new states and don't animate. - long duration = 0; - if (mAdjustedForIme == adjustedForIme - && mAdjustedForDivider == adjustedForDivider) { - duration = IME_ADJUST_ANIM_DURATION; - } else { - Slog.w(TAG, "IME adjust changed while waiting for drawn:" - + " adjustedForIme=" + adjustedForIme - + " adjustedForDivider=" + adjustedForDivider - + " mAdjustedForIme=" + mAdjustedForIme - + " mAdjustedForDivider=" + mAdjustedForDivider); - } - notifyAdjustedForImeChanged( - mAdjustedForIme || mAdjustedForDivider, duration); - } - }); - } else { - notifyAdjustedForImeChanged( - adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION); - } - } - - private boolean setMinimizedDockedStack(boolean minimized) { - final ActivityStack stack = mDisplayContent.getRootSplitScreenPrimaryTask(); - notifyDockedStackMinimizedChanged(minimized, false /* animate */, isHomeStackResizable()); - return stack != null && stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f); - } - - private boolean isAnimationMaximizing() { - return mAnimationTarget == 0f; - } - - public boolean animate(long now) { - if (mWindow == null) { - return false; - } - if (mAnimatingForMinimizedDockedStack) { - return animateForMinimizedDockedStack(now); - } else if (mAnimatingForIme && !mDisplayContent.mAppTransition.isRunning()) { - // To prevent task stack resize animation may flicking when playing app transition - // animation & IME window enter animation in parallel, make sure app transition is done - // and then start to animate for IME. - return animateForIme(now); - } - return false; - } - - private boolean animateForIme(long now) { - if (!mAnimationStarted || mAnimationStartDelayed) { - mAnimationStarted = true; - mAnimationStartTime = now; - mAnimationDuration = (long) - (IME_ADJUST_ANIM_DURATION * mService.getWindowAnimationScaleLocked()); - } - float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration); - t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR) - .getInterpolation(t); - final boolean updated = - mDisplayContent.animateForIme(t, mAnimationTarget, mDividerAnimationTarget); - if (updated) { - mService.mWindowPlacerLocked.performSurfacePlacement(); - } - if (t >= 1.0f) { - mLastAnimationProgress = mAnimationTarget; - mLastDividerProgress = mDividerAnimationTarget; - mAnimatingForIme = false; - return false; - } else { - return true; - } - } - - private boolean animateForMinimizedDockedStack(long now) { - final ActivityStack stack = mDisplayContent.getRootSplitScreenPrimaryTask(); - if (!mAnimationStarted) { - mAnimationStarted = true; - mAnimationStartTime = now; - notifyDockedStackMinimizedChanged(mMinimizedDock, true /* animate */, - isHomeStackResizable() /* isHomeStackResizable */); - } - float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration); - t = (isAnimationMaximizing() ? TOUCH_RESPONSE_INTERPOLATOR : mMinimizedDockInterpolator) - .getInterpolation(t); - if (stack != null) { - if (stack.setAdjustedForMinimizedDock(getMinimizeAmount(stack, t))) { - mService.mWindowPlacerLocked.performSurfacePlacement(); - } - } - if (t >= 1.0f) { - mAnimatingForMinimizedDockedStack = false; - return false; - } else { - return true; - } - } - - float getInterpolatedAnimationValue(float t) { - return t * mAnimationTarget + (1 - t) * mAnimationStart; - } - - float getInterpolatedDividerValue(float t) { - return t * mDividerAnimationTarget + (1 - t) * mDividerAnimationStart; - } - - /** - * Gets the amount how much to minimize a stack depending on the interpolated fraction t. - */ - private float getMinimizeAmount(ActivityStack stack, float t) { - final float naturalAmount = getInterpolatedAnimationValue(t); - if (isAnimationMaximizing()) { - return adjustMaximizeAmount(stack, t, naturalAmount); - } else { - return naturalAmount; - } - } - - /** - * When maximizing the stack during a clip reveal transition, this adjusts the minimize amount - * during the transition such that the edge of the clip reveal rect is met earlier in the - * transition so we don't create a visible "hole", but only if both the clip reveal and the - * docked stack divider start from about the same portion on the screen. - */ - private float adjustMaximizeAmount(ActivityStack stack, float t, float naturalAmount) { - if (mMaximizeMeetFraction == 1f) { - return naturalAmount; - } - final int minimizeDistance = stack.getMinimizeDistance(); - final float startPrime = mDisplayContent.mAppTransition.getLastClipRevealMaxTranslation() - / (float) minimizeDistance; - final float amountPrime = t * mAnimationTarget + (1 - t) * startPrime; - final float t2 = Math.min(t / mMaximizeMeetFraction, 1); - return amountPrime * t2 + naturalAmount * (1 - t2); - } - - /** - * Retrieves the animation fraction at which the docked stack has to meet the clip reveal - * edge. See {@link #adjustMaximizeAmount}. - */ - private float getClipRevealMeetFraction(ActivityStack stack) { - if (!isAnimationMaximizing() || stack == null || - !mDisplayContent.mAppTransition.hadClipRevealAnimation()) { - return 1f; - } - final int minimizeDistance = stack.getMinimizeDistance(); - final float fraction = Math.abs(mDisplayContent.mAppTransition - .getLastClipRevealMaxTranslation()) / (float) minimizeDistance; - final float t = Math.max(0, Math.min(1, (fraction - CLIP_REVEAL_MEET_FRACTION_MIN) - / (CLIP_REVEAL_MEET_FRACTION_MAX - CLIP_REVEAL_MEET_FRACTION_MIN))); - return CLIP_REVEAL_MEET_EARLIEST - + (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST); - } - - public String toShortString() { - return TAG; - } - - WindowState getWindow() { - return mWindow; - } - - void dump(String prefix, PrintWriter pw) { - pw.println(prefix + "DockedStackDividerController"); - pw.println(prefix + " mLastVisibility=" + mLastVisibility); - pw.println(prefix + " mMinimizedDock=" + mMinimizedDock); - pw.println(prefix + " mAdjustedForIme=" + mAdjustedForIme); - pw.println(prefix + " mAdjustedForDivider=" + mAdjustedForDivider); - } - - void dumpDebug(ProtoOutputStream proto, long fieldId) { - final long token = proto.start(fieldId); - proto.write(MINIMIZED_DOCK, mMinimizedDock); - proto.end(token); - } } diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java index 668b6095738f..e14b8ae13bbc 100644 --- a/services/core/java/com/android/server/wm/PinnedStackController.java +++ b/services/core/java/com/android/server/wm/PinnedStackController.java @@ -16,8 +16,6 @@ package com.android.server.wm; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; - import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -52,7 +50,7 @@ import java.util.List; * 1) When first entering PiP: the controller returns the valid bounds given, taking aspect ratio * and IME state into account. * 2) When rotating the device: the controller calculates the new bounds in the new orientation, - * taking the minimized and IME state into account. In this case, we currently ignore the + * taking the IME state into account. In this case, we currently ignore the * SystemUI adjustments (ie. expanded for menu, interaction, etc). * * Other changes in the system, including adjustment of IME, configuration change, and more are @@ -72,8 +70,6 @@ class PinnedStackController { private final PinnedStackControllerCallback mCallbacks = new PinnedStackControllerCallback(); - // States that affect how the PIP can be manipulated - private boolean mIsMinimized; private boolean mIsImeShowing; private int mImeHeight; @@ -84,9 +80,6 @@ class PinnedStackController { // Used to calculate stack bounds across rotations private final DisplayInfo mDisplayInfo = new DisplayInfo(); - // The size and position information that describes where the pinned stack will go by default. - private float mDefaultAspectRatio; - // The aspect ratio bounds of the PIP. private float mMinAspectRatio; private float mMaxAspectRatio; @@ -98,43 +91,12 @@ class PinnedStackController { * The callback object passed to listeners for them to notify the controller of state changes. */ private class PinnedStackControllerCallback extends IPinnedStackController.Stub { - - @Override - public void setIsMinimized(final boolean isMinimized) { - mHandler.post(() -> { - mIsMinimized = isMinimized; - }); - } - @Override public int getDisplayRotation() { synchronized (mService.mGlobalLock) { return mDisplayInfo.rotation; } } - - @Override - public void startAnimation(Rect destinationBounds, Rect sourceRectHint, - int animationDuration) { - synchronized (mService.mGlobalLock) { - final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask(); - pinnedStack.animateResizePinnedStack(destinationBounds, - sourceRectHint, animationDuration, true /* fromFullscreen */); - } - } - - @Override - public void resetBoundsAnimation(Rect bounds) { - synchronized (mService.mGlobalLock) { - if (mDisplayContent.hasPinnedTask()) { - final ActivityStack pinnedStack = mDisplayContent.getTopStackInWindowingMode( - WINDOWING_MODE_PINNED); - if (pinnedStack != null) { - pinnedStack.resetCurrentBoundsAnimation(bounds); - } - } - } - } } /** @@ -157,10 +119,6 @@ class PinnedStackController { mDisplayContent = displayContent; mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo()); reloadResources(); - // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload - // resources as it would clobber mAspectRatio when entering PiP from fullscreen which - // triggers a configuration change and the resources to be reloaded. - mAspectRatio = mDefaultAspectRatio; } void onConfigurationChanged() { @@ -172,8 +130,6 @@ class PinnedStackController { */ private void reloadResources() { final Resources res = mService.mContext.getResources(); - mDefaultAspectRatio = res.getFloat( - com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio); mDisplayContent.getDisplay().getRealMetrics(mTmpMetrics); mMinAspectRatio = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio); @@ -191,12 +147,8 @@ class PinnedStackController { mPinnedStackListener = listener; notifyDisplayInfoChanged(mDisplayInfo); notifyImeVisibilityChanged(mIsImeShowing, mImeHeight); - // The movement bounds notification needs to be sent before the minimized state, since - // SystemUI may use the bounds to restore the minimized position - notifyMovementBoundsChanged(false /* fromImeAdjustment */, - false /* fromShelfAdjustment */); + notifyMovementBoundsChanged(false /* fromImeAdjustment */); notifyActionsChanged(mActions); - notifyMinimizeChanged(mIsMinimized); } catch (RemoteException e) { Log.e(TAG, "Failed to register pinned stack listener", e); } @@ -247,8 +199,7 @@ class PinnedStackController { void onDisplayInfoChanged(DisplayInfo displayInfo) { synchronized (mService.mGlobalLock) { setDisplayInfo(displayInfo); - notifyMovementBoundsChanged(false /* fromImeAdjustment */, - false /* fromShelfAdjustment */); + notifyMovementBoundsChanged(false /* fromImeAdjustment */); } } @@ -269,7 +220,7 @@ class PinnedStackController { mIsImeShowing = imeShowing; mImeHeight = imeHeight; notifyImeVisibilityChanged(imeShowing, imeHeight); - notifyMovementBoundsChanged(true /* fromImeAdjustment */, false /* fromShelfAdjustment */); + notifyMovementBoundsChanged(true /* fromImeAdjustment */); } /** @@ -279,10 +230,7 @@ class PinnedStackController { if (Float.compare(mAspectRatio, aspectRatio) != 0) { mAspectRatio = aspectRatio; notifyAspectRatioChanged(aspectRatio); - notifyMovementBoundsChanged(false /* fromImeAdjustment */, - false /* fromShelfAdjustment */); - notifyPrepareAnimation(null /* sourceHintRect */, aspectRatio, - null /* stackBounds */); + notifyMovementBoundsChanged(false /* fromImeAdjustment */); } } @@ -304,10 +252,6 @@ class PinnedStackController { notifyActionsChanged(mActions); } - void prepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) { - notifyPrepareAnimation(sourceRectHint, aspectRatio, stackBounds); - } - /** * Notifies listeners that the PIP needs to be adjusted for the IME. */ @@ -331,19 +275,6 @@ class PinnedStackController { } /** - * Notifies listeners that the PIP minimized state has changed. - */ - private void notifyMinimizeChanged(boolean isMinimized) { - if (mPinnedStackListener != null) { - try { - mPinnedStackListener.onMinimizedStateChanged(isMinimized); - } catch (RemoteException e) { - Slog.e(TAG_WM, "Error delivering minimize changed event.", e); - } - } - } - - /** * Notifies listeners that the PIP actions have changed. */ private void notifyActionsChanged(List<RemoteAction> actions) { @@ -359,8 +290,7 @@ class PinnedStackController { /** * Notifies listeners that the PIP movement bounds have changed. */ - private void notifyMovementBoundsChanged(boolean fromImeAdjustment, - boolean fromShelfAdjustment) { + private void notifyMovementBoundsChanged(boolean fromImeAdjustment) { synchronized (mService.mGlobalLock) { if (mPinnedStackListener == null) { return; @@ -371,8 +301,7 @@ class PinnedStackController { if (pinnedStack != null) { pinnedStack.getAnimationOrCurrentBounds(animatingBounds); } - mPinnedStackListener.onMovementBoundsChanged(animatingBounds, - fromImeAdjustment, fromShelfAdjustment); + mPinnedStackListener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment); } catch (RemoteException e) { Slog.e(TAG_WM, "Error delivering actions changed event.", e); } @@ -391,24 +320,10 @@ class PinnedStackController { } } - /** - * Notifies listeners that the PIP animation is about to happen. - */ - private void notifyPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) { - if (mPinnedStackListener == null) return; - try { - mPinnedStackListener.onPrepareAnimation(sourceRectHint, aspectRatio, stackBounds); - } catch (RemoteException e) { - Slog.e(TAG_WM, "Error delivering prepare animation event.", e); - } - } - void dump(String prefix, PrintWriter pw) { pw.println(prefix + "PinnedStackController"); - pw.println(prefix + " mDefaultAspectRatio=" + mDefaultAspectRatio); pw.println(prefix + " mIsImeShowing=" + mIsImeShowing); pw.println(prefix + " mImeHeight=" + mImeHeight); - pw.println(prefix + " mIsMinimized=" + mIsMinimized); pw.println(prefix + " mAspectRatio=" + mAspectRatio); pw.println(prefix + " mMinAspectRatio=" + mMinAspectRatio); pw.println(prefix + " mMaxAspectRatio=" + mMaxAspectRatio); diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index e92bbaae71d3..adafdec40d37 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -26,8 +26,6 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.WindowManager.TRANSIT_NONE; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; -import static com.android.server.wm.BoundsAnimationController.BOUNDS; -import static com.android.server.wm.BoundsAnimationController.FADE_IN; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; @@ -426,11 +424,6 @@ class RecentsAnimation implements RecentsAnimationCallbacks, return; } - final DisplayContent dc = - mService.mRootWindowContainer.getDefaultDisplay().mDisplayContent; - dc.mBoundsAnimationController.setAnimationType( - controller.shouldDeferCancelUntilNextTransition() ? FADE_IN : BOUNDS); - // We defer canceling the recents animation until the next app transition in the following // cases: // 1) The next launching task is not being animated by the recents animation diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 6b6b94681879..54cea938b57b 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -26,7 +26,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM; import static com.android.server.wm.AnimationAdapterProto.REMOTE; -import static com.android.server.wm.BoundsAnimationController.FADE_IN; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; @@ -55,6 +54,7 @@ import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledFunction; import com.android.internal.util.function.pooled.PooledLambda; @@ -125,10 +125,6 @@ public class RecentsAnimationController implements DeathRecipient { // enabled for it to start intercepting touch events. private boolean mInputConsumerEnabled; - // Whether or not the recents animation should cause the primary split-screen stack to be - // minimized - private boolean mSplitScreenMinimized; - private final Rect mTmpRect = new Rect(); private boolean mLinkedToDeathOfRunner; @@ -231,7 +227,6 @@ public class RecentsAnimationController implements DeathRecipient { mCallbacks.onAnimationFinished(moveHomeToTop ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION, sendUserLeaveHint); - mDisplayContent.mBoundsAnimationController.setAnimationType(FADE_IN); } finally { Binder.restoreCallingIdentity(token); } @@ -278,30 +273,14 @@ public class RecentsAnimationController implements DeathRecipient { } @Override - public void setSplitScreenMinimized(boolean minimized) { - final long token = Binder.clearCallingIdentity(); - try { - synchronized (mService.getWindowManagerLock()) { - if (mCanceled) { - return; - } - - mSplitScreenMinimized = minimized; - mService.checkSplitScreenMinimizedChanged(true /* animate */); - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override public void hideCurrentInputMethod() { final long token = Binder.clearCallingIdentity(); try { final InputMethodManagerInternal inputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class); if (inputMethodManagerInternal != null) { - inputMethodManagerInternal.hideCurrentInputMethod(); + inputMethodManagerInternal.hideCurrentInputMethod( + SoftInputShowHideReason.HIDE_RECENTS_ANIMATION); } } finally { Binder.restoreCallingIdentity(token); @@ -309,14 +288,6 @@ public class RecentsAnimationController implements DeathRecipient { } @Override - @Deprecated - public void setCancelWithDeferredScreenshot(boolean screenshot) { - synchronized (mService.mGlobalLock) { - setDeferredCancel(true /* deferred */, screenshot); - } - } - - @Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { synchronized (mService.mGlobalLock) { setDeferredCancel(defer, screenshot); @@ -508,7 +479,7 @@ public class RecentsAnimationController implements DeathRecipient { } if (mTargetActivityRecord != null) { - final ArrayMap<ActivityRecord, Integer> reasons = new ArrayMap<>(1); + final ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>(1); reasons.put(mTargetActivityRecord, APP_TRANSITION_RECENTS_ANIM); mService.mAtmService.mStackSupervisor.getActivityMetricsLogger() .notifyTransitionStarting(reasons); @@ -746,10 +717,6 @@ public class RecentsAnimationController implements DeathRecipient { } } - boolean isSplitScreenMinimized() { - return mSplitScreenMinimized; - } - boolean isWallpaperVisible(WindowState w) { return w != null && w.mAttrs.type == TYPE_BASE_APPLICATION && ((w.mActivityRecord != null && mTargetActivityRecord == w.mActivityRecord) @@ -952,7 +919,6 @@ public class RecentsAnimationController implements DeathRecipient { pw.print(innerPrefix); pw.println("mPendingAnimations=" + mPendingAnimations.size()); pw.print(innerPrefix); pw.println("mCanceled=" + mCanceled); pw.print(innerPrefix); pw.println("mInputConsumerEnabled=" + mInputConsumerEnabled); - pw.print(innerPrefix); pw.println("mSplitScreenMinimized=" + mSplitScreenMinimized); pw.print(innerPrefix); pw.println("mTargetActivityRecord=" + mTargetActivityRecord); pw.print(innerPrefix); pw.println("isTargetOverWallpaper=" + isTargetOverWallpaper()); pw.print(innerPrefix); pw.println("mRequestDeferCancelUntilNextTransition=" diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index d2dbab841f16..0eb9daf26d47 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -387,7 +387,7 @@ class RemoteAnimationController implements DeathRecipient { final ActivityRecord topActivity = mWindowContainer.getTopMostActivity(); if (dc.mOpeningApps.contains(topActivity)) { return RemoteAnimationTarget.MODE_OPENING; - } else if (dc.mChangingApps.contains(topActivity)) { + } else if (dc.mChangingContainers.contains(topActivity)) { return RemoteAnimationTarget.MODE_CHANGING; } else { return RemoteAnimationTarget.MODE_CLOSING; diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index aa6bdfd1aa5b..ada5685e6817 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -258,9 +258,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> */ final ArrayList<ActivityTaskManagerInternal.SleepToken> mSleepTokens = new ArrayList<>(); - /** Is dock currently minimized. */ - boolean mIsDockMinimized; - /** Set when a power hint has started, but not ended. */ private boolean mPowerHintSent; @@ -1011,9 +1008,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Send any pending task-info changes that were queued-up during a layout deferment mWmService.mAtmService.mTaskOrganizerController.dispatchPendingTaskInfoChanges(); - if (DEBUG_WINDOW_TRACE) Slog.e(TAG, - "performSurfacePlacementInner exit: animating=" - + mWmService.mAnimator.isAnimating()); + if (DEBUG_WINDOW_TRACE) Slog.e(TAG, "performSurfacePlacementInner exit"); } private void checkAppTransitionReady(WindowSurfacePlacer surfacePlacer) { @@ -1989,8 +1984,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // We dismiss the docked stack whenever we switch users. final ActivityStack dockedStack = getDefaultDisplay().getRootSplitScreenPrimaryTask(); if (dockedStack != null) { - mStackSupervisor.moveTasksToFullscreenStackLocked( - dockedStack, dockedStack.isFocusedStackOnDisplay()); + getDefaultDisplay().onSplitScreenModeDismissed(); } // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will // also cause all tasks to be moved to the fullscreen stack at a position that is @@ -2149,19 +2143,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mService.continueWindowLayout(); } - // TODO(b/146594635): Remove all PIP animation code from WM once SysUI handles animation. - // Notify the pinned stack controller to prepare the PiP animation, expect callback - // delivered from SystemUI to WM to start the animation. Unless we are using - // the TaskOrganizer in which case the animation will be entirely handled - // on that side. - if (mService.mTaskOrganizerController.getTaskOrganizer(WINDOWING_MODE_PINNED) - == null) { - final PinnedStackController pinnedStackController = - display.mDisplayContent.getPinnedStackController(); - pinnedStackController.prepareAnimation(sourceHintBounds, aspectRatio, - null /* stackBounds */); - } - // TODO: revisit the following statement after the animation is moved from WM to SysUI. // Update the visibility of all activities after the they have been reparented to the new // stack. This MUST run after the animation above is scheduled to ensure that the windows @@ -2180,21 +2161,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } - void setDockedStackMinimized(boolean minimized) { - // Get currently focused stack before setting mIsDockMinimized. We do this because if - // split-screen is active, primary stack will not be focusable (see #isFocusable) while - // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null. - final ActivityStack current = getTopDisplayFocusedStack(); - mIsDockMinimized = minimized; - if (mIsDockMinimized) { - if (current.inSplitScreenPrimaryWindowingMode()) { - // The primary split-screen stack can't be focused while it is minimize, so move - // focus to something else. - current.adjustFocusToNextFocusableStack("setDockedStackMinimized"); - } - } - } - ActivityRecord findTask(ActivityRecord r, int preferredDisplayId) { if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r); mTmpFindTaskResult.clear(); diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index 7164cd85230b..1e54e69c0635 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -128,7 +128,8 @@ class SurfaceAnimator { */ void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden, @AnimationType int type, - @Nullable OnAnimationFinishedCallback animationFinishedCallback) { + @Nullable OnAnimationFinishedCallback animationFinishedCallback, + @Nullable SurfaceFreezer freezer) { cancelAnimation(t, true /* restarting */, true /* forwardCancel */); mAnimation = anim; mAnimationType = type; @@ -139,9 +140,14 @@ class SurfaceAnimator { cancelAnimation(); return; } - mLeash = createAnimationLeash(surface, t, - mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), hidden); - mAnimatable.onAnimationLeashCreated(t, mLeash); + mLeash = freezer != null ? freezer.takeLeashForAnimation() : null; + if (mLeash == null) { + mLeash = createAnimationLeash(mAnimatable, surface, t, + mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */, + 0 /* y */, hidden); + mAnimatable.onAnimationLeashCreated(t, mLeash); + } + mAnimatable.onLeashAnimationStarting(t, mLeash); if (mAnimationStartDelayed) { if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed"); return; @@ -150,6 +156,12 @@ class SurfaceAnimator { } void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden, + @AnimationType int type, + @Nullable OnAnimationFinishedCallback animationFinishedCallback) { + startAnimation(t, anim, hidden, type, animationFinishedCallback, null /* freezer */); + } + + void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden, @AnimationType int type) { startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */); } @@ -311,15 +323,31 @@ class SurfaceAnimator { } private void reset(Transaction t, boolean destroyLeash) { - final SurfaceControl surface = mAnimatable.getSurfaceControl(); - final SurfaceControl parent = mAnimatable.getParentSurfaceControl(); + mService.mAnimationTransferMap.remove(mAnimation); + mAnimation = null; + mAnimationFinishedCallback = null; + mAnimationType = ANIMATION_TYPE_NONE; + if (mLeash == null) { + return; + } + SurfaceControl leash = mLeash; + mLeash = null; + final boolean scheduleAnim = removeLeash(t, mAnimatable, leash, destroyLeash); + if (scheduleAnim) { + mService.scheduleAnimationLocked(); + } + } + static boolean removeLeash(Transaction t, Animatable animatable, @NonNull SurfaceControl leash, + boolean destroy) { boolean scheduleAnim = false; + final SurfaceControl surface = animatable.getSurfaceControl(); + final SurfaceControl parent = animatable.getParentSurfaceControl(); // If the surface was destroyed or the leash is invalid, we don't care to reparent it back. // Note that we also set this variable to true even if the parent isn't valid anymore, in // order to ensure onAnimationLeashLost still gets called in this case. - final boolean reparent = mLeash != null && surface != null; + final boolean reparent = surface != null; if (reparent) { if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent: " + parent); // We shouldn't really need these isValid checks but we do @@ -329,40 +357,30 @@ class SurfaceAnimator { scheduleAnim = true; } } - mService.mAnimationTransferMap.remove(mAnimation); - if (mLeash != null && destroyLeash) { - t.remove(mLeash); + if (destroy) { + t.remove(leash); scheduleAnim = true; } - mLeash = null; - mAnimation = null; - mAnimationFinishedCallback = null; - mAnimationType = ANIMATION_TYPE_NONE; if (reparent) { // Make sure to inform the animatable after the surface was reparented (or reparent // wasn't possible, but we still need to invoke the callback) - mAnimatable.onAnimationLeashLost(t); + animatable.onAnimationLeashLost(t); scheduleAnim = true; } - - if (scheduleAnim) { - mService.scheduleAnimationLocked(); - } + return scheduleAnim; } - private SurfaceControl createAnimationLeash(SurfaceControl surface, Transaction t, int width, - int height, boolean hidden) { + static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface, + Transaction t, int width, int height, int x, int y, boolean hidden) { if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash"); - final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash() - .setParent(mAnimatable.getAnimationLeashParent()) + final SurfaceControl.Builder builder = animatable.makeAnimationLeash() + .setParent(animatable.getAnimationLeashParent()) .setHidden(hidden) .setName(surface + " - animation-leash"); final SurfaceControl leash = builder.build(); t.setWindowCrop(leash, width, height); - - // TODO: rely on builder.setHidden(hidden) instead of show and setAlpha when b/138459974 is - // fixed. + t.setPosition(leash, x, y); t.show(leash); t.setAlpha(leash, hidden ? 0 : 1); @@ -489,7 +507,8 @@ class SurfaceAnimator { void commitPendingTransaction(); /** - * Called when the was created. + * Called when the animation leash is created. Note that this is also called by + * {@link SurfaceFreezer}, so this doesn't mean we're about to start animating. * * @param t The transaction to use to apply any necessary changes. * @param leash The leash that was created. @@ -497,6 +516,14 @@ class SurfaceAnimator { void onAnimationLeashCreated(Transaction t, SurfaceControl leash); /** + * Called when the animator is about to start animating the leash. + * + * @param t The transaction to use to apply any necessary changes. + * @param leash The leash that was created. + */ + default void onLeashAnimationStarting(Transaction t, SurfaceControl leash) { } + + /** * Called when the leash is being destroyed, or when the leash is being transferred to * another SurfaceAnimator. * diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java new file mode 100644 index 000000000000..20435ea7dfaf --- /dev/null +++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java @@ -0,0 +1,245 @@ +/* + * 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.server.wm; + +import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS; +import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.GraphicBuffer; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.view.Surface; +import android.view.SurfaceControl; + +import com.android.server.protolog.common.ProtoLog; + +import java.util.function.Supplier; + +/** + * This class handles "freezing" of an Animatable. The Animatable in question should implement + * Freezable. + * + * The point of this is to enable WindowContainers to each be capable of freezing themselves. + * Freezing means taking a snapshot and placing it above everything in the sub-hierarchy. + * The "placing above" requires that a parent surface be inserted above the target surface so that + * the target surface and the snapshot are siblings. + * + * The overall flow for a transition using this would be: + * 1. Set transition and record animatable in mChangingApps + * 2. Call {@link #freeze} to set-up the leashes and cover with a snapshot. + * 3. When transition participants are ready, start SurfaceAnimator with this as a parameter + * 4. SurfaceAnimator will then {@link #takeLeashForAnimation} instead of creating another leash. + * 5. The animation system should eventually clean this up via {@link #unfreeze}. + */ +class SurfaceFreezer { + + private final Freezable mAnimatable; + private final WindowManagerService mWmService; + private SurfaceControl mLeash; + Snapshot mSnapshot = null; + final Rect mFreezeBounds = new Rect(); + + /** + * @param animatable The object to animate. + */ + SurfaceFreezer(Freezable animatable, WindowManagerService service) { + mAnimatable = animatable; + mWmService = service; + } + + /** + * Freeze the target surface. This is done by creating a leash (inserting a parent surface + * above the target surface) and then taking a snapshot and placing it over the target surface. + * + * @param startBounds The original bounds (on screen) of the surface we are snapshotting. + */ + void freeze(SurfaceControl.Transaction t, Rect startBounds) { + mFreezeBounds.set(startBounds); + + mLeash = SurfaceAnimator.createAnimationLeash(mAnimatable, mAnimatable.getSurfaceControl(), + t, startBounds.width(), startBounds.height(), startBounds.left, startBounds.top, + false /* hidden */); + mAnimatable.onAnimationLeashCreated(t, mLeash); + + SurfaceControl freezeTarget = mAnimatable.getFreezeSnapshotTarget(); + if (freezeTarget != null) { + GraphicBuffer snapshot = createSnapshotBuffer(freezeTarget, startBounds); + if (snapshot != null) { + mSnapshot = new Snapshot(mWmService.mSurfaceFactory, t, snapshot, mLeash); + } + } + } + + /** + * Used by {@link SurfaceAnimator}. This "transfers" the leash to be used for animation. + * By transferring the leash, this will no longer try to clean-up the leash when finished. + */ + SurfaceControl takeLeashForAnimation() { + SurfaceControl out = mLeash; + mLeash = null; + return out; + } + + /** + * Clean-up the snapshot and remove leash. If the leash was taken, this just cleans-up the + * snapshot. + */ + void unfreeze(SurfaceControl.Transaction t) { + if (mSnapshot != null) { + mSnapshot.destroy(t); + } + if (mLeash == null) { + return; + } + SurfaceControl leash = mLeash; + mLeash = null; + final boolean scheduleAnim = SurfaceAnimator.removeLeash(t, mAnimatable, leash, + false /* destroy */); + if (scheduleAnim) { + mWmService.scheduleAnimationLocked(); + } + } + + boolean hasLeash() { + return mLeash != null; + } + + private static GraphicBuffer createSnapshotBuffer(@NonNull SurfaceControl target, + @Nullable Rect bounds) { + Rect cropBounds = null; + if (bounds != null) { + cropBounds = new Rect(bounds); + cropBounds.offsetTo(0, 0); + } + final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer = + SurfaceControl.captureLayers( + target, cropBounds, 1.f /* frameScale */, PixelFormat.RGBA_8888); + final GraphicBuffer buffer = screenshotBuffer != null ? screenshotBuffer.getGraphicBuffer() + : null; + if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) { + return null; + } + return buffer; + } + + class Snapshot { + private SurfaceControl mSurfaceControl; + private AnimationAdapter mAnimation; + private SurfaceAnimator.OnAnimationFinishedCallback mFinishedCallback; + + /** + * @param t Transaction to create the thumbnail in. + * @param thumbnailHeader A thumbnail or placeholder for thumbnail to initialize with. + */ + Snapshot(Supplier<Surface> surfaceFactory, SurfaceControl.Transaction t, + GraphicBuffer thumbnailHeader, SurfaceControl parent) { + Surface drawSurface = surfaceFactory.get(); + // We can't use a delegating constructor since we need to + // reference this::onAnimationFinished + final int width = thumbnailHeader.getWidth(); + final int height = thumbnailHeader.getHeight(); + + mSurfaceControl = mAnimatable.makeAnimationLeash() + .setName("snapshot anim: " + mAnimatable.toString()) + .setBufferSize(width, height) + .setFormat(PixelFormat.TRANSLUCENT) + .setParent(parent) + .build(); + + ProtoLog.i(WM_SHOW_TRANSACTIONS, " THUMBNAIL %s: CREATE", mSurfaceControl); + + // Transfer the thumbnail to the surface + drawSurface.copyFrom(mSurfaceControl); + drawSurface.attachAndQueueBuffer(thumbnailHeader); + drawSurface.release(); + t.show(mSurfaceControl); + + // We parent the thumbnail to the container, and just place it on top of anything else + // in the container. + t.setLayer(mSurfaceControl, Integer.MAX_VALUE); + } + + void destroy(SurfaceControl.Transaction t) { + if (mSurfaceControl == null) { + return; + } + t.remove(mSurfaceControl); + mSurfaceControl = null; + } + + /** + * Starts an animation. + * + * @param anim The object that bridges the controller, {@link SurfaceAnimator}, with the + * component responsible for running the animation. It runs the animation with + * {@link AnimationAdapter#startAnimation} once the hierarchy with + * the Leash has been set up. + * @param animationFinishedCallback The callback being triggered when the animation + * finishes. + */ + void startAnimation(SurfaceControl.Transaction t, AnimationAdapter anim, int type, + @Nullable SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback) { + cancelAnimation(t, true /* restarting */); + mAnimation = anim; + mFinishedCallback = animationFinishedCallback; + if (mSurfaceControl == null) { + cancelAnimation(t, false /* restarting */); + return; + } + mAnimation.startAnimation(mSurfaceControl, t, type, animationFinishedCallback); + } + + /** + * Cancels the animation, and resets the leash. + * + * @param t The transaction to use for all cancelling surface operations. + * @param restarting Whether we are restarting the animation. + */ + private void cancelAnimation(SurfaceControl.Transaction t, boolean restarting) { + final SurfaceControl leash = mSurfaceControl; + final AnimationAdapter animation = mAnimation; + final SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback = + mFinishedCallback; + mAnimation = null; + mFinishedCallback = null; + if (animation != null) { + animation.onAnimationCancelled(leash); + if (!restarting) { + if (animationFinishedCallback != null) { + animationFinishedCallback.onAnimationFinished( + ANIMATION_TYPE_APP_TRANSITION, animation); + } + } + } + if (!restarting) { + // TODO: do we need to destroy? + destroy(t); + } + } + } + + /** freezable */ + public interface Freezable extends SurfaceAnimator.Animatable { + /** + * @return The surface to take a snapshot of. If this returns {@code null}, no snapshot + * will be generated (but the rest of the freezing logic will still happen). + */ + @Nullable SurfaceControl getFreezeSnapshotTarget(); + } +} diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index ecfac1bc7b6e..27acb2356585 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -58,6 +58,7 @@ import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.SurfaceControl.METADATA_TASK_ID; +import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE; import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP; import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP; @@ -78,6 +79,9 @@ 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.TAG_STACK; import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; +import static com.android.server.wm.IdentifierProto.HASH_CODE; +import static com.android.server.wm.IdentifierProto.TITLE; +import static com.android.server.wm.IdentifierProto.USER_ID; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; @@ -111,7 +115,6 @@ import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.drawable.Icon; import android.os.Debug; import android.os.IBinder; import android.os.RemoteException; @@ -120,11 +123,13 @@ import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; import android.service.voice.IVoiceInteractionSession; -import android.text.TextUtils; +import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.Slog; +import android.util.proto.ProtoOutputStream; import android.view.DisplayInfo; import android.view.ITaskOrganizer; +import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.Surface; import android.view.SurfaceControl; @@ -1886,6 +1891,8 @@ class Task extends WindowContainer<WindowContainer> { .setBounds(mLastNonFullscreenBounds); } + final int prevWinMode = getWindowingMode(); + mTmpPrevBounds.set(getBounds()); final boolean wasInMultiWindowMode = inMultiWindowMode(); super.onConfigurationChanged(newParentConfig); if (wasInMultiWindowMode != inMultiWindowMode()) { @@ -1893,6 +1900,12 @@ class Task extends WindowContainer<WindowContainer> { updateShadowsRadius(isFocused(), getPendingTransaction()); } + final int newWinMode = getWindowingMode(); + if ((prevWinMode != newWinMode) && (mDisplayContent != null) + && shouldStartChangeTransition(prevWinMode, newWinMode)) { + initializeChangeTransition(mTmpPrevBounds); + } + // If the configuration supports persistent bounds (eg. Freeform), keep track of the // current (non-fullscreen) bounds for persistence. if (getWindowConfiguration().persistTaskBounds()) { @@ -1907,6 +1920,63 @@ class Task extends WindowContainer<WindowContainer> { } /** + * Initializes a change transition. See {@link SurfaceFreezer} for more information. + */ + private void initializeChangeTransition(Rect startBounds) { + mDisplayContent.prepareAppTransition(TRANSIT_TASK_CHANGE_WINDOWING_MODE, + false /* alwaysKeepCurrent */, 0, false /* forceOverride */); + mDisplayContent.mChangingContainers.add(this); + + mSurfaceFreezer.freeze(getPendingTransaction(), startBounds); + } + + private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) { + if (mWmService.mDisableTransitionAnimation + || !isVisible() + || getDisplayContent().mAppTransition.isTransitionSet() + || getSurfaceControl() == null) { + return false; + } + // Only do an animation into and out-of freeform mode for now. Other mode + // transition animations are currently handled by system-ui. + return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM); + } + + @VisibleForTesting + boolean isInChangeTransition() { + return mSurfaceFreezer.hasLeash() || AppTransition.isChangeTransit(mTransit); + } + + @Override + public SurfaceControl getFreezeSnapshotTarget() { + final int transit = mDisplayContent.mAppTransition.getAppTransition(); + if (!AppTransition.isChangeTransit(transit)) { + return null; + } + // Skip creating snapshot if this transition is controlled by a remote animator which + // doesn't need it. + final ArraySet<Integer> activityTypes = new ArraySet<>(); + activityTypes.add(getActivityType()); + final RemoteAnimationAdapter adapter = + mDisplayContent.mAppTransitionController.getRemoteAnimationOverride( + this, transit, activityTypes); + if (adapter != null && !adapter.getChangeNeedsSnapshot()) { + return null; + } + return getSurfaceControl(); + } + + @Override + void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + proto.write(HASH_CODE, System.identityHashCode(this)); + proto.write(USER_ID, mUserId); + proto.write(TITLE, intent != null && intent.getComponent() != null + ? intent.getComponent().flattenToShortString() : "Task"); + proto.end(token); + } + + /** * Saves launching state if necessary so that we can launch the activity to its latest state. * It only saves state if this task has been shown to user and it's in fullscreen or freeform * mode on freeform displays. @@ -2038,18 +2108,6 @@ class Task extends WindowContainer<WindowContainer> { intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets); } - /** - * Asks docked-divider controller for the smallestwidthdp given bounds. - * @param bounds bounds to calculate smallestwidthdp for. - */ - private int getSmallestScreenWidthDpForDockedBounds(Rect bounds) { - DisplayContent dc = getDisplayContent(); - if (dc != null) { - return dc.getDockedDividerController().getSmallestWidthDpForBounds(bounds); - } - return Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; - } - void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Configuration parentConfig) { computeConfigResourceOverrides(inOutConfig, parentConfig, null /* compatInsets */); @@ -2616,12 +2674,22 @@ class Task extends WindowContainer<WindowContainer> { if (!isRootTask) { adjustBoundsForDisplayChangeIfNeeded(dc); } + final DisplayContent prevDc = mDisplayContent; super.onDisplayChanged(dc); if (!isRootTask) { final int displayId = (dc != null) ? dc.getDisplayId() : INVALID_DISPLAY; mWmService.mAtmService.getTaskChangeNotificationController().notifyTaskDisplayChanged( mTaskId, displayId); } + if (prevDc != null && prevDc.mChangingContainers.remove(this)) { + // This gets called *after* this has been reparented to the new display. + // That reparenting resulted in this window changing modes (eg. FREEFORM -> FULLSCREEN), + // so this token is now "frozen" while waiting for the animation to start on prevDc + // (which will be cancelled since the window is no-longer a child). However, since this + // is no longer a child of prevDc, this won't be notified of the cancelled animation, + // so we need to cancel the change transition here. + mSurfaceFreezer.unfreeze(getPendingTransaction()); + } } /** @@ -2910,8 +2978,7 @@ class Task extends WindowContainer<WindowContainer> { * we will have a jump at the end. */ boolean isFloating() { - return getWindowConfiguration().tasksAreFloating() - && !getStack().isAnimatingBoundsToFullscreen() && !mPreserveNonFloatingState; + return getWindowConfiguration().tasksAreFloating() && !mPreserveNonFloatingState; } /** @@ -3013,15 +3080,6 @@ class Task extends WindowContainer<WindowContainer> { return forAllTasks((t) -> { return t != this && t.isTaskAnimating(); }); } - /** - * @return {@code true} if changing app transition is running. - */ - @Override - boolean isChangingAppTransition() { - final ActivityRecord activity = getTopVisibleActivity(); - return activity != null && getDisplayContent().mChangingApps.contains(activity); - } - @Override RemoteAnimationTarget createRemoteAnimationTarget( RemoteAnimationController.RemoteAnimationRecord record) { diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index f715d8f3529b..96a9127344e8 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -37,7 +37,6 @@ class TaskChangeNotificationController { private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 2; private static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 3; private static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4; - private static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 5; private static final int NOTIFY_FORCED_RESIZABLE_MSG = 6; private static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 7; private static final int NOTIFY_TASK_ADDED_LISTENERS_MSG = 8; @@ -48,7 +47,6 @@ class TaskChangeNotificationController { private static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13; private static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14; private static final int NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG = 15; - private static final int NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG = 16; private static final int NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG = 17; private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18; private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG = 19; @@ -124,14 +122,6 @@ class TaskChangeNotificationController { l.onPinnedActivityRestartAttempt(m.arg1 != 0); }; - private final TaskStackConsumer mNotifyPinnedStackAnimationStarted = (l, m) -> { - l.onPinnedStackAnimationStarted(); - }; - - private final TaskStackConsumer mNotifyPinnedStackAnimationEnded = (l, m) -> { - l.onPinnedStackAnimationEnded(); - }; - private final TaskStackConsumer mNotifyActivityForcedResizable = (l, m) -> { l.onActivityForcedResizable((String) m.obj, m.arg1, m.arg2); }; @@ -233,12 +223,6 @@ class TaskChangeNotificationController { case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG: forAllRemoteListeners(mNotifyPinnedActivityRestartAttempt, msg); break; - case NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG: - forAllRemoteListeners(mNotifyPinnedStackAnimationStarted, msg); - break; - case NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG: - forAllRemoteListeners(mNotifyPinnedStackAnimationEnded, msg); - break; case NOTIFY_FORCED_RESIZABLE_MSG: forAllRemoteListeners(mNotifyActivityForcedResizable, msg); break; @@ -386,24 +370,6 @@ class TaskChangeNotificationController { msg.sendToTarget(); } - /** Notifies all listeners when the pinned stack animation starts. */ - void notifyPinnedStackAnimationStarted() { - mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG); - final Message msg = - mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG); - forAllLocalListeners(mNotifyPinnedStackAnimationStarted, msg); - msg.sendToTarget(); - } - - /** Notifies all listeners when the pinned stack animation ends. */ - void notifyPinnedStackAnimationEnded() { - mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG); - final Message msg = - mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG); - forAllLocalListeners(mNotifyPinnedStackAnimationEnded, msg); - msg.sendToTarget(); - } - void notifyActivityDismissingDockedStack() { mHandler.removeMessages(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG); final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG); diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 6caa27c7fa6f..0f1e623a38f1 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -306,7 +306,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub task.fillTaskInfo(mTmpTaskInfo); boolean changed = lastInfo == null || mTmpTaskInfo.topActivityType != lastInfo.topActivityType - || mTmpTaskInfo.isResizable() != lastInfo.isResizable(); + || mTmpTaskInfo.isResizable() != lastInfo.isResizable() + || mTmpTaskInfo.pictureInPictureParams != lastInfo.pictureInPictureParams; if (!(changed || force)) { return; } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 5c73f92ee6cd..f83b0522846c 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -18,19 +18,18 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; -import static com.android.server.wm.TaskSnapshotPersister.DISABLE_HIGH_RES_BITMAPS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityManager; import android.app.ActivityManager.TaskSnapshot; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.RenderNode; @@ -89,14 +88,6 @@ class TaskSnapshotController { @VisibleForTesting static final int SNAPSHOT_MODE_NONE = 2; - /** - * Constant for <code>scaleFactor</code> when calling {@link #snapshotTask} which is - * interpreted as using the most appropriate scale ratio for the system. - * This may yield a smaller ratio on low memory devices. - */ - @VisibleForTesting - static final float SNAPSHOT_SCALE_AUTO = -1f; - private final WindowManagerService mService; private final TaskSnapshotCache mCache; @@ -229,7 +220,7 @@ class TaskSnapshotController { @Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk, boolean isLowResolution) { return mCache.getSnapshot(taskId, userId, restoreFromDisk, isLowResolution - || DISABLE_HIGH_RES_BITMAPS); + && mPersister.enableLowResSnapshots()); } /** @@ -273,8 +264,6 @@ class TaskSnapshotController { * information from the task and populates the builder. * * @param task the task to capture - * @param scaleFraction the scale fraction between 0-1.0, or {@link #SNAPSHOT_SCALE_AUTO} - * to automatically select * @param pixelFormat the desired pixel format, or {@link PixelFormat#UNKNOWN} to * automatically select * @param builder the snapshot builder to populate @@ -282,8 +271,7 @@ class TaskSnapshotController { * @return true if the state of the task is ok to proceed */ @VisibleForTesting - boolean prepareTaskSnapshot(Task task, float scaleFraction, int pixelFormat, - TaskSnapshot.Builder builder) { + boolean prepareTaskSnapshot(Task task, int pixelFormat, TaskSnapshot.Builder builder) { if (!mService.mPolicy.isScreenOn()) { if (DEBUG_SCREENSHOT) { Slog.i(TAG_WM, "Attempted to take screenshot while display was off."); @@ -314,18 +302,6 @@ class TaskSnapshotController { builder.setId(System.currentTimeMillis()); builder.setContentInsets(getInsets(mainWindow)); - final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic(); - - if (scaleFraction == SNAPSHOT_SCALE_AUTO) { - builder.setScaleFraction(isLowRamDevice - ? mPersister.getLowResScale() - : mHighResTaskSnapshotScale); - builder.setIsLowResolution(isLowRamDevice); - } else { - builder.setScaleFraction(scaleFraction); - builder.setIsLowResolution(scaleFraction < 1.0f); - } - final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE; final boolean isShowWallpaper = (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) != 0; @@ -351,13 +327,23 @@ class TaskSnapshotController { @Nullable SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task, + TaskSnapshot.Builder builder) { + Point taskSize = new Point(); + final SurfaceControl.ScreenshotGraphicBuffer taskSnapshot = createTaskSnapshot(task, + mHighResTaskSnapshotScale, builder.getPixelFormat(), taskSize); + builder.setTaskSize(taskSize); + return taskSnapshot; + } + + @Nullable + SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task, float scaleFraction) { - return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888); + return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888, null); } @Nullable SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task, - float scaleFraction, int pixelFormat) { + float scaleFraction, int pixelFormat, Point outTaskSize) { if (task.getSurfaceControl() == null) { if (DEBUG_SCREENSHOT) { Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + task); @@ -369,6 +355,10 @@ class TaskSnapshotController { final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer = SurfaceControl.captureLayers( task.getSurfaceControl(), mTmpRect, scaleFraction, pixelFormat); + if (outTaskSize != null) { + outTaskSize.x = mTmpRect.width(); + outTaskSize.y = mTmpRect.height(); + } final GraphicBuffer buffer = screenshotBuffer != null ? screenshotBuffer.getGraphicBuffer() : null; if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) { @@ -379,21 +369,20 @@ class TaskSnapshotController { @Nullable TaskSnapshot snapshotTask(Task task) { - return snapshotTask(task, SNAPSHOT_SCALE_AUTO, PixelFormat.UNKNOWN); + return snapshotTask(task, PixelFormat.UNKNOWN); } @Nullable - TaskSnapshot snapshotTask(Task task, float scaleFraction, int pixelFormat) { + TaskSnapshot snapshotTask(Task task, int pixelFormat) { TaskSnapshot.Builder builder = new TaskSnapshot.Builder(); - if (!prepareTaskSnapshot(task, scaleFraction, pixelFormat, builder)) { + if (!prepareTaskSnapshot(task, pixelFormat, builder)) { // Failed some pre-req. Has been logged. return null; } final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer = - createTaskSnapshot(task, builder.getScaleFraction(), - builder.getPixelFormat()); + createTaskSnapshot(task, builder); if (screenshotBuffer == null) { // Failed to acquire image. Has been logged. @@ -472,8 +461,10 @@ class TaskSnapshotController { final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags, attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(), mHighResTaskSnapshotScale, mainWindow.getRequestedInsetsState()); - final int width = (int) (task.getBounds().width() * mHighResTaskSnapshotScale); - final int height = (int) (task.getBounds().height() * mHighResTaskSnapshotScale); + final int taskWidth = task.getBounds().width(); + final int taskHeight = task.getBounds().height(); + final int width = (int) (taskWidth * mHighResTaskSnapshotScale); + final int height = (int) (taskHeight * mHighResTaskSnapshotScale); final RenderNode node = RenderNode.create("TaskSnapshotController", null); node.setLeftTopRightBottom(0, 0, width, height); @@ -494,9 +485,9 @@ class TaskSnapshotController { System.currentTimeMillis() /* id */, topChild.mActivityComponent, hwBitmap.createGraphicBufferHandle(), hwBitmap.getColorSpace(), mainWindow.getConfiguration().orientation, - mainWindow.getWindowConfiguration().getRotation(), - getInsets(mainWindow), ActivityManager.isLowRamDeviceStatic() /* isLowResolution */, - mHighResTaskSnapshotScale, false /* isRealSnapshot */, task.getWindowingMode(), + mainWindow.getWindowConfiguration().getRotation(), new Point(taskWidth, taskHeight), + getInsets(mainWindow), false /* isLowResolution */, + false /* isRealSnapshot */, task.getWindowingMode(), getSystemUiVisibility(task), false); } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java index 01f3427d78e1..c20ce5f40bc2 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.app.ActivityManager; import android.app.ActivityManager.TaskSnapshot; import android.content.ComponentName; import android.graphics.Bitmap; @@ -26,6 +27,7 @@ import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; import android.graphics.GraphicBuffer; +import android.graphics.Point; import android.graphics.Rect; import android.util.Slog; @@ -52,28 +54,110 @@ class TaskSnapshotLoader { mPersister = persister; } + static class PreRLegacySnapshotConfig { + /** + * If isPreRLegacy is {@code true}, specifies the scale the snapshot was taken at + */ + final float mScale; + + /** + * If {@code true}, always load *_reduced.jpg file, no matter what was requested + */ + final boolean mForceLoadReducedJpeg; + + PreRLegacySnapshotConfig(float scale, boolean forceLoadReducedJpeg) { + mScale = scale; + mForceLoadReducedJpeg = forceLoadReducedJpeg; + } + } + + /** + * When device is upgraded, we might be loading a legacy snapshot. In those cases, + * restore the scale based on how it was configured historically. See history of + * TaskSnapshotPersister for more information. + * + * | low_ram=false | low_ram=true + * +------------------------------------------------------------------------------+ + * O | *.jpg = 100%, *_reduced.jpg = 50% | + * | +-----------------------------------------| + * P | | *.jpg = NONE, *_reduced.jpg = 60% | + * +------------------------------------+-----------------------------------------+ + * Q | *.jpg = proto.scale, | *.jpg = NONE, | + * | *_reduced.jpg = 50% * proto.scale | *_reduced.jpg = proto.scale | + * +------------------------------------+-----------------------------------------+ + * + * @return null if Android R, otherwise a PreRLegacySnapshotConfig object + */ + PreRLegacySnapshotConfig getLegacySnapshotConfig(int taskWidth, float legacyScale, + boolean highResFileExists, boolean loadLowResolutionBitmap) { + float preRLegacyScale = 0; + boolean forceLoadReducedJpeg = false; + boolean isPreRLegacySnapshot = (taskWidth == 0); + if (!isPreRLegacySnapshot) { + return null; + } + final boolean isPreQLegacyProto = isPreRLegacySnapshot + && (Float.compare(legacyScale, 0f) == 0); + + if (isPreQLegacyProto) { + // Android O or Android P + if (ActivityManager.isLowRamDeviceStatic() && !highResFileExists) { + // Android P w/ low_ram=true + preRLegacyScale = 0.6f; + // Force bitmapFile to always be *_reduced.jpg + forceLoadReducedJpeg = true; + } else { + // Android O, OR Android P w/ low_ram=false + preRLegacyScale = loadLowResolutionBitmap ? 0.5f : 1.0f; + } + } else if (isPreRLegacySnapshot) { + // If not pre-Q but is pre-R, then it must be Android Q + if (ActivityManager.isLowRamDeviceStatic()) { + preRLegacyScale = legacyScale; + // Force bitmapFile to always be *_reduced.jpg + forceLoadReducedJpeg = true; + } else { + preRLegacyScale = + loadLowResolutionBitmap ? 0.5f * legacyScale : legacyScale; + } + } + return new PreRLegacySnapshotConfig(preRLegacyScale, forceLoadReducedJpeg); + } + /** * Loads a task from the disk. * <p> * Do not hold the window manager lock when calling this method, as we directly read data from * disk here, which might be slow. * - * @param taskId The id of the task to load. - * @param userId The id of the user the task belonged to. - * @param isLowResolution Whether to load a reduced resolution version of the snapshot. + * @param taskId The id of the task to load. + * @param userId The id of the user the task belonged to. + * @param loadLowResolutionBitmap Whether to load a low resolution resolution version of the + * snapshot. * @return The loaded {@link TaskSnapshot} or {@code null} if it couldn't be loaded. */ - TaskSnapshot loadTask(int taskId, int userId, boolean isLowResolution) { + TaskSnapshot loadTask(int taskId, int userId, boolean loadLowResolutionBitmap) { final File protoFile = mPersister.getProtoFile(taskId, userId); - final File bitmapFile = isLowResolution - ? mPersister.getLowResolutionBitmapFile(taskId, userId) - : mPersister.getHighResolutionBitmapFile(taskId, userId); - if (bitmapFile == null || !protoFile.exists() || !bitmapFile.exists()) { + if (!protoFile.exists()) { return null; } try { final byte[] bytes = Files.readAllBytes(protoFile.toPath()); final TaskSnapshotProto proto = TaskSnapshotProto.parseFrom(bytes); + final File highResBitmap = mPersister.getHighResolutionBitmapFile(taskId, userId); + + PreRLegacySnapshotConfig legacyConfig = getLegacySnapshotConfig(proto.taskWidth, + proto.legacyScale, highResBitmap.exists(), loadLowResolutionBitmap); + + boolean forceLoadReducedJpeg = + legacyConfig != null && legacyConfig.mForceLoadReducedJpeg; + File bitmapFile = (loadLowResolutionBitmap || forceLoadReducedJpeg) + ? mPersister.getLowResolutionBitmapFile(taskId, userId) : highResBitmap; + + if (!bitmapFile.exists()) { + return null; + } + final Options options = new Options(); options.inPreferredConfig = mPersister.use16BitFormat() && !proto.isTranslucent ? Config.RGB_565 @@ -99,13 +183,20 @@ class TaskSnapshotLoader { final ComponentName topActivityComponent = ComponentName.unflattenFromString( proto.topActivityComponent); - // For legacy snapshots, restore the scale based on the reduced resolution state - final float legacyScale = isLowResolution ? mPersister.getLowResScale() : 1f; - final float scale = Float.compare(proto.scale, 0f) != 0 ? proto.scale : legacyScale; - return new TaskSnapshot(proto.id, topActivityComponent, buffer, hwBitmap.getColorSpace(), - proto.orientation, proto.rotation, + + Point taskSize; + if (legacyConfig != null) { + int taskWidth = (int) ((float) hwBitmap.getWidth() / legacyConfig.mScale); + int taskHeight = (int) ((float) hwBitmap.getHeight() / legacyConfig.mScale); + taskSize = new Point(taskWidth, taskHeight); + } else { + taskSize = new Point(proto.taskWidth, proto.taskHeight); + } + + return new TaskSnapshot(proto.id, topActivityComponent, buffer, + hwBitmap.getColorSpace(), proto.orientation, proto.rotation, taskSize, new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom), - isLowResolution, scale, proto.isRealSnapshot, proto.windowingMode, + loadLowResolutionBitmap, proto.isRealSnapshot, proto.windowingMode, proto.systemUiVisibility, proto.isTranslucent); } catch (IOException e) { Slog.w(TAG, "Unable to load task snapshot data for taskId=" + taskId); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java index 31212b8a6bd9..164d3e055e94 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java @@ -21,8 +21,8 @@ import static android.graphics.Bitmap.CompressFormat.JPEG; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.annotation.NonNull; import android.annotation.TestApi; -import android.app.ActivityManager; import android.app.ActivityManager.TaskSnapshot; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; @@ -52,8 +52,6 @@ class TaskSnapshotPersister { private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotPersister" : TAG_WM; private static final String SNAPSHOTS_DIRNAME = "snapshots"; private static final String LOW_RES_FILE_POSTFIX = "_reduced"; - private static final float LOW_RAM_REDUCED_SCALE = .8f; - static final boolean DISABLE_HIGH_RES_BITMAPS = ActivityManager.isLowRamDeviceStatic(); private static final long DELAY_MS = 100; private static final int QUALITY = 95; private static final String PROTO_EXTENSION = ".proto"; @@ -71,7 +69,8 @@ class TaskSnapshotPersister { private boolean mStarted; private final Object mLock = new Object(); private final DirectoryResolver mDirectoryResolver; - private final float mLowResScale; + private final float mLowResScaleFactor; + private boolean mEnableLowResSnapshots; private final boolean mUse16BitFormat; /** @@ -83,13 +82,29 @@ class TaskSnapshotPersister { TaskSnapshotPersister(WindowManagerService service, DirectoryResolver resolver) { mDirectoryResolver = resolver; + final float highResTaskSnapshotScale = service.mContext.getResources().getFloat( + com.android.internal.R.dimen.config_highResTaskSnapshotScale); + final float lowResTaskSnapshotScale = service.mContext.getResources().getFloat( + com.android.internal.R.dimen.config_lowResTaskSnapshotScale); - if (ActivityManager.isLowRamDeviceStatic()) { - mLowResScale = LOW_RAM_REDUCED_SCALE; + if (lowResTaskSnapshotScale < 0 || 1 <= lowResTaskSnapshotScale) { + throw new RuntimeException("Low-res scale must be between 0 and 1"); + } + if (highResTaskSnapshotScale <= 0 || 1 < highResTaskSnapshotScale) { + throw new RuntimeException("High-res scale must be between 0 and 1"); + } + if (highResTaskSnapshotScale <= lowResTaskSnapshotScale) { + throw new RuntimeException("High-res scale must be greater than low-res scale"); + } + + if (lowResTaskSnapshotScale > 0) { + mLowResScaleFactor = lowResTaskSnapshotScale / highResTaskSnapshotScale; + setEnableLowResSnapshots(true); } else { - mLowResScale = service.mContext.getResources().getFloat( - com.android.internal.R.dimen.config_lowResTaskSnapshotScale); + mLowResScaleFactor = 0; + setEnableLowResSnapshots(false); } + mUse16BitFormat = service.mContext.getResources().getBoolean( com.android.internal.R.bool.config_use16BitTaskSnapshotPixelFormat); } @@ -155,13 +170,16 @@ class TaskSnapshotPersister { } } + boolean enableLowResSnapshots() { + return mEnableLowResSnapshots; + } + /** - * Gets the scaling the persister uses for low resolution task snapshots. - * - * @return the lowResBitmap scale of task snapshots when they are set to be low res + * Not to be used. Only here for testing. */ - float getLowResScale() { - return mLowResScale; + @VisibleForTesting + void setEnableLowResSnapshots(boolean enabled) { + mEnableLowResSnapshots = enabled; } /** @@ -213,14 +231,10 @@ class TaskSnapshotPersister { } File getHighResolutionBitmapFile(int taskId, int userId) { - // Full sized bitmaps are disabled on low ram devices - if (DISABLE_HIGH_RES_BITMAPS) { - Slog.wtf(TAG, "This device does not support full sized resolution bitmaps."); - return null; - } return new File(getDirectory(userId), taskId + BITMAP_EXTENSION); } + @NonNull File getLowResolutionBitmapFile(int taskId, int userId) { return new File(getDirectory(userId), taskId + LOW_RES_FILE_POSTFIX + BITMAP_EXTENSION); } @@ -234,11 +248,11 @@ class TaskSnapshotPersister { final File protoFile = getProtoFile(taskId, userId); final File bitmapLowResFile = getLowResolutionBitmapFile(taskId, userId); protoFile.delete(); - bitmapLowResFile.delete(); - - // Low ram devices do not have a full sized file to delete - if (!DISABLE_HIGH_RES_BITMAPS) { - final File bitmapFile = getHighResolutionBitmapFile(taskId, userId); + if (bitmapLowResFile.exists()) { + bitmapLowResFile.delete(); + } + final File bitmapFile = getHighResolutionBitmapFile(taskId, userId); + if (bitmapFile.exists()) { bitmapFile.delete(); } } @@ -343,6 +357,8 @@ class TaskSnapshotPersister { final TaskSnapshotProto proto = new TaskSnapshotProto(); proto.orientation = mSnapshot.getOrientation(); proto.rotation = mSnapshot.getRotation(); + proto.taskWidth = mSnapshot.getTaskSize().x; + proto.taskHeight = mSnapshot.getTaskSize().y; proto.insetLeft = mSnapshot.getContentInsets().left; proto.insetTop = mSnapshot.getContentInsets().top; proto.insetRight = mSnapshot.getContentInsets().right; @@ -352,7 +368,6 @@ class TaskSnapshotPersister { proto.systemUiVisibility = mSnapshot.getSystemUiVisibility(); proto.isTranslucent = mSnapshot.isTranslucent(); proto.topActivityComponent = mSnapshot.getTopActivityComponent().flattenToString(); - proto.scale = mSnapshot.getScale(); proto.id = mSnapshot.getId(); final byte[] bytes = TaskSnapshotProto.toByteArray(proto); final File file = getProtoFile(mTaskId, mUserId); @@ -379,39 +394,38 @@ class TaskSnapshotPersister { } final Bitmap swBitmap = bitmap.copy(Config.ARGB_8888, false /* isMutable */); - final Bitmap lowResBitmap = mSnapshot.isLowResolution() - ? swBitmap - : Bitmap.createScaledBitmap(swBitmap, - (int) (bitmap.getWidth() * mLowResScale), - (int) (bitmap.getHeight() * mLowResScale), true /* filter */); - final File lowResFile = getLowResolutionBitmapFile(mTaskId, mUserId); + final File file = getHighResolutionBitmapFile(mTaskId, mUserId); try { - FileOutputStream lowResFos = new FileOutputStream(lowResFile); - lowResBitmap.compress(JPEG, QUALITY, lowResFos); - lowResFos.close(); + FileOutputStream fos = new FileOutputStream(file); + swBitmap.compress(JPEG, QUALITY, fos); + fos.close(); } catch (IOException e) { - Slog.e(TAG, "Unable to open " + lowResFile + " for persisting.", e); + Slog.e(TAG, "Unable to open " + file + " for persisting.", e); return false; } - lowResBitmap.recycle(); - // For snapshots with lowResBitmap resolution, do not create or save full sized bitmaps - if (mSnapshot.isLowResolution()) { + if (!enableLowResSnapshots()) { swBitmap.recycle(); return true; } - final File file = getHighResolutionBitmapFile(mTaskId, mUserId); + final Bitmap lowResBitmap = Bitmap.createScaledBitmap(swBitmap, + (int) (bitmap.getWidth() * mLowResScaleFactor), + (int) (bitmap.getHeight() * mLowResScaleFactor), true /* filter */); + swBitmap.recycle(); + + final File lowResFile = getLowResolutionBitmapFile(mTaskId, mUserId); try { - FileOutputStream fos = new FileOutputStream(file); - swBitmap.compress(JPEG, QUALITY, fos); - fos.close(); + FileOutputStream lowResFos = new FileOutputStream(lowResFile); + lowResBitmap.compress(JPEG, QUALITY, lowResFos); + lowResFos.close(); } catch (IOException e) { - Slog.e(TAG, "Unable to open " + file + " for persisting.", e); + Slog.e(TAG, "Unable to open " + lowResFile + " for persisting.", e); return false; } - swBitmap.recycle(); + lowResBitmap.recycle(); + return true; } } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index f4e42455087d..eb005e0f7eda 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -36,6 +36,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; + import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES; import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES; import static com.android.internal.policy.DecorView.getColorViewLeftInset; @@ -53,9 +54,11 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.GraphicBuffer; +import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.RectF; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -131,6 +134,8 @@ class TaskSnapshotSurface implements StartingSurface { private final Rect mContentInsets = new Rect(); private final Rect mFrame = new Rect(); private TaskSnapshot mSnapshot; + private final RectF mTmpSnapshotSize = new RectF(); + private final RectF mTmpDstFrame = new RectF(); private final CharSequence mTitle; private boolean mHasDrawn; private long mShownTime; @@ -141,6 +146,8 @@ class TaskSnapshotSurface implements StartingSurface { @VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter; private final int mOrientationOnCreation; private final SurfaceControl.Transaction mTransaction; + private final Matrix mSnapshotMatrix = new Matrix(); + private final float[] mTmpFloat9 = new float[9]; static TaskSnapshotSurface create(WindowManagerService service, ActivityRecord activity, TaskSnapshot snapshot) { @@ -365,13 +372,17 @@ class TaskSnapshotSurface implements StartingSurface { frame = calculateSnapshotFrame(crop); mTransaction.setWindowCrop(mChildSurfaceControl, crop); mTransaction.setPosition(mChildSurfaceControl, frame.left, frame.top); + mTmpDstFrame.set(frame); } else { frame = null; + mTmpDstFrame.set(mFrame); } // Scale the mismatch dimensions to fill the task bounds - final float scale = 1 / mSnapshot.getScale(); - mTransaction.setMatrix(mChildSurfaceControl, scale, 0, 0, scale); + mTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight()); + mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL); + mTransaction.setMatrix(mChildSurfaceControl, mSnapshotMatrix, mTmpFloat9); + mTransaction.apply(); surface.attachAndQueueBufferWithColorSpace(buffer, mSnapshot.getColorSpace()); surface.release(); @@ -395,13 +406,17 @@ class TaskSnapshotSurface implements StartingSurface { rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight()); final Rect insets = mSnapshot.getContentInsets(); + final float scaleX = (float) mSnapshot.getSnapshot().getWidth() / mSnapshot.getTaskSize().x; + final float scaleY = + (float) mSnapshot.getSnapshot().getHeight() / mSnapshot.getTaskSize().y; + // Let's remove all system decorations except the status bar, but only if the task is at the // very top of the screen. final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0; - rect.inset((int) (insets.left * mSnapshot.getScale()), - isTop ? 0 : (int) (insets.top * mSnapshot.getScale()), - (int) (insets.right * mSnapshot.getScale()), - (int) (insets.bottom * mSnapshot.getScale())); + rect.inset((int) (insets.left * scaleX), + isTop ? 0 : (int) (insets.top * scaleY), + (int) (insets.right * scaleX), + (int) (insets.bottom * scaleY)); return rect; } @@ -412,14 +427,20 @@ class TaskSnapshotSurface implements StartingSurface { */ @VisibleForTesting Rect calculateSnapshotFrame(Rect crop) { - final Rect frame = new Rect(crop); - final float scale = mSnapshot.getScale(); + final float scaleX = (float) mSnapshot.getSnapshot().getWidth() / mSnapshot.getTaskSize().x; + final float scaleY = + (float) mSnapshot.getSnapshot().getHeight() / mSnapshot.getTaskSize().y; // Rescale the frame from snapshot to window coordinate space - frame.scale(1 / scale); + final Rect frame = new Rect( + (int) (crop.left / scaleX + 0.5f), + (int) (crop.top / scaleY + 0.5f), + (int) (crop.right / scaleX + 0.5f), + (int) (crop.bottom / scaleY + 0.5f) + ); // By default, offset it to to top/left corner - frame.offsetTo((int) (-crop.left / scale), (int) (-crop.top / scale)); + frame.offsetTo((int) (-crop.left / scaleX), (int) (-crop.top / scaleY)); // However, we also need to make space for the navigation bar on the left side. final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left, diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 137d122cd6d5..669eb7804d41 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -669,8 +669,7 @@ class WallpaperController { * Adjusts the wallpaper windows if the input display has a pending wallpaper layout or one of * the opening apps should be a wallpaper target. */ - void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<ActivityRecord> openingApps, - ArraySet<ActivityRecord> changingApps) { + void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<ActivityRecord> openingApps) { boolean adjust = false; if ((mDisplayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) { adjust = true; @@ -682,15 +681,6 @@ class WallpaperController { break; } } - if (!adjust) { - for (int i = changingApps.size() - 1; i >= 0; --i) { - final ActivityRecord activity = changingApps.valueAt(i); - if (activity.windowsCanBeWallpaperTarget()) { - adjust = true; - break; - } - } - } } if (adjust) { diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index fd91bc549f07..b6be386fa181 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -50,7 +50,6 @@ public class WindowAnimator { final WindowManagerPolicy mPolicy; /** Is any window animating? */ - private boolean mAnimating; private boolean mLastRootAnimating; final Choreographer.FrameCallback mAnimationFrameCallback; @@ -135,7 +134,6 @@ public class WindowAnimator { synchronized (mService.mGlobalLock) { mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS; mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE; - mAnimating = false; if (DEBUG_WINDOW_TRACE) { Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime); } @@ -160,17 +158,12 @@ public class WindowAnimator { final DisplayContent dc = mService.mRoot.getDisplayContent(displayId); dc.checkAppWindowsReadyToShow(); - orAnimating(dc.getDockedDividerController().animate(mCurrentTime)); if (accessibilityController != null) { accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId, mTransaction); } } - if (!mAnimating) { - cancelAnimation(); - } - if (mService.mWatermark != null) { mService.mWatermark.drawIfNeeded(); } @@ -220,7 +213,7 @@ public class WindowAnimator { executeAfterPrepareSurfacesRunnables(); if (DEBUG_WINDOW_TRACE) { - Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating + Slog.i(TAG, "!!! animate: exit" + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams) + " hasPendingLayoutChanges=" + hasPendingLayoutChanges); } @@ -302,10 +295,6 @@ public class WindowAnimator { private class DisplayContentsAnimator { } - boolean isAnimating() { - return mAnimating; - } - boolean isAnimationScheduled() { return mAnimationFrameCallbackScheduled; } @@ -314,14 +303,6 @@ public class WindowAnimator { return mChoreographer; } - void setAnimating(boolean animating) { - mAnimating = animating; - } - - void orAnimating(boolean animating) { - mAnimating |= animating; - } - /** * Adds a runnable to be executed after {@link WindowContainer#prepareSurfaces} is called and * the corresponding transaction is closed and applied. diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 7a4d0b0d9169..a0a70dc6a1d7 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -25,9 +25,13 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.os.UserHandle.USER_NULL; import static android.view.SurfaceControl.Transaction; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; +import static com.android.server.wm.IdentifierProto.HASH_CODE; +import static com.android.server.wm.IdentifierProto.TITLE; +import static com.android.server.wm.IdentifierProto.USER_ID; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION; @@ -64,6 +68,7 @@ import android.util.proto.ProtoOutputStream; import android.view.DisplayInfo; import android.view.IWindowContainer; import android.view.MagnificationSpec; +import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.SurfaceControl.Builder; @@ -94,7 +99,7 @@ import java.util.function.Predicate; * changes are made to this class. */ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E> - implements Comparable<WindowContainer>, Animatable, + implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable, BLASTSyncEngine.TransactionReadyListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM; @@ -169,6 +174,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * Applied as part of the animation pass in "prepareSurfaces". */ protected final SurfaceAnimator mSurfaceAnimator; + final SurfaceFreezer mSurfaceFreezer; protected final WindowManagerService mWmService; private final Point mTmpPos = new Point(); @@ -252,7 +258,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * where it represents the starting-state snapshot. */ WindowContainerThumbnail mThumbnail; - final Rect mTransitStartRect = new Rect(); final Point mTmpPoint = new Point(); protected final Rect mTmpRect = new Rect(); final Rect mTmpPrevBounds = new Rect(); @@ -277,6 +282,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mWmService = wms; mPendingTransaction = wms.mTransactionFactory.get(); mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms); + mSurfaceFreezer = new SurfaceFreezer(this, wms); } @Override @@ -524,13 +530,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< if (mSurfaceControl != null) { getPendingTransaction().remove(mSurfaceControl); - - // Merge to parent transaction to ensure the transactions on this WindowContainer are - // applied in native even if WindowContainer is removed. - if (mParent != null) { - mParent.getPendingTransaction().merge(getPendingTransaction()); - } - setSurfaceControl(null); mLastSurfacePosition.set(0, 0); scheduleAnimation(); @@ -841,7 +840,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * @return {@code true} if the container is in changing app transition. */ boolean isChangingAppTransition() { - return false; + return mDisplayContent != null && mDisplayContent.mChangingContainers.contains(this); } void sendAppVisibilityToClients() { @@ -893,6 +892,31 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } /** + * Called when the visibility of a child is asked to change. This is before visibility actually + * changes (eg. a transition animation might play out first). + */ + void onChildVisibilityRequested(boolean visible) { + // If we are changing visibility, then a snapshot isn't necessary and we are no-longer + // part of a change transition. + mSurfaceFreezer.unfreeze(getPendingTransaction()); + if (mDisplayContent != null) { + mDisplayContent.mChangingContainers.remove(this); + } + WindowContainer parent = getParent(); + if (parent != null) { + parent.onChildVisibilityRequested(visible); + } + } + + void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + proto.write(HASH_CODE, System.identityHashCode(this)); + proto.write(USER_ID, USER_NULL); + proto.write(TITLE, "WindowContainer"); + proto.end(token); + } + + /** * Returns {@code true} if this container is focusable. Generally, if a parent is not focusable, * this will not be focusable either. */ @@ -1924,7 +1948,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< // TODO: This should use isVisible() but because isVisible has a really weird meaning at // the moment this doesn't work for all animatable window containers. - mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback); + mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback, + mSurfaceFreezer); } void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden, @@ -1941,6 +1966,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } @Override + public SurfaceControl getFreezeSnapshotTarget() { + return null; + } + + @Override public Builder makeAnimationLeash() { return makeSurface().setContainerLayer(); } @@ -2009,8 +2039,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } if (thumbnailAdapter != null) { - mThumbnail.startAnimation( - getPendingTransaction(), thumbnailAdapter, !isVisible()); + mSurfaceFreezer.mSnapshot.startAnimation(getPendingTransaction(), + thumbnailAdapter, ANIMATION_TYPE_APP_TRANSITION, + (type, anim) -> { }); } } } else { @@ -2056,7 +2087,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) { final RemoteAnimationController.RemoteAnimationRecord adapters = controller.createRemoteAnimationRecord(this, mTmpPoint, mTmpRect, - (isChanging ? mTransitStartRect : null)); + (isChanging ? mSurfaceFreezer.mFreezeBounds : null)); resultAdapters = new Pair<>(adapters.mAdapter, adapters.mThumbnailAdapter); } else if (isChanging) { final float durationScale = mWmService.getTransitionAnimationScaleLocked(); @@ -2064,14 +2095,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mTmpRect.offsetTo(mTmpPoint.x, mTmpPoint.y); final AnimationAdapter adapter = new LocalAnimationAdapter( - new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect, displayInfo, - durationScale, true /* isAppAnimation */, false /* isThumbnail */), + new WindowChangeAnimationSpec(mSurfaceFreezer.mFreezeBounds, mTmpRect, + displayInfo, durationScale, true /* isAppAnimation */, + false /* isThumbnail */), getSurfaceAnimationRunner()); - final AnimationAdapter thumbnailAdapter = mThumbnail != null - ? new LocalAnimationAdapter(new WindowChangeAnimationSpec(mTransitStartRect, - mTmpRect, displayInfo, durationScale, true /* isAppAnimation */, - true /* isThumbnail */), getSurfaceAnimationRunner()) + final AnimationAdapter thumbnailAdapter = mSurfaceFreezer.mSnapshot != null + ? new LocalAnimationAdapter(new WindowChangeAnimationSpec( + mSurfaceFreezer.mFreezeBounds, mTmpRect, displayInfo, durationScale, + true /* isAppAnimation */, true /* isThumbnail */), getSurfaceAnimationRunner()) : null; resultAdapters = new Pair<>(adapter, thumbnailAdapter); mTransit = transit; @@ -2189,6 +2221,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< @Override public void onAnimationLeashLost(Transaction t) { mLastLayer = -1; + mSurfaceFreezer.unfreeze(t); reassignLayer(t); } @@ -2329,6 +2362,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mSurfaceControl = sc; } + RemoteAnimationDefinition getRemoteAnimationDefinition() { + return null; + } + /** Cheap way of doing cast and instanceof. */ Task asTask() { return null; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 9cb5ba7a74a4..a59eab5959b3 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -42,7 +42,6 @@ import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIV import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; -import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; @@ -59,7 +58,6 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CO import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; -import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; @@ -214,7 +212,6 @@ import android.view.IDisplayFoldListener; import android.view.IDisplayWindowInsetsController; import android.view.IDisplayWindowListener; import android.view.IDisplayWindowRotationController; -import android.view.IDockedStackListener; import android.view.IInputFilter; import android.view.IOnKeyguardExitResult; import android.view.IPinnedStackListener; @@ -1636,14 +1633,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - // If the window is being added to a stack that's currently adjusted for IME, - // make sure to apply the same adjust to this new window. - win.applyAdjustForImeIfNeeded(); - - if (type == TYPE_DOCK_DIVIDER) { - mRoot.getDisplayContent(displayId).getDockedDividerController().setWindow(win); - } - final WindowStateAnimator winAnimator = win.mWinAnimator; winAnimator.mEnterAnimationPending = true; winAnimator.mEnteringAnimation = true; @@ -2816,16 +2805,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - void setDockedStackCreateStateLocked(int mode, Rect bounds) { - mDockedStackCreateMode = mode; - mDockedStackCreateBounds = bounds; - } - - void checkSplitScreenMinimizedChanged(boolean animate) { - final DisplayContent displayContent = getDefaultDisplayContentLocked(); - displayContent.getDockedDividerController().checkMinimizeChanged(animate); - } - boolean isValidPictureInPictureAspectRatio(DisplayContent displayContent, float aspectRatio) { return displayContent.getPinnedStackController().isValidPictureInPictureAspectRatio( aspectRatio); @@ -3295,10 +3274,6 @@ public class WindowManagerService extends IWindowManager.Stub // Notify whether the docked stack exists for the current user final DisplayContent displayContent = getDefaultDisplayContentLocked(); - final ActivityStack stack = - displayContent.getRootSplitScreenPrimaryTask(); - displayContent.mDividerControllerLocked.notifyDockedStackExistsChanged( - stack != null && stack.hasTaskForUser(newUserId)); mRoot.forAllDisplays(dc -> dc.mAppTransition.setCurrentUser(newUserId)); @@ -4687,7 +4662,7 @@ public class WindowManagerService extends IWindowManager.Stub public static final int RESET_ANR_MESSAGE = 38; public static final int WALLPAPER_DRAW_PENDING_TIMEOUT = 39; - public static final int UPDATE_DOCKED_STACK_DIVIDER = 41; + public static final int UPDATE_MULTI_WINDOW_STACKS = 41; public static final int WINDOW_REPLACEMENT_TIMEOUT = 46; @@ -4832,7 +4807,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mGlobalLock) { // Since we're holding both mWindowMap and mAnimator we don't need to // hold mAnimator.mLayoutToAnim. - if (mAnimator.isAnimating() || mAnimator.isAnimationScheduled()) { + if (mAnimator.isAnimationScheduled()) { // If we are animating, don't do the gc now but // delay a bit so we don't interrupt the animation. sendEmptyMessageDelayed(H.FORCE_GC, 2000); @@ -4994,11 +4969,10 @@ public class WindowManagerService extends IWindowManager.Stub } break; } - case UPDATE_DOCKED_STACK_DIVIDER: { + case UPDATE_MULTI_WINDOW_STACKS: { synchronized (mGlobalLock) { final DisplayContent displayContent = getDefaultDisplayContentLocked(); if (displayContent != null) { - displayContent.getDockedDividerController().reevaluateVisibility(false); displayContent.adjustForImeIfNeeded(); } } @@ -6551,11 +6525,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public int getDockedStackSide() { - synchronized (mGlobalLock) { - final ActivityStack dockedStack = getDefaultDisplayContentLocked() - .getRootSplitScreenPrimaryTask(); - return dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide(); - } + return 0; } void setDockedStackResizing(boolean resizing) { @@ -6572,14 +6542,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - @Override - public void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) { - synchronized (mGlobalLock) { - getDefaultDisplayContentLocked().getDockedDividerController().setResizeDimLayer( - visible, targetWindowingMode, alpha); - } - } - void setForceDesktopModeOnExternalDisplays(boolean forceDesktopModeOnExternalDisplays) { synchronized (mGlobalLock) { mForceDesktopModeOnExternalDisplays = forceDesktopModeOnExternalDisplays; @@ -6598,17 +6560,6 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void registerDockedStackListener(IDockedStackListener listener) { - mAtmInternal.enforceCallerIsRecentsOrHasPermission(REGISTER_WINDOW_MANAGER_LISTENERS, - "registerDockedStackListener()"); - synchronized (mGlobalLock) { - // TODO(multi-display): The listener is registered on the default display only. - getDefaultDisplayContentLocked().mDividerControllerLocked.registerDockedStackListener( - listener); - } - } - - @Override public void registerPinnedStackListener(int displayId, IPinnedStackListener listener) { if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS, "registerPinnedStackListener()")) { @@ -8075,32 +8026,39 @@ public class WindowManagerService extends IWindowManager.Stub public void getWindowInsets(WindowManager.LayoutParams attrs, int displayId, Rect outContentInsets, Rect outStableInsets, DisplayCutout.ParcelableWrapper displayCutout) { - synchronized (mGlobalLock) { - final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId); - if (dc == null) { - throw new WindowManager.InvalidDisplayException("Display#" + displayId - + "could not be found!"); - } - final WindowToken windowToken = dc.getWindowToken(attrs.token); - final ActivityRecord activity; - if (windowToken != null && windowToken.asActivityRecord() != null) { - activity = windowToken.asActivityRecord(); - } else { - activity = null; - } - final Rect taskBounds = new Rect(); - final boolean floatingStack; - if (activity != null && activity.getTask() != null) { - final Task task = activity.getTask(); - task.getBounds(taskBounds); - floatingStack = task.isFloating(); - } else { - floatingStack = false; + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent dc = getDisplayContentOrCreate(displayId, attrs.token); + if (dc == null) { + throw new WindowManager.InvalidDisplayException("Display#" + displayId + + "could not be found!"); + } + final WindowToken windowToken = dc.getWindowToken(attrs.token); + final ActivityRecord activity; + if (windowToken != null && windowToken.asActivityRecord() != null) { + activity = windowToken.asActivityRecord(); + } else { + activity = null; + } + final Rect taskBounds; + final boolean floatingStack; + if (activity != null && activity.getTask() != null) { + final Task task = activity.getTask(); + taskBounds = new Rect(); + task.getBounds(taskBounds); + floatingStack = task.isFloating(); + } else { + taskBounds = null; + floatingStack = false; + } + final DisplayFrames displayFrames = dc.mDisplayFrames; + final DisplayPolicy policy = dc.getDisplayPolicy(); + policy.getLayoutHintLw(attrs, taskBounds, displayFrames, floatingStack, + new Rect(), outContentInsets, outStableInsets, displayCutout); } - final DisplayFrames displayFrames = dc.mDisplayFrames; - final DisplayPolicy policy = dc.getDisplayPolicy(); - policy.getLayoutHintLw(attrs, taskBounds, displayFrames, floatingStack, - new Rect(), outContentInsets, outStableInsets, displayCutout); + } finally { + Binder.restoreCallingIdentity(origId); } } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index b25008373a7b..e452c4a87f50 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1078,15 +1078,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } final ActivityStack stack = getRootTask(); - if (inPinnedWindowingMode() && stack != null - && stack.lastAnimatingBoundsWasToFullscreen()) { - // PIP edge case: When going from pinned to fullscreen, we apply a - // tempInsetFrame for the full task - but we're still at the start of the animation. - // To prevent a jump if there's a letterbox, restrict to the parent frame. - mInsetFrame.intersectUnchecked(windowFrames.mParentFrame); - windowFrames.mContainingFrame.intersectUnchecked(windowFrames.mParentFrame); - } - layoutDisplayFrame = new Rect(windowFrames.mDisplayFrame); windowFrames.mDisplayFrame.set(windowFrames.mContainingFrame); layoutXDiff = mInsetFrame.left - windowFrames.mContainingFrame.left; @@ -1137,7 +1128,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } } else if (mAttrs.type == TYPE_DOCK_DIVIDER) { - dc.getDockedDividerController().positionDockedStackedDivider(windowFrames.mFrame); windowFrames.mContentFrame.set(windowFrames.mFrame); if (!windowFrames.mFrame.equals(windowFrames.mLastFrame)) { mMovedByResize = true; @@ -1343,16 +1333,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return; } - final Task task = getTask(); - // In the case of stack bound animations, the window frames will update (unlike other - // animations which just modify various transformation properties). We don't want to - // notify the client of frame changes in this case. Not only is it a lot of churn, but - // the frame may not correspond to the surface size or the onscreen area at various - // phases in the animation, and the client will become sad and confused. - if (task != null && task.getStack().isAnimatingBounds()) { - return; - } - boolean didFrameInsetsChange = setReportResizeHints(); boolean configChanged = !isLastConfigReportedToClient(); if (DEBUG_CONFIGURATION && configChanged) { @@ -1956,13 +1936,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // animating... let's do something. final int left = mWindowFrames.mFrame.left; final int top = mWindowFrames.mFrame.top; - final Task task = getTask(); - final boolean adjustedForMinimizedDockOrIme = task != null - && (task.getStack().isAdjustedForMinimizedDockedStack() - || task.getStack().isAdjustedForIme()); if (mToken.okToAnimate() && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0 - && !isDragResizing() && !adjustedForMinimizedDockOrIme + && !isDragResizing() && getWindowConfiguration().hasMovementAnimations() && !mWinAnimator.mLastHidden && !mSeamlesslyRotated) { @@ -2269,15 +2245,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } final ActivityStack stack = getRootTask(); - if (stack != null && stack.shouldIgnoreInput()) { + if (stack != null && !stack.isFocusable()) { // Ignore when the stack shouldn't receive input event. // (i.e. the minimized stack in split screen mode.) return false; } - if (PixelFormat.formatHasAlpha(mAttrs.format)) { - // Support legacy use cases where transparent windows can still be ime target with - // FLAG_NOT_FOCUSABLE and ALT_FOCUSABLE_IM set. + if (PixelFormat.formatHasAlpha(mAttrs.format) && mAttrs.alpha == 0) { + // Support legacy use cases where completely transparent windows can still be ime target + // with FLAG_NOT_FOCUSABLE and ALT_FOCUSABLE_IM set. // Certain apps listen for IME insets using transparent windows and ADJUST_NOTHING to // manually synchronize app content to IME animation b/144619551. // TODO(b/145812508): remove this once new focus management is complete b/141738570 @@ -2431,13 +2407,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - void applyAdjustForImeIfNeeded() { - final Task task = getTask(); - if (task != null && task.getStack() != null && task.getStack().isAdjustedForIme()) { - task.getStack().applyAdjustForImeIfNeeded(task); - } - } - @Override void switchUser(int userId) { super.switchUser(userId); @@ -4327,10 +4296,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - if (mAttrs.type == TYPE_INPUT_METHOD) { - getDisplayContent().mDividerControllerLocked.resetImeHideRequested(); - } - return true; } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index a1a9af680e92..81d0e3e88c1a 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -902,15 +902,9 @@ class WindowStateAnimator { boolean allowStretching = false; task.getStack().getFinalAnimationSourceHintBounds(mTmpSourceBounds); // If we don't have source bounds, we can attempt to use the content insets - // in the following scenario: - // 1. We have content insets. - // 2. We are not transitioning to full screen - // We have to be careful to check "lastAnimatingBoundsWasToFullscreen" rather than - // the mBoundsAnimating state, as we may have already left it and only be here - // because of the force-scale until resize state. + // if we have content insets. if (mTmpSourceBounds.isEmpty() && (mWin.mLastRelayoutContentInsets.width() > 0 - || mWin.mLastRelayoutContentInsets.height() > 0) - && !task.getStack().lastAnimatingBoundsWasToFullscreen()) { + || mWin.mLastRelayoutContentInsets.height() > 0)) { mTmpSourceBounds.set(task.getStack().mPreAnimationBounds); mTmpSourceBounds.inset(mWin.mLastRelayoutContentInsets); allowStretching = true; diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java index 2499614a3738..5d7f4e9e1c9d 100644 --- a/services/people/java/com/android/server/people/PeopleService.java +++ b/services/people/java/com/android/server/people/PeopleService.java @@ -71,12 +71,12 @@ public class PeopleService extends SystemService { } @Override - public void onUnlockUser(@NonNull TargetUser targetUser) { + public void onUserUnlocking(@NonNull TargetUser targetUser) { mDataManager.onUserUnlocked(targetUser.getUserIdentifier()); } @Override - public void onStopUser(@NonNull TargetUser targetUser) { + public void onUserStopping(@NonNull TargetUser targetUser) { mDataManager.onUserStopped(targetUser.getUserIdentifier()); } diff --git a/services/people/java/com/android/server/people/data/PackageData.java b/services/people/java/com/android/server/people/data/PackageData.java index 35d245fc8d10..3e4c992ce70b 100644 --- a/services/people/java/com/android/server/people/data/PackageData.java +++ b/services/people/java/com/android/server/people/data/PackageData.java @@ -26,6 +26,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.LocusId; +import android.os.FileUtils; import android.text.TextUtils; import android.util.ArrayMap; @@ -251,5 +252,6 @@ public class PackageData { void onDestroy() { mEventStore.onDestroy(); mConversationStore.onDestroy(); + FileUtils.deleteContentsAndDir(mPackageDataDir); } } diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp index d2f86eeabb47..602e4e1ac8ce 100644 --- a/services/robotests/Android.bp +++ b/services/robotests/Android.bp @@ -26,6 +26,8 @@ android_app { "services.core", "services.net", ], + + libs: ["ike-stubs"], } //################################################################## diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp index ef0ca66610c9..5160eae060f6 100644 --- a/services/robotests/backup/Android.bp +++ b/services/robotests/backup/Android.bp @@ -28,6 +28,8 @@ android_app { "services.core", "services.net", ], + + libs: ["ike-stubs"], } //################################################################## diff --git a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java index 5a1ad8655ab0..757a2b1280f3 100644 --- a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java @@ -696,6 +696,31 @@ public final class AdbDebuggingManagerTest { mAdbKeyXmlFile.exists()); } + @Test + public void testAdbKeyStore_removeKey() throws Exception { + // Accept the test key with the 'Always allow' option selected. + runAdbTest(TEST_KEY_1, true, true, false); + runAdbTest(TEST_KEY_2, true, true, false); + + // Set the connection time to 0 to restore the original behavior. + setAllowedConnectionTime(0); + + // Verify that the key is in the adb_keys file to ensure subsequent connections are + // automatically allowed by adbd. + persistKeyStore(); + assertTrue("The key was not in the adb_keys file after persisting the keystore", + isKeyInFile(TEST_KEY_1, mAdbKeyFile)); + assertTrue("The key was not in the adb_keys file after persisting the keystore", + isKeyInFile(TEST_KEY_2, mAdbKeyFile)); + + // Now remove one of the keys and make sure the other key is still there + mKeyStore.removeKey(TEST_KEY_1); + assertFalse("The key was still in the adb_keys file after removing the key", + isKeyInFile(TEST_KEY_1, mAdbKeyFile)); + assertTrue("The key was not in the adb_keys file after removing a different key", + isKeyInFile(TEST_KEY_2, mAdbKeyFile)); + } + /** * Runs an adb test with the provided configuration. * diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index feae1e173f52..08bd1ee3c389 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -144,4 +144,49 @@ public class DisplayModeDirectorTest { Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60); } + + @Test + public void testBrightnessHasLowerPriorityThanUser() { + assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE); + assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_SIZE); + + int displayId = 0; + DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90); + SparseArray<Vote> votes = new SparseArray<>(); + SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); + votesByDisplay.put(displayId, votes); + votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90)); + votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60)); + director.injectVotesByDisplay(votesByDisplay); + DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); + Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); + Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + + votes.clear(); + votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90)); + votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90)); + director.injectVotesByDisplay(votesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); + Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); + Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); + + + votes.clear(); + votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90)); + votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60)); + director.injectVotesByDisplay(votesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); + Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); + Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); + + votes.clear(); + votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 60)); + votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90)); + director.injectVotesByDisplay(votesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); + Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); + Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + + + } } 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 d2ec50001789..a19d91976307 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -430,13 +430,6 @@ public class PackageParserTest { ParsedProvider b ) { assertComponentsEqual(a, b); - - // Sanity check for ProviderInfo - ProviderInfo aInfo = PackageInfoUtils.generateProviderInfo(aPkg, a, 0, - new PackageUserState(), 0, mockPkgSetting(aPkg)); - ProviderInfo bInfo = PackageInfoUtils.generateProviderInfo(bPkg, b, 0, - new PackageUserState(), 0, mockPkgSetting(bPkg)); - assertApplicationInfoEqual(aInfo.applicationInfo, bInfo.applicationInfo); assertEquals(a.getName(), b.getName()); } diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java index 30ab9cd2f875..dc30add1b383 100644 --- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java +++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java @@ -117,7 +117,7 @@ public class BatterySaverPolicyTest extends AndroidTestCase { @SmallTest public void testGetBatterySaverPolicy_PolicyVibration_WithAccessibilityEnabled() { - mBatterySaverPolicy.setAccessibilityEnabledForTest(true); + mBatterySaverPolicy.setAccessibilityEnabled(true); testServiceDefaultValue_Off(ServiceType.VIBRATION); } @@ -339,4 +339,57 @@ public class BatterySaverPolicyTest extends AndroidTestCase { Policy.fromSettings(BATTERY_SAVER_CONSTANTS, "")); verifyBatterySaverConstantsUpdated(); } + + public void testCarModeChanges_Full() { + mBatterySaverPolicy.updateConstantsLocked( + "gps_mode=" + PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF + + ",enable_night_mode=true", ""); + mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_FULL); + assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode) + .isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF); + assertTrue(mBatterySaverPolicy.getBatterySaverPolicy( + ServiceType.NIGHT_MODE).batterySaverEnabled); + + mBatterySaverPolicy.setCarModeEnabled(true); + + assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode) + .isAnyOf(PowerManager.LOCATION_MODE_NO_CHANGE, + PowerManager.LOCATION_MODE_FOREGROUND_ONLY); + assertFalse(mBatterySaverPolicy.getBatterySaverPolicy( + ServiceType.NIGHT_MODE).batterySaverEnabled); + + mBatterySaverPolicy.setCarModeEnabled(false); + + assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode) + .isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF); + assertTrue(mBatterySaverPolicy.getBatterySaverPolicy( + ServiceType.NIGHT_MODE).batterySaverEnabled); + } + + public void testCarModeChanges_Adaptive() { + mBatterySaverPolicy.setAdaptivePolicyLocked( + Policy.fromSettings( + "gps_mode=" + PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF + + ",enable_night_mode=true", "")); + mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_ADAPTIVE); + assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode) + .isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF); + assertTrue(mBatterySaverPolicy.getBatterySaverPolicy( + ServiceType.NIGHT_MODE).batterySaverEnabled); + + mBatterySaverPolicy.setCarModeEnabled(true); + + assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode) + .isAnyOf(PowerManager.LOCATION_MODE_NO_CHANGE, + PowerManager.LOCATION_MODE_FOREGROUND_ONLY); + assertFalse(mBatterySaverPolicy.getBatterySaverPolicy( + ServiceType.NIGHT_MODE).batterySaverEnabled); + + mBatterySaverPolicy.setCarModeEnabled(false); + + assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode) + .isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF); + assertTrue(mBatterySaverPolicy.getBatterySaverPolicy( + ServiceType.NIGHT_MODE).batterySaverEnabled); + } } diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java new file mode 100644 index 000000000000..192c6fe4ab75 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java @@ -0,0 +1,497 @@ +/* + * Copyright 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.server.tv.tunerresourcemanager; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.ContextWrapper; +import android.media.tv.ITvInputManager; +import android.media.tv.TvInputManager; +import android.media.tv.TvInputService; +import android.media.tv.tuner.frontend.FrontendSettings; +import android.media.tv.tunerresourcemanager.ResourceClientProfile; +import android.media.tv.tunerresourcemanager.TunerFrontendInfo; +import android.media.tv.tunerresourcemanager.TunerFrontendRequest; +import android.media.tv.tunerresourcemanager.TunerResourceManager; +import android.os.RemoteException; +import android.util.SparseArray; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Tests for {@link TunerResourceManagerService} class. + */ +@SmallTest +@RunWith(JUnit4.class) +public class TunerResourceManagerServiceTest { + private static final String TAG = "TunerResourceManagerServiceTest"; + private Context mContextSpy; + @Mock private ITvInputManager mITvInputManagerMock; + private TunerResourceManagerService mTunerResourceManagerService; + private int mReclaimingId; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + TvInputManager tvInputManager = new TvInputManager(mITvInputManagerMock, 0); + mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext())); + when(mContextSpy.getSystemService(Context.TV_INPUT_SERVICE)).thenReturn(tvInputManager); + mTunerResourceManagerService = new TunerResourceManagerService(mContextSpy) { + @Override + protected void reclaimFrontendResource(int reclaimingId) { + mReclaimingId = reclaimingId; + } + }; + mTunerResourceManagerService.onStart(true /*isForTesting*/); + mReclaimingId = -1; + } + + @Test + public void setFrontendListTest_addFrontendResources_noExclusiveGroupId() { + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + SparseArray<FrontendResource> resources = + mTunerResourceManagerService.getFrontendResources(); + assertThat(resources.size()).isEqualTo(infos.length); + for (int id = 0; id < infos.length; id++) { + FrontendResource fe = resources.get(infos[id].getId()); + assertThat(fe.getId()).isEqualTo(infos[id].getId()); + assertThat(fe.getType()).isEqualTo(infos[id].getFrontendType()); + assertThat(fe.getExclusiveGroupId()).isEqualTo(infos[id].getExclusiveGroupId()); + assertThat(fe.getExclusiveGroupMemberFeIds().size()).isEqualTo(0); + } + } + + @Test + public void setFrontendListTest_addFrontendResources_underTheSameExclusiveGroupId() { + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[4]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos[2] = + new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + infos[3] = + new TunerFrontendInfo(3 /*id*/, FrontendSettings.TYPE_ATSC, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + SparseArray<FrontendResource> resources = + mTunerResourceManagerService.getFrontendResources(); + assertThat(resources.size()).isEqualTo(infos.length); + for (int id = 0; id < infos.length; id++) { + FrontendResource fe = resources.get(infos[id].getId()); + assertThat(fe.getId()).isEqualTo(infos[id].getId()); + assertThat(fe.getType()).isEqualTo(infos[id].getFrontendType()); + assertThat(fe.getExclusiveGroupId()).isEqualTo(infos[id].getExclusiveGroupId()); + } + + assertThat(resources.get(0).getExclusiveGroupMemberFeIds()) + .isEqualTo(new ArrayList<Integer>()); + assertThat(resources.get(1).getExclusiveGroupMemberFeIds()) + .isEqualTo(new ArrayList<Integer>(Arrays.asList(2, 3))); + assertThat(resources.get(2).getExclusiveGroupMemberFeIds()) + .isEqualTo(new ArrayList<Integer>(Arrays.asList(1, 3))); + assertThat(resources.get(3).getExclusiveGroupMemberFeIds()) + .isEqualTo(new ArrayList<Integer>(Arrays.asList(1, 2))); + } + + @Test + public void setFrontendListTest_updateExistingFrontendResources() { + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + SparseArray<FrontendResource> resources0 = + mTunerResourceManagerService.getFrontendResources(); + + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + SparseArray<FrontendResource> resources1 = + mTunerResourceManagerService.getFrontendResources(); + + assertThat(resources0).isEqualTo(resources1); + } + + @Test + public void setFrontendListTest_removeFrontendResources_noExclusiveGroupId() { + // Init frontend resources. + TunerFrontendInfo[] infos0 = new TunerFrontendInfo[3]; + infos0[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + infos0[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos0[2] = + new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 2 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos0); + + TunerFrontendInfo[] infos1 = new TunerFrontendInfo[1]; + infos1[0] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos1); + + SparseArray<FrontendResource> resources = + mTunerResourceManagerService.getFrontendResources(); + assertThat(resources.size()).isEqualTo(infos1.length); + for (int id = 0; id < infos1.length; id++) { + FrontendResource fe = resources.get(infos1[id].getId()); + assertThat(fe.getId()).isEqualTo(infos1[id].getId()); + assertThat(fe.getType()).isEqualTo(infos1[id].getFrontendType()); + assertThat(fe.getExclusiveGroupId()).isEqualTo(infos1[id].getExclusiveGroupId()); + assertThat(fe.getExclusiveGroupMemberFeIds().size()).isEqualTo(0); + } + } + + @Test + public void setFrontendListTest_removeFrontendResources_underTheSameExclusiveGroupId() { + // Init frontend resources. + TunerFrontendInfo[] infos0 = new TunerFrontendInfo[3]; + infos0[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + infos0[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos0[2] = + new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos0); + + TunerFrontendInfo[] infos1 = new TunerFrontendInfo[1]; + infos1[0] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos1); + + SparseArray<FrontendResource> resources = + mTunerResourceManagerService.getFrontendResources(); + assertThat(resources.size()).isEqualTo(infos1.length); + for (int id = 0; id < infos1.length; id++) { + FrontendResource fe = resources.get(infos1[id].getId()); + assertThat(fe.getId()).isEqualTo(infos1[id].getId()); + assertThat(fe.getType()).isEqualTo(infos1[id].getFrontendType()); + assertThat(fe.getExclusiveGroupId()).isEqualTo(infos1[id].getExclusiveGroupId()); + assertThat(fe.getExclusiveGroupMemberFeIds().size()).isEqualTo(0); + } + } + + @Test + public void requestFrontendTest_ClientNotRegistered() { + TunerFrontendRequest request = + new TunerFrontendRequest(0 /*clientId*/, FrontendSettings.TYPE_DVBT); + int[] frontendId = new int[1]; + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isFalse(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + assertThat(frontendId[0]).isEqualTo(TunerResourceManager.INVALID_FRONTEND_ID); + } + + @Test + public void requestFrontendTest_NoFrontendWithGiveTypeAvailable() { + ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] clientId = new int[1]; + mTunerResourceManagerService.registerClientProfileInternal( + profile, null /*listener*/, clientId); + assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[1]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBS, 0 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + TunerFrontendRequest request = + new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + int[] frontendId = new int[1]; + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isFalse(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + assertThat(frontendId[0]).isEqualTo(TunerResourceManager.INVALID_FRONTEND_ID); + } + + @Test + public void requestFrontendTest_FrontendWithNoExclusiveGroupAvailable() { + ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] clientId = new int[1]; + mTunerResourceManagerService.registerClientProfileInternal( + profile, null /*listener*/, clientId); + assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[3]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos[2] = + new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + TunerFrontendRequest request = + new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + int[] frontendId = new int[1]; + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isTrue(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + assertThat(frontendId[0]).isEqualTo(0); + } + + @Test + public void requestFrontendTest_FrontendWithExclusiveGroupAvailable() { + ResourceClientProfile profile0 = new ResourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + ResourceClientProfile profile1 = new ResourceClientProfile("1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] clientId0 = new int[1]; + int[] clientId1 = new int[1]; + mTunerResourceManagerService.registerClientProfileInternal( + profile0, null /*listener*/, clientId0); + mTunerResourceManagerService.registerClientProfileInternal( + profile1, null /*listener*/, clientId1); + assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[3]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos[2] = + new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + int[] frontendId = new int[1]; + TunerFrontendRequest request = + new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isTrue(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + assertThat(frontendId[0]).isEqualTo(infos[0].getId()); + + request = + new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isTrue(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + assertThat(frontendId[0]).isEqualTo(infos[1].getId()); + assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId()) + .isInUse()).isTrue(); + assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[2].getId()) + .isInUse()).isTrue(); + } + + @Test + public void requestFrontendTest_NoFrontendAvailable_RequestWithLowerPriority() { + // Register clients + ResourceClientProfile[] profiles = new ResourceClientProfile[2]; + profiles[0] = new ResourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + profiles[1] = new ResourceClientProfile("1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] clientPriorities = {100, 50}; + int[] clientId0 = new int[1]; + int[] clientId1 = new int[1]; + mTunerResourceManagerService.registerClientProfileInternal( + profiles[0], null /*listener*/, clientId0); + assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + mTunerResourceManagerService.getClientProfiles().get(clientId0[0]) + .setPriority(clientPriorities[0]); + mTunerResourceManagerService.registerClientProfileInternal( + profiles[1], null /*listener*/, clientId1); + assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + mTunerResourceManagerService.getClientProfiles().get(clientId1[0]) + .setPriority(clientPriorities[1]); + + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + TunerFrontendRequest request = + new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + int[] frontendId = new int[1]; + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isTrue(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + request = + new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isFalse(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + assertThat(mReclaimingId).isEqualTo(-1); + + request = + new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS); + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isFalse(); + assertThat(mReclaimingId).isEqualTo(-1); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Test + public void requestFrontendTest_NoFrontendAvailable_RequestWithHigherPriority() { + // Register clients + ResourceClientProfile[] profiles = new ResourceClientProfile[2]; + profiles[0] = new ResourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + profiles[1] = new ResourceClientProfile("1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] clientPriorities = {100, 500}; + int[] clientId0 = new int[1]; + int[] clientId1 = new int[1]; + mTunerResourceManagerService.registerClientProfileInternal( + profiles[0], null /*listener*/, clientId0); + assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + mTunerResourceManagerService.getClientProfiles().get(clientId0[0]) + .setPriority(clientPriorities[0]); + mTunerResourceManagerService.registerClientProfileInternal( + profiles[1], null /*listener*/, clientId1); + assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + mTunerResourceManagerService.getClientProfiles().get(clientId1[0]) + .setPriority(clientPriorities[1]); + + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + TunerFrontendRequest request = + new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + int[] frontendId = new int[1]; + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isTrue(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + assertThat(frontendId[0]).isEqualTo(infos[0].getId()); + + request = + new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS); + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isTrue(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + assertThat(frontendId[0]).isEqualTo(infos[1].getId()); + assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[0].getId()) + .isInUse()).isTrue(); + assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId()) + .isInUse()).isTrue(); + assertThat(mTunerResourceManagerService.getFrontendResources() + .get(infos[0].getId()).getOwnerClientId()).isEqualTo(clientId1[0]); + assertThat(mTunerResourceManagerService.getFrontendResources() + .get(infos[1].getId()).getOwnerClientId()).isEqualTo(clientId1[0]); + assertThat(mReclaimingId).isEqualTo(clientId0[0]); + } + + @Test + public void unregisterClientTest_usingFrontend() { + // Register client + ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] clientId = new int[1]; + mTunerResourceManagerService.registerClientProfileInternal( + profile, null /*listener*/, clientId); + assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + TunerFrontendRequest request = + new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + int[] frontendId = new int[1]; + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isTrue(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + assertThat(frontendId[0]).isEqualTo(infos[0].getId()); + assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[0].getId()) + .isInUse()).isTrue(); + assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId()) + .isInUse()).isTrue(); + + // Unregister client when using frontend + mTunerResourceManagerService.unregisterClientProfileInternal(clientId[0]); + assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[0].getId()) + .isInUse()).isFalse(); + assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId()) + .isInUse()).isFalse(); + + } +} diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java new file mode 100644 index 000000000000..ab5665ba99fc --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java @@ -0,0 +1,111 @@ +/* + * Copyright 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.server.tv.tunerresourcemanager; + +import static com.google.common.truth.Truth.assertThat; + +import android.media.tv.TvInputService; +import android.util.Slog; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * Tests for {@link UseCasePriorityHints} class. + */ +@SmallTest +@RunWith(JUnit4.class) +public class UseCasePriorityHintsTest { + private static final String TAG = "UseCasePriorityHintsTest"; + private UseCasePriorityHints mPriorityHints; + + private final String mExampleXML = + "<!-- A sample Use Case Priority Hints xml -->" + + "<config version=\"1.0\" xmlns:xi=\"http://www.w3.org/2001/XInclude\">" + + "<useCaseDefault fgPriority=\"150\" bgPriority=\"50\"/>" + + "<useCasePreDefined type=\"USE_CASE_RECORD\" fgPriority=\"600\" bgPriority=\"500\"/>" + + "<useCasePreDefined type=\"USE_CASE_LIVE\" fgPriority=\"490\" bgPriority=\"400\"/>" + + "<useCasePreDefined type=\"USE_CASE_PLAYBACK\" fgPriority=\"480\"" + + " bgPriority=\"300\"/>" + + "<useCasePreDefined type=\"USE_CASE_BACKGROUND\" fgPriority=\"180\"" + + " bgPriority=\"100\"/>" + + "<useCaseVendor type=\"VENDOR_USE_CASE_1\" id=\"1001\" fgPriority=\"300\"" + + " bgPriority=\"80\"/>" + + "</config>"; + + @Before + public void setUp() throws Exception { + mPriorityHints = new UseCasePriorityHints(); + try { + mPriorityHints.parseInternal( + new ByteArrayInputStream(mExampleXML.getBytes(StandardCharsets.UTF_8))); + } catch (IOException | XmlPullParserException e) { + Slog.e(TAG, "Error parse xml.", e); + } + } + + @Test + public void parseTest_parseSampleXml() { + // Pre-defined foreground + assertThat(mPriorityHints.getForegroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND)).isEqualTo(180); + assertThat(mPriorityHints.getForegroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN)).isEqualTo(150); + assertThat(mPriorityHints.getForegroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK)).isEqualTo(480); + assertThat(mPriorityHints.getForegroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE)).isEqualTo(490); + assertThat(mPriorityHints.getForegroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD)).isEqualTo(600); + + // Pre-defined background + assertThat(mPriorityHints.getBackgroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND)).isEqualTo(100); + assertThat(mPriorityHints.getBackgroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN)).isEqualTo(50); + assertThat(mPriorityHints.getBackgroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK)).isEqualTo(300); + assertThat(mPriorityHints.getBackgroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE)).isEqualTo(400); + assertThat(mPriorityHints.getBackgroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD)).isEqualTo(500); + + // Vendor use case + assertThat(mPriorityHints.getForegroundPriority(1001)).isEqualTo(300); + assertThat(mPriorityHints.getBackgroundPriority(1001)).isEqualTo(80); + } + + @Test + public void isDefinedUseCaseTest_invalidUseCase() { + assertThat(mPriorityHints.isDefinedUseCase(1992)).isFalse(); + } + + @Test + public void isDefinedUseCaseTest_validUseCase() { + assertThat(mPriorityHints.isDefinedUseCase(1001)).isTrue(); + assertThat(mPriorityHints.isDefinedUseCase( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD)).isTrue(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 0fdffd554b36..23613e0fccc1 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -76,6 +76,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.SystemService; +import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; import org.junit.Before; import org.junit.Test; @@ -87,6 +88,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** @@ -127,6 +129,15 @@ public class AppStandbyControllerTests { private MyInjector mInjector; private AppStandbyController mController; + private CountDownLatch mStateChangedLatch = new CountDownLatch(1); + private AppIdleStateChangeListener mListener = new AppIdleStateChangeListener() { + @Override + public void onAppIdleStateChanged(String packageName, int userId, + boolean idle, int bucket, int reason) { + mStateChangedLatch.countDown(); + } + }; + static class MyContextWrapper extends ContextWrapper { PackageManager mockPm = mock(PackageManager.class); @@ -156,6 +167,7 @@ public class AppStandbyControllerTests { String mBoundWidgetPackage = PACKAGE_EXEMPTED_1; int[] mRunningUsers = new int[] {USER_ID}; List<UserHandle> mCrossProfileTargets = Collections.emptyList(); + boolean mDeviceIdleMode = false; MyInjector(Context context, Looper looper) { super(context, looper); @@ -251,7 +263,7 @@ public class AppStandbyControllerTests { @Override public boolean isDeviceIdleMode() { - return false; + return mDeviceIdleMode; } @Override @@ -327,6 +339,7 @@ public class AppStandbyControllerTests { controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime, false)); + controller.addListener(mListener); return controller; } @@ -1055,6 +1068,46 @@ public class AppStandbyControllerTests { STANDBY_BUCKET_WORKING_SET, getStandbyBucket(USER_ID2, mController, PACKAGE_1)); } + @Test + public void testUnexemptedSyncScheduled() throws Exception { + mStateChangedLatch = new CountDownLatch(1); + mController.addListener(mListener); + assertEquals("Test package did not start in the Never bucket", STANDBY_BUCKET_NEVER, + getStandbyBucket(mController, PACKAGE_1)); + + mController.postReportSyncScheduled(PACKAGE_1, USER_ID, false); + mStateChangedLatch.await(100, TimeUnit.MILLISECONDS); + assertEquals("Unexempted sync scheduled should bring the package out of the Never bucket", + STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1)); + + setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM); + + mStateChangedLatch = new CountDownLatch(1); + mController.postReportSyncScheduled(PACKAGE_1, USER_ID, false); + mStateChangedLatch.await(100, TimeUnit.MILLISECONDS); + assertEquals("Unexempted sync scheduled should not elevate a non Never bucket", + STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); + } + + @Test + public void testExemptedSyncScheduled() throws Exception { + setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM); + mInjector.mDeviceIdleMode = true; + mStateChangedLatch = new CountDownLatch(1); + mController.postReportSyncScheduled(PACKAGE_1, USER_ID, true); + mStateChangedLatch.await(100, TimeUnit.MILLISECONDS); + assertEquals("Exempted sync scheduled in doze should set bucket to working set", + STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1)); + + setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM); + mInjector.mDeviceIdleMode = false; + mStateChangedLatch = new CountDownLatch(1); + mController.postReportSyncScheduled(PACKAGE_1, USER_ID, true); + mStateChangedLatch.await(100, TimeUnit.MILLISECONDS); + assertEquals("Exempted sync scheduled while not in doze should set bucket to active", + STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1)); + } + private String getAdminAppsStr(int userId) { return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId)); } @@ -1095,4 +1148,12 @@ public class AppStandbyControllerTests { private void setActiveAdmins(int userId, String... admins) { mController.setActiveAdminApps(new ArraySet<>(Arrays.asList(admins)), userId); } + + private void setAndAssertBucket(String pkg, int user, int bucket, int reason) throws Exception { + mStateChangedLatch = new CountDownLatch(1); + mController.setAppStandbyBucket(pkg, user, bucket, reason); + mStateChangedLatch.await(100, TimeUnit.MILLISECONDS); + assertEquals("Failed to set package bucket", bucket, + getStandbyBucket(mController, PACKAGE_1)); + } } diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml index dab0a5f0e279..767857bf2de8 100644 --- a/services/tests/uiservicestests/AndroidManifest.xml +++ b/services/tests/uiservicestests/AndroidManifest.xml @@ -23,7 +23,6 @@ <uses-permission android:name="android.permission.MANAGE_USERS" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" /> - <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" /> <uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" /> <uses-permission android:name="android.permission.DEVICE_POWER" /> diff --git a/services/tests/uiservicestests/src/com/android/internal/logging/InstanceIdSequenceFake.java b/services/tests/uiservicestests/src/com/android/internal/logging/InstanceIdSequenceFake.java index 1629ef00b65a..6e7df05e8afe 100644 --- a/services/tests/uiservicestests/src/com/android/internal/logging/InstanceIdSequenceFake.java +++ b/services/tests/uiservicestests/src/com/android/internal/logging/InstanceIdSequenceFake.java @@ -17,7 +17,7 @@ package com.android.internal.logging; /** - * A fake implementation of InstanceIdSequence that returns 0, 1, 2, ... + * A fake implementation of InstanceIdSequence that returns 1, 2, ... */ public class InstanceIdSequenceFake extends InstanceIdSequence { @@ -25,13 +25,13 @@ public class InstanceIdSequenceFake extends InstanceIdSequence { super(instanceIdMax); } - private int mNextId = 0; + private int mNextId = 1; @Override public InstanceId newInstanceId() { synchronized (this) { if (mNextId >= mInstanceIdMax) { - mNextId = 0; + mNextId = 1; } return newInstanceIdInternal(mNextId++); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index d0283f78338d..b6cdbfb42c2d 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -153,7 +153,6 @@ import android.widget.RemoteViews; import androidx.annotation.Nullable; import androidx.test.InstrumentationRegistry; -import com.android.internal.R; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.logging.InstanceIdSequence; import com.android.internal.logging.InstanceIdSequenceFake; @@ -360,7 +359,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Before public void setUp() throws Exception { // Shell permisssions will override permissions of our app, so add all necessary permissions - // fo this test here: + // for this test here: InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( "android.permission.WRITE_DEVICE_CONFIG", "android.permission.READ_DEVICE_CONFIG", @@ -1164,7 +1163,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(PKG, call.r.getSbn().getPackageName()); assertEquals(0, call.r.getSbn().getId()); assertEquals(tag, call.r.getSbn().getTag()); - assertEquals(0, call.getInstanceId()); // Fake instance IDs are assigned in order + assertEquals(1, call.getInstanceId()); // Fake instance IDs are assigned in order } @Test @@ -1186,14 +1185,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals( NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED, mNotificationRecordLogger.get(0).event); - assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId()); + assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId()); assertTrue(mNotificationRecordLogger.get(1).shouldLogReported); assertEquals( NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED, mNotificationRecordLogger.get(1).event); // Instance ID doesn't change on update of an active notification - assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId()); + assertEquals(1, mNotificationRecordLogger.get(1).getInstanceId()); } @Test @@ -1248,19 +1247,19 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED, mNotificationRecordLogger.get(0).event); assertTrue(mNotificationRecordLogger.get(0).shouldLogReported); - assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId()); + assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId()); assertEquals( NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_APP_CANCEL, mNotificationRecordLogger.get(1).event); - assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId()); + assertEquals(1, mNotificationRecordLogger.get(1).getInstanceId()); assertEquals( NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED, mNotificationRecordLogger.get(2).event); assertTrue(mNotificationRecordLogger.get(2).shouldLogReported); // New instance ID because notification was canceled before re-post - assertEquals(1, mNotificationRecordLogger.get(2).getInstanceId()); + assertEquals(2, mNotificationRecordLogger.get(2).getInstanceId()); } @Test @@ -3453,6 +3452,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testStats_dismissalSurface() throws Exception { final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); + r.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId()); mService.addNotification(r); final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 0, 1, true); @@ -3470,7 +3470,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals( NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_USER_AOD, mNotificationRecordLogger.get(0).event); - assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId()); + assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId()); } @Test @@ -4344,6 +4344,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final NotificationRecord r = generateNotificationRecord( mTestNotificationChannel, 1, null, true); r.setTextChanged(true); + r.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId()); mService.addNotification(r); mService.mNotificationDelegate.onNotificationVisibilityChanged(new NotificationVisibility[] @@ -4353,7 +4354,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(1, mNotificationRecordLogger.getCalls().size()); assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_OPEN, mNotificationRecordLogger.get(0).event); - assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId()); + assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId()); mService.mNotificationDelegate.onNotificationVisibilityChanged( new NotificationVisibility[]{}, @@ -4364,7 +4365,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(2, mNotificationRecordLogger.getCalls().size()); assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLOSE, mNotificationRecordLogger.get(1).event); - assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId()); + assertEquals(1, mNotificationRecordLogger.get(1).getInstanceId()); } @Test @@ -6089,7 +6090,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // Pretend the shortcut exists List<ShortcutInfo> shortcutInfos = new ArrayList<>(); - shortcutInfos.add(mock(ShortcutInfo.class)); + ShortcutInfo info = mock(ShortcutInfo.class); + when(info.isLongLived()).thenReturn(true); + shortcutInfos.add(info); when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos); // Test: Send the bubble notification @@ -6116,7 +6119,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mLauncherApps, times(1)).unregisterCallback(launcherAppsCallback.getValue()); // We're no longer a bubble - Notification notif2 = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification(); + Notification notif2 = mService.getNotificationRecord( + nr.getSbn().getKey()).getNotification(); assertFalse(notif2.isBubbleNotification()); } @@ -6409,11 +6413,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { convos.add(convo2); when(mPreferencesHelper.getConversations(anyString(), anyInt())).thenReturn(convos); - // only one valid shortcut - LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery() - .setPackage(PKG_P) - .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED) - .setShortcutIds(Arrays.asList(channel1.getConversationId())); ShortcutInfo si = mock(ShortcutInfo.class); when(si.getShortLabel()).thenReturn("Hello"); when(mLauncherApps.getShortcuts(any(), any())).thenReturn(Arrays.asList(si)); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java index 2d4b5a73bea0..00b9273c1eb1 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java @@ -51,6 +51,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.ShortcutInfo; import android.graphics.Color; import android.graphics.drawable.Icon; import android.media.AudioAttributes; @@ -203,16 +204,13 @@ public class NotificationRecordTest extends UiServiceTestCase { return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid); } - private StatusBarNotification getMessagingStyleNotification(@Nullable String shortcutId) { + private StatusBarNotification getMessagingStyleNotification() { final Builder builder = new Builder(mMockContext) .setContentTitle("foo") .setSmallIcon(android.R.drawable.sym_def_app_icon); Person person = new Person.Builder().setName("Bob").build(); builder.setStyle(new Notification.MessagingStyle(person)); - if (shortcutId != null) { - builder.setShortcutId(shortcutId); - } Notification n = builder.build(); return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid); @@ -1122,16 +1120,18 @@ public class NotificationRecordTest extends UiServiceTestCase { @Test public void testIsConversation() { - StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id"); + StatusBarNotification sbn = getMessagingStyleNotification(); NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + record.setShortcutInfo(mock(ShortcutInfo.class)); assertTrue(record.isConversation()); } @Test - public void testIsConversation_nullShortcutId() { - StatusBarNotification sbn = getMessagingStyleNotification(null); + public void testIsConversation_nullShortcut() { + StatusBarNotification sbn = getMessagingStyleNotification(); NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + record.setShortcutInfo(null); assertFalse(record.isConversation()); } @@ -1140,25 +1140,28 @@ public class NotificationRecordTest extends UiServiceTestCase { public void testIsConversation_bypassShortcutFlagEnabled() { Settings.Global.putString(mContentResolver, FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true"); - StatusBarNotification sbn = getMessagingStyleNotification(null); + StatusBarNotification sbn = getMessagingStyleNotification(); NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + record.setShortcutInfo(null); assertTrue(record.isConversation()); } @Test public void testIsConversation_channelDemoted() { - StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id"); + StatusBarNotification sbn = getMessagingStyleNotification(); channel.setDemoted(true); NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + record.setShortcutInfo(mock(ShortcutInfo.class)); assertFalse(record.isConversation()); } @Test public void testIsConversation_withAdjustmentOverride() { - StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id"); + StatusBarNotification sbn = getMessagingStyleNotification(); NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + record.setShortcutInfo(mock(ShortcutInfo.class)); Bundle bundle = new Bundle(); bundle.putBoolean(KEY_NOT_CONVERSATION, true); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java index 27f72a13dc60..e4d50c0e1786 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java @@ -55,6 +55,8 @@ import android.util.ArraySet; import android.util.AtomicFile; import android.util.Pair; +import androidx.test.InstrumentationRegistry; + import com.android.internal.logging.InstanceIdSequence; import com.android.internal.logging.InstanceIdSequenceFake; import com.android.server.LocalServices; @@ -65,6 +67,7 @@ import com.android.server.notification.NotificationManagerService.NotificationLi import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.wm.WindowManagerInternal; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -116,6 +119,10 @@ public class RoleObserverTest extends UiServiceTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + // Shell permisssions will override permissions of our app, so add all necessary permissions + // for this test here: + InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( + "android.permission.READ_CONTACTS"); LocalServices.removeServiceForTest(WindowManagerInternal.class); LocalServices.addService(WindowManagerInternal.class, mock(WindowManagerInternal.class)); @@ -153,6 +160,12 @@ public class RoleObserverTest extends UiServiceTestCase { mService.setPreferencesHelper(mPreferencesHelper); } + @After + public void tearDown() { + InstrumentationRegistry.getInstrumentation() + .getUiAutomation().dropShellPermissionIdentity(); + } + @Test public void testInit() throws Exception { List<String> dialer0 = new ArrayList<>(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index 7204a811fbe2..e1ce431fc97c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -255,7 +255,7 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { } private void notifyTransitionStarting(ActivityRecord activity) { - final ArrayMap<ActivityRecord, Integer> reasons = new ArrayMap<>(); + final ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>(); reasons.put(activity, ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN); mActivityMetricsLogger.notifyTransitionStarting(reasons); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index b917e1b92fb9..049c8e1e5746 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -31,7 +31,6 @@ import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT; @@ -50,7 +49,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -139,39 +137,6 @@ public class ActivityStarterTests extends ActivityTestsBase { } @Test - public void testUpdateLaunchBounds() { - // When in a non-resizeable stack, the task bounds should be updated. - final Task task = new TaskBuilder(mService.mStackSupervisor) - .setStack(mService.mRootWindowContainer.getDefaultDisplay().createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */)) - .build(); - final Rect bounds = new Rect(10, 10, 100, 100); - - mStarter.updateBounds(task, bounds); - assertEquals(bounds, task.getRequestedOverrideBounds()); - assertEquals(new Rect(), task.getStack().getRequestedOverrideBounds()); - - // When in a resizeable stack, the stack bounds should be updated as well. - final Task task2 = new TaskBuilder(mService.mStackSupervisor) - .setStack(mService.mRootWindowContainer.getDefaultDisplay().createStack( - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */)) - .build(); - assertThat((Object) task2.getStack()).isInstanceOf(ActivityStack.class); - mStarter.updateBounds(task2, bounds); - - verify(mService, times(1)).animateResizePinnedStack(eq(task2.getRootTaskId()), - eq(bounds), anyInt()); - - // In the case of no animation, the stack and task bounds should be set immediately. - if (!ANIMATE) { - assertEquals(bounds, task2.getStack().getRequestedOverrideBounds()); - assertEquals(bounds, task2.getRequestedOverrideBounds()); - } else { - assertEquals(new Rect(), task2.getRequestedOverrideBounds()); - } - } - - @Test public void testStartActivityPreconditions() { verifyStartActivityPreconditions(PRECONDITION_NO_CALLER_APP, START_PERMISSION_DENIED); verifyStartActivityPreconditions(PRECONDITION_NO_INTENT_COMPONENT, @@ -383,7 +348,7 @@ public class ActivityStarterTests extends ActivityTestsBase { // Never review permissions doReturn(false).when(mMockPackageManager).isPermissionsReviewRequired(any(), anyInt()); doNothing().when(mMockPackageManager).grantImplicitAccess( - anyInt(), any(), anyInt(), anyInt()); + anyInt(), any(), anyInt(), anyInt(), anyBoolean()); doNothing().when(mMockPackageManager).notifyPackageUse(anyString(), anyInt()); final Intent intent = new Intent(); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java index 8c2b2939e540..4cb50c7a9e4d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java @@ -103,12 +103,12 @@ public class AppChangeTransitionTests extends WindowTestsBase { setUpOnDisplay(mDisplayContent); mTask.setWindowingMode(WINDOWING_MODE_FREEFORM); - assertEquals(1, mDisplayContent.mChangingApps.size()); + assertEquals(1, mDisplayContent.mChangingContainers.size()); // Verify we are in a change transition, but without a snapshot. // Though, the test will actually have crashed by now if a snapshot is attempted. - assertNull(mActivity.getThumbnail()); - assertTrue(mActivity.isInChangeTransition()); + assertNull(mTask.mSurfaceFreezer.mSnapshot); + assertTrue(mTask.isInChangeTransition()); waitUntilHandlersIdle(); mActivity.removeImmediately(); @@ -121,14 +121,14 @@ public class AppChangeTransitionTests extends WindowTestsBase { setUpOnDisplay(mDisplayContent); mTask.setWindowingMode(WINDOWING_MODE_FREEFORM); - assertEquals(1, mDisplayContent.mChangingApps.size()); - assertTrue(mActivity.isInChangeTransition()); + assertEquals(1, mDisplayContent.mChangingContainers.size()); + assertTrue(mTask.isInChangeTransition()); // Removing the app-token from the display should clean-up the // the change leash. mDisplayContent.removeAppToken(mActivity.token); - assertEquals(0, mDisplayContent.mChangingApps.size()); - assertFalse(mActivity.isInChangeTransition()); + assertEquals(0, mDisplayContent.mChangingContainers.size()); + assertFalse(mTask.isInChangeTransition()); waitUntilHandlersIdle(); mActivity.removeImmediately(); @@ -152,8 +152,8 @@ public class AppChangeTransitionTests extends WindowTestsBase { assertEquals(WINDOWING_MODE_FULLSCREEN, mTask.getWindowingMode()); // Make sure we're not waiting for a change animation (no leash) - assertFalse(mActivity.isInChangeTransition()); - assertNull(mActivity.getThumbnail()); + assertFalse(mTask.isInChangeTransition()); + assertNull(mActivity.mSurfaceFreezer.mSnapshot); waitUntilHandlersIdle(); mActivity.removeImmediately(); @@ -165,13 +165,13 @@ public class AppChangeTransitionTests extends WindowTestsBase { setUpOnDisplay(mDisplayContent); mTask.setWindowingMode(WINDOWING_MODE_FREEFORM); - assertEquals(1, mDisplayContent.mChangingApps.size()); - assertTrue(mActivity.isInChangeTransition()); + assertEquals(1, mDisplayContent.mChangingContainers.size()); + assertTrue(mTask.isInChangeTransition()); // Changing visibility should cancel the change transition and become closing mActivity.setVisibility(false, false); - assertEquals(0, mDisplayContent.mChangingApps.size()); - assertFalse(mActivity.isInChangeTransition()); + assertEquals(0, mDisplayContent.mChangingContainers.size()); + assertFalse(mTask.isInChangeTransition()); waitUntilHandlersIdle(); mActivity.removeImmediately(); diff --git a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java deleted file mode 100644 index 1dda535cfb95..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java +++ /dev/null @@ -1,627 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static com.android.server.wm.BoundsAnimationController.BOUNDS; -import static com.android.server.wm.BoundsAnimationController.FADE_IN; -import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; -import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; -import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; -import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - -import android.animation.ValueAnimator; -import android.content.Context; -import android.graphics.Rect; -import android.os.Handler; -import android.os.Looper; -import android.platform.test.annotations.Presubmit; - -import androidx.test.annotation.UiThreadTest; -import androidx.test.filters.SmallTest; - -import com.android.server.wm.BoundsAnimationController.BoundsAnimator; -import com.android.server.wm.WindowManagerInternal.AppTransitionListener; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Test class for {@link BoundsAnimationController} to ensure that it sends the right callbacks - * depending on the various interactions. - * - * We are really concerned about only three of the transition states [F = fullscreen, !F = floating] - * F->!F, !F->!F, and !F->F. Each animation can only be cancelled from the target mid-transition, - * or if a new animation starts on the same target. The tests below verifies that the target is - * notified of all the cases where it is animating and cancelled so that it can respond - * appropriately. - * - * Build/Install/Run: - * atest WmTests:BoundsAnimationControllerTests - */ -@SmallTest -@Presubmit -@RunWith(WindowTestRunner.class) -public class BoundsAnimationControllerTests extends WindowTestsBase { - - /** - * Mock value animator to simulate updates with. - */ - private static class MockValueAnimator extends ValueAnimator { - - private float mFraction; - - MockValueAnimator getWithValue(float fraction) { - mFraction = fraction; - return this; - } - - @Override - public Object getAnimatedValue() { - return mFraction; - } - } - - /** - * Mock app transition to fire notifications to the bounds animator. - */ - private static class MockAppTransition extends AppTransition { - - private AppTransitionListener mListener; - - MockAppTransition(Context context, WindowManagerService wm, DisplayContent displayContent) { - super(context, wm, displayContent); - } - - @Override - void registerListenerLocked(AppTransitionListener listener) { - mListener = listener; - } - - public void notifyTransitionPending() { - mListener.onAppTransitionPendingLocked(); - } - - public void notifyTransitionCancelled(int transit) { - mListener.onAppTransitionCancelledLocked(transit); - } - - public void notifyTransitionStarting(int transit) { - mListener.onAppTransitionStartingLocked(transit, 0, 0, 0); - } - - public void notifyTransitionFinished() { - mListener.onAppTransitionFinishedLocked(null); - } - } - - /** - * A test animate bounds user to track callbacks from the bounds animation. - */ - private static class TestBoundsAnimationTarget implements BoundsAnimationTarget { - - boolean mAwaitingAnimationStart; - boolean mMovedToFullscreen; - boolean mAnimationStarted; - boolean mSchedulePipModeChangedOnStart; - boolean mForcePipModeChangedCallback; - boolean mAnimationEnded; - Rect mAnimationEndFinalStackBounds; - boolean mSchedulePipModeChangedOnEnd; - boolean mBoundsUpdated; - boolean mCancelRequested; - Rect mStackBounds; - Rect mTaskBounds; - float mAlpha; - @BoundsAnimationController.AnimationType int mAnimationType; - - void initialize(Rect from) { - mAwaitingAnimationStart = true; - mMovedToFullscreen = false; - mAnimationStarted = false; - mAnimationEnded = false; - mAnimationEndFinalStackBounds = null; - mForcePipModeChangedCallback = false; - mSchedulePipModeChangedOnStart = false; - mSchedulePipModeChangedOnEnd = false; - mStackBounds = from; - mTaskBounds = null; - mBoundsUpdated = false; - } - - @Override - public boolean onAnimationStart(boolean schedulePipModeChangedCallback, - boolean forceUpdate, @BoundsAnimationController.AnimationType int animationType) { - mAwaitingAnimationStart = false; - mAnimationStarted = true; - mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback; - mForcePipModeChangedCallback = forceUpdate; - mAnimationType = animationType; - return true; - } - - @Override - public boolean shouldDeferStartOnMoveToFullscreen() { - return true; - } - - @Override - public boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds) { - // TODO: Once we break the runs apart, we should fail() here if this is called outside - // of onAnimationStart() and onAnimationEnd() - if (mCancelRequested) { - mCancelRequested = false; - return false; - } else { - mBoundsUpdated = true; - mStackBounds = stackBounds; - mTaskBounds = taskBounds; - return true; - } - } - - @Override - public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackBounds, - boolean moveToFullscreen) { - mAnimationEnded = true; - mAnimationEndFinalStackBounds = finalStackBounds; - mSchedulePipModeChangedOnEnd = schedulePipModeChangedCallback; - mMovedToFullscreen = moveToFullscreen; - mTaskBounds = null; - } - - @Override - public boolean setPinnedStackAlpha(float alpha) { - mAlpha = alpha; - return true; - } - } - - /** - * Drives the animations, makes common assertions along the way. - */ - private static class BoundsAnimationDriver { - - private final BoundsAnimationController mController; - private final TestBoundsAnimationTarget mTarget; - private final MockValueAnimator mMockAnimator; - - private BoundsAnimator mAnimator; - private Rect mFrom; - private Rect mTo; - private Rect mLargerBounds; - private Rect mExpectedFinalBounds; - private @BoundsAnimationController.AnimationType int mAnimationType; - - BoundsAnimationDriver(BoundsAnimationController controller, - TestBoundsAnimationTarget target, MockValueAnimator mockValueAnimator) { - mController = controller; - mTarget = target; - mMockAnimator = mockValueAnimator; - } - - BoundsAnimationDriver start(Rect from, Rect to, - @BoundsAnimationController.AnimationType int animationType) { - if (mAnimator != null) { - throw new IllegalArgumentException("Call restart() to restart an animation"); - } - - boolean fromFullscreen = from.equals(BOUNDS_FULL); - boolean toFullscreen = to.equals(BOUNDS_FULL); - - mTarget.initialize(from); - - // Started, not running - assertTrue(mTarget.mAwaitingAnimationStart); - assertFalse(mTarget.mAnimationStarted); - - startImpl(from, to, animationType); - - // Ensure that the animator is paused for the all windows drawn signal when animating - // to/from fullscreen - if (fromFullscreen || toFullscreen) { - assertTrue(mAnimator.isPaused()); - mController.onAllWindowsDrawn(); - } else { - assertTrue(!mAnimator.isPaused()); - } - - // Started and running - assertFalse(mTarget.mAwaitingAnimationStart); - assertTrue(mTarget.mAnimationStarted); - - return this; - } - - BoundsAnimationDriver restart(Rect to, boolean expectStartedAndPipModeChangedCallback) { - if (mAnimator == null) { - throw new IllegalArgumentException("Call start() to start a new animation"); - } - - BoundsAnimator oldAnimator = mAnimator; - boolean toSameBounds = mAnimator.isStarted() && to.equals(mTo); - - // Reset the animation start state - mTarget.mAnimationStarted = false; - - // Start animation - startImpl(mTarget.mStackBounds, to, BOUNDS); - - if (toSameBounds) { - // Same animator if same final bounds - assertSame(oldAnimator, mAnimator); - } - - if (expectStartedAndPipModeChangedCallback) { - // Replacing animation with pending pip mode changed callback, ensure we update - assertTrue(mTarget.mAnimationStarted); - assertTrue(mTarget.mSchedulePipModeChangedOnStart); - assertTrue(mTarget.mForcePipModeChangedCallback); - } else { - // No animation start for replacing animation - assertFalse(mTarget.mAnimationStarted); - } - mTarget.mAnimationStarted = true; - return this; - } - - private BoundsAnimationDriver startImpl(Rect from, Rect to, - @BoundsAnimationController.AnimationType int animationType) { - boolean fromFullscreen = from.equals(BOUNDS_FULL); - boolean toFullscreen = to.equals(BOUNDS_FULL); - mFrom = new Rect(from); - mTo = new Rect(to); - mExpectedFinalBounds = new Rect(to); - mLargerBounds = getLargerBounds(mFrom, mTo); - mAnimationType = animationType; - - // Start animation - final @SchedulePipModeChangedState int schedulePipModeChangedState = toFullscreen - ? SCHEDULE_PIP_MODE_CHANGED_ON_START - : fromFullscreen - ? SCHEDULE_PIP_MODE_CHANGED_ON_END - : NO_PIP_MODE_CHANGED_CALLBACKS; - mAnimator = mController.animateBoundsImpl(mTarget, from, to, DURATION, - schedulePipModeChangedState, fromFullscreen, toFullscreen, animationType); - - if (animationType == BOUNDS) { - // Original stack bounds, frozen task bounds - assertEquals(mFrom, mTarget.mStackBounds); - assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); - - // Animating to larger size - if (mFrom.equals(mLargerBounds)) { - assertFalse(mAnimator.animatingToLargerSize()); - } else if (mTo.equals(mLargerBounds)) { - assertTrue(mAnimator.animatingToLargerSize()); - } - } - - return this; - } - - BoundsAnimationDriver expectStarted(boolean schedulePipModeChanged) { - // Callback made - assertTrue(mTarget.mAnimationStarted); - - assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnStart); - return this; - } - - BoundsAnimationDriver update(float t) { - mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(t)); - - if (mAnimationType == BOUNDS) { - // Temporary stack bounds, frozen task bounds - if (t == 0f) { - assertEquals(mFrom, mTarget.mStackBounds); - } else if (t == 1f) { - assertEquals(mTo, mTarget.mStackBounds); - } else { - assertNotEquals(mFrom, mTarget.mStackBounds); - assertNotEquals(mTo, mTarget.mStackBounds); - } - assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); - } else { - assertEquals((float) mMockAnimator.getAnimatedValue(), mTarget.mAlpha, 0.01f); - } - return this; - } - - BoundsAnimationDriver cancel() { - // Cancel - mTarget.mCancelRequested = true; - mTarget.mBoundsUpdated = false; - mExpectedFinalBounds = null; - - // Update - mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(0.5f)); - - // Not started, not running, cancel reset - assertFalse(mTarget.mCancelRequested); - - // Stack/task bounds not updated - assertFalse(mTarget.mBoundsUpdated); - - // Callback made - assertTrue(mTarget.mAnimationEnded); - assertNull(mTarget.mAnimationEndFinalStackBounds); - - return this; - } - - BoundsAnimationDriver end() { - mAnimator.end(); - - if (mAnimationType == BOUNDS) { - // Final stack bounds - assertEquals(mTo, mTarget.mStackBounds); - assertEquals(mExpectedFinalBounds, mTarget.mAnimationEndFinalStackBounds); - assertNull(mTarget.mTaskBounds); - } else { - assertEquals(mTarget.mAlpha, 1f, 0.01f); - } - - return this; - } - - BoundsAnimationDriver expectEnded(boolean schedulePipModeChanged, - boolean moveToFullscreen) { - // Callback made - assertTrue(mTarget.mAnimationEnded); - - assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnEnd); - assertEquals(moveToFullscreen, mTarget.mMovedToFullscreen); - return this; - } - - private static Rect getLargerBounds(Rect r1, Rect r2) { - int r1Area = r1.width() * r1.height(); - int r2Area = r2.width() * r2.height(); - return (r1Area <= r2Area) ? r2 : r1; - } - } - - // Constants - private static final boolean SCHEDULE_PIP_MODE_CHANGED = true; - private static final boolean MOVE_TO_FULLSCREEN = true; - private static final int DURATION = 100; - - // Some dummy bounds to represent fullscreen and floating bounds - private static final Rect BOUNDS_FULL = new Rect(0, 0, 100, 100); - private static final Rect BOUNDS_FLOATING = new Rect(60, 60, 95, 95); - private static final Rect BOUNDS_SMALLER_FLOATING = new Rect(80, 80, 95, 95); - - // Common - private MockAppTransition mMockAppTransition; - private TestBoundsAnimationTarget mTarget; - private BoundsAnimationController mController; - private BoundsAnimationDriver mDriver; - - // Temp - private static final Rect sTmpRect = new Rect(); - - @Before - public void setUp() throws Exception { - final Context context = getInstrumentation().getTargetContext(); - final Handler handler = new Handler(Looper.getMainLooper()); - mMockAppTransition = new MockAppTransition(context, mWm, mDisplayContent); - mTarget = new TestBoundsAnimationTarget(); - mController = new BoundsAnimationController(context, mMockAppTransition, handler, null); - final MockValueAnimator mockValueAnimator = new MockValueAnimator(); - mDriver = new BoundsAnimationDriver(mController, mTarget, mockValueAnimator); - } - - /** BASE TRANSITIONS **/ - - @UiThreadTest - @Test - public void testFullscreenToFloatingTransition() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToFullscreenTransition() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) - .expectStarted(SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToSmallerFloatingTransition() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToLargerFloatingTransition() { - mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - /** F->!F w/ CANCEL **/ - - @UiThreadTest - @Test - public void testFullscreenToFloatingCancelFromTarget() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .cancel() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFullscreenToFloatingCancelFromAnimationToSameBounds() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_FLOATING, false /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFullscreenToFloatingCancelFromAnimationToFloatingBounds() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_SMALLER_FLOATING, - false /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() { - // When animating from fullscreen and the animation is interruped, we expect the animation - // start callback to be made, with a forced pip mode change callback - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_FULL, true /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); - } - - /** !F->F w/ CANCEL **/ - - @UiThreadTest - @Test - public void testFloatingToFullscreenCancelFromTarget() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) - .expectStarted(SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .cancel() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToFullscreenCancelFromAnimationToSameBounds() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) - .expectStarted(SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_FULL, false /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToFullscreenCancelFromAnimationToFloatingBounds() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) - .expectStarted(SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_SMALLER_FLOATING, - false /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); - } - - /** !F->!F w/ CANCEL **/ - - @UiThreadTest - @Test - public void testFloatingToSmallerFloatingCancelFromTarget() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .cancel() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToLargerFloatingCancelFromTarget() { - mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .cancel() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFadeIn() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, FADE_IN) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - /** MISC **/ - - @UiThreadTest - @Test - public void testBoundsAreCopied() { - Rect from = new Rect(0, 0, 100, 100); - Rect to = new Rect(25, 25, 75, 75); - mDriver.start(from, to, BOUNDS) - .update(0.25f) - .end(); - assertEquals(new Rect(0, 0, 100, 100), from); - assertEquals(new Rect(25, 25, 75, 75), to); - } - - /** - * @return whether the task and stack bounds would be the same if they were at the same offset. - */ - private static boolean assertEqualSizeAtOffset(Rect stackBounds, Rect taskBounds) { - sTmpRect.set(taskBounds); - sTmpRect.offsetTo(stackBounds.left, stackBounds.top); - return stackBounds.equals(sTmpRect); - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java deleted file mode 100644 index 32062080a22c..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static android.view.WindowManager.DOCKED_BOTTOM; -import static android.view.WindowManager.DOCKED_LEFT; -import static android.view.WindowManager.DOCKED_RIGHT; -import static android.view.WindowManager.DOCKED_TOP; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; - -@SmallTest -@Presubmit -public class DockedStackDividerControllerTests { - - @Test - public void testIsDockSideAllowedDockTop() { - // Docked top is always allowed - assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, - NAV_BAR_BOTTOM, true /* navigationBarCanMove */)); - assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, - NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); - } - - @Test - public void testIsDockSideAllowedDockBottom() { - // Cannot dock bottom - assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_BOTTOM, DOCKED_LEFT, - NAV_BAR_BOTTOM, true /* navigationBarCanMove */)); - } - - @Test - public void testIsDockSideAllowedNavigationBarMovable() { - assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, - NAV_BAR_BOTTOM, true /* navigationBarCanMove */)); - assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, - NAV_BAR_LEFT, true /* navigationBarCanMove */)); - assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, - NAV_BAR_RIGHT, true /* navigationBarCanMove */)); - assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, - NAV_BAR_BOTTOM, true /* navigationBarCanMove */)); - assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, - NAV_BAR_RIGHT, true /* navigationBarCanMove */)); - assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, - NAV_BAR_LEFT, true /* navigationBarCanMove */)); - } - - @Test - public void testIsDockSideAllowedNavigationBarNotMovable() { - // Navigation bar is not movable such as tablets - assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, - NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); - assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_TOP, - NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); - assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_RIGHT, - NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); - assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, - NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); - assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_TOP, - NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); - assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_RIGHT, - NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 444906977f47..9a898fda4ef3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -17,7 +17,6 @@ package com.android.server.wm; import static android.app.ActivityManager.RECENT_WITH_EXCLUDED; -import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; @@ -978,10 +977,7 @@ public class RecentTasksTest extends ActivityTestsBase { () -> mService.setTaskWindowingMode(taskId, WINDOWING_MODE_FULLSCREEN, false/* toTop */)); assertNotRestoreTask( - () -> mService.setTaskWindowingModeSplitScreenPrimary(taskId, - SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, - false /* toTop */, false /* animate */, null /* initialBounds */, - true /* showRecents */)); + () -> mService.setTaskWindowingModeSplitScreenPrimary(taskId, false /* toTop */)); } @Test @@ -1096,18 +1092,10 @@ public class RecentTasksTest extends ActivityTestsBase { assertSecurityException(expectCallable, () -> mService.moveTaskToStack(0, INVALID_STACK_ID, true)); assertSecurityException(expectCallable, - () -> mService.setTaskWindowingModeSplitScreenPrimary(0, - SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect(), true)); + () -> mService.setTaskWindowingModeSplitScreenPrimary(0, true)); assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0)); assertSecurityException(expectCallable, () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect())); - assertSecurityException(expectCallable, - () -> mService.animateResizePinnedStack(INVALID_STACK_ID, new Rect(), -1)); - assertSecurityException(expectCallable, - () -> mService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(), - new Rect())); - assertSecurityException(expectCallable, - () -> mService.resizePinnedStack(new Rect(), new Rect())); assertSecurityException(expectCallable, () -> mService.getAllStackInfos()); assertSecurityException(expectCallable, () -> mService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED)); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index c9efb7018616..5c36f5c39e0c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -333,7 +333,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { assertTrue(mController.isAnimatingTask(activity.getTask())); // Assume activity transition should animate when no - // IRecentsAnimationController#setCancelWithDeferredScreenshot called. + // IRecentsAnimationController#setDeferCancelUntilNextTransition called. assertFalse(mController.shouldDeferCancelWithScreenshot()); assertTrue(activity.shouldAnimate(TRANSIT_ACTIVITY_CLOSE)); } diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java index b78107e9024f..b3a253029ed0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -208,7 +208,6 @@ public class RecentsAnimationTest extends ActivityTestsBase { @Test public void testSetLaunchTaskBehindOfTargetActivity() { DisplayContent display = mRootWindowContainer.getDefaultDisplay(); - display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class); ActivityStack homeStack = display.getRootHomeTask(); // Assume the home activity support recents. ActivityRecord targetActivity = homeStack.getTopNonFinishingActivity(); @@ -254,7 +253,6 @@ public class RecentsAnimationTest extends ActivityTestsBase { @Test public void testCancelAnimationOnVisibleStackOrderChange() { DisplayContent display = mService.mRootWindowContainer.getDefaultDisplay(); - display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class); ActivityStack fullscreenStack = display.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); new ActivityBuilder(mService) diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java index 3a724a140ffb..c7f94efdfde0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -247,7 +247,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { @Test public void testChange() throws Exception { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); - mDisplayContent.mChangingApps.add(win.mActivityRecord); + mDisplayContent.mChangingContainers.add(win.mActivityRecord); try { final RemoteAnimationRecord record = mController.createRemoteAnimationRecord( win.mActivityRecord, new Point(50, 100), new Rect(50, 100, 150, 150), @@ -290,7 +290,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { verify(mThumbnailFinishedCallback).onAnimationFinished( eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mThumbnailAdapter)); } finally { - mDisplayContent.mChangingApps.clear(); + mDisplayContent.mChangingContainers.clear(); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index ec2026255f8e..0ef25824df2a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -52,7 +52,6 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.refEq; import android.app.ActivityOptions; -import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; @@ -251,13 +250,12 @@ public class RootActivityContainerTests extends ActivityTestsBase { final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true) .setStack(stack).build(); - // Under split screen primary we should be focusable when not minimized - mRootWindowContainer.setDockedStackMinimized(false); + // Created stacks are focusable by default. assertTrue(stack.isTopActivityFocusable()); assertTrue(activity.isFocusable()); - // Under split screen primary we should not be focusable when minimized - mRootWindowContainer.setDockedStackMinimized(true); + // If the stack is made unfocusable, its activities should inherit that. + stack.setFocusable(false); assertFalse(stack.isTopActivityFocusable()); assertFalse(activity.isFocusable()); @@ -308,33 +306,6 @@ public class RootActivityContainerTests extends ActivityTestsBase { } /** - * Verify split-screen primary stack & task can resized by - * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect. - */ - @Test - public void testResizeDockedStackForSplitScreenPrimary() { - final Rect configSize = new Rect(0, 0, 1000, 1000); - final Rect displayedSize = new Rect(0, 0, 300, 300); - - // Create primary split-screen stack with a task. - final ActivityStack primaryStack = new StackBuilder(mRootWindowContainer) - .setActivityType(ACTIVITY_TYPE_STANDARD) - .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) - .setOnTop(true) - .build(); - final Task task = primaryStack.getTopMostTask(); - - // Resize dock stack. - mService.resizeDockedStack(displayedSize, configSize, null, null, null); - - // Verify dock stack & its task bounds if is equal as resized result. - assertEquals(displayedSize, primaryStack.getDisplayedBounds()); - assertEquals(displayedSize, primaryStack.getDisplayedBounds()); - assertEquals(configSize, primaryStack.getBounds()); - assertEquals(configSize, task.getBounds()); - } - - /** * Verify that home stack would be moved to front when the top activity is Recents. */ @Test @@ -517,7 +488,7 @@ public class RootActivityContainerTests extends ActivityTestsBase { */ @Test public void testStartHomeOnAllDisplays() { - mockResolveHomeActivity(); + mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); mockResolveSecondaryHomeActivity(); // Create secondary displays. @@ -644,30 +615,26 @@ public class RootActivityContainerTests extends ActivityTestsBase { } /** - * Tests that secondary home should be selected if default home not set. + * Tests that secondary home should be selected if primary home not set. */ @Test - public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSet() { - final Intent defaultHomeIntent = mService.getHomeIntent(); - final ActivityInfo aInfoDefault = new ActivityInfo(); - aInfoDefault.name = ResolverActivity.class.getName(); - doReturn(aInfoDefault).when(mRootWindowContainer).resolveHomeActivity(anyInt(), - refEq(defaultHomeIntent)); - - final String secondaryHomeComponent = mService.mContext.getResources().getString( - com.android.internal.R.string.config_secondaryHomeComponent); - final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent); - final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null); - final ActivityInfo aInfoSecondary = new ActivityInfo(); - aInfoSecondary.name = comp.getClassName(); - doReturn(aInfoSecondary).when(mRootWindowContainer).resolveHomeActivity(anyInt(), - refEq(secondaryHomeIntent)); - - // Should fallback to secondary home if default home not set. + public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSet() { + // Setup: primary home not set. + final Intent primaryHomeIntent = mService.getHomeIntent(); + final ActivityInfo aInfoPrimary = new ActivityInfo(); + aInfoPrimary.name = ResolverActivity.class.getName(); + doReturn(aInfoPrimary).when(mRootWindowContainer).resolveHomeActivity(anyInt(), + refEq(primaryHomeIntent)); + // Setup: set secondary home. + mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */); + + // Run the test. final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); - - assertEquals(comp.getClassName(), resolvedInfo.first.name); + final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/); + assertEquals(aInfoSecondary.name, resolvedInfo.first.name); + assertEquals(aInfoSecondary.applicationInfo.packageName, + resolvedInfo.first.applicationInfo.packageName); } /** @@ -675,103 +642,60 @@ public class RootActivityContainerTests extends ActivityTestsBase { * config_useSystemProvidedLauncherForSecondary. */ @Test - public void testResolveSecondaryHomeActivityForced() throws Exception { - Resources resources = mContext.getResources(); - spyOn(resources); - try { - // setUp: set secondary launcher and force it. - final String defaultSecondaryHome = - "com.android.test/com.android.test.TestDefaultSecondaryHome"; - final ComponentName secondaryComp = ComponentName.unflattenFromString( - defaultSecondaryHome); - doReturn(defaultSecondaryHome).when(resources).getString( - com.android.internal.R.string.config_secondaryHomeComponent); - doReturn(true).when(resources).getBoolean( - com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary); - final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null); - assertEquals(secondaryComp, secondaryHomeIntent.getComponent()); - final ActivityInfo aInfoSecondary = new ActivityInfo(); - aInfoSecondary.name = secondaryComp.getClassName(); - aInfoSecondary.applicationInfo = new ApplicationInfo(); - aInfoSecondary.applicationInfo.packageName = secondaryComp.getPackageName(); - doReturn(aInfoSecondary).when(mRootWindowContainer).resolveHomeActivity(anyInt(), - refEq(secondaryHomeIntent)); - final Intent homeIntent = mService.getHomeIntent(); - final ActivityInfo aInfoDefault = new ActivityInfo(); - aInfoDefault.name = "fakeHomeActivity"; - aInfoDefault.applicationInfo = new ApplicationInfo(); - aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; - doReturn(aInfoDefault).when(mRootWindowContainer).resolveHomeActivity(anyInt(), - refEq(homeIntent)); - // Let resolveActivities call to validate both main launcher and second launcher so that - // resolveActivities call does not work as enabler for secondary. - final List<ResolveInfo> resolutions1 = new ArrayList<>(); - final ResolveInfo resolveInfo1 = new ResolveInfo(); - resolveInfo1.activityInfo = new ActivityInfo(); - resolveInfo1.activityInfo.name = aInfoDefault.name; - resolveInfo1.activityInfo.applicationInfo = aInfoDefault.applicationInfo; - resolutions1.add(resolveInfo1); - doReturn(resolutions1).when(mRootWindowContainer).resolveActivities(anyInt(), - refEq(homeIntent)); - final List<ResolveInfo> resolutions2 = new ArrayList<>(); - final ResolveInfo resolveInfo2 = new ResolveInfo(); - resolveInfo2.activityInfo = new ActivityInfo(); - resolveInfo2.activityInfo.name = aInfoSecondary.name; - resolveInfo2.activityInfo.applicationInfo = aInfoSecondary.applicationInfo; - resolutions2.add(resolveInfo2); - doReturn(resolutions2).when(mRootWindowContainer).resolveActivities(anyInt(), - refEq(secondaryHomeIntent)); - doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay( - any(), anyInt(), anyBoolean()); - - // Run the test - final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer - .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); - assertEquals(secondaryComp.getClassName(), resolvedInfo.first.name); - assertEquals(secondaryComp.getPackageName(), - resolvedInfo.first.applicationInfo.packageName); - assertEquals(aInfoSecondary.name, resolvedInfo.first.name); - } finally { - // tearDown - reset(resources); - } + public void testResolveSecondaryHomeActivityForced() { + // SetUp: set primary home. + mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); + // SetUp: set secondary home and force it. + mockResolveHomeActivity(false /* primaryHome */, true /* forceSystemProvided */); + final Intent secondaryHomeIntent = + mService.getSecondaryHomeIntent(null /* preferredPackage */); + final List<ResolveInfo> resolutions = new ArrayList<>(); + final ResolveInfo resolveInfo = new ResolveInfo(); + final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/); + resolveInfo.activityInfo = aInfoSecondary; + resolutions.add(resolveInfo); + doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), + refEq(secondaryHomeIntent)); + doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay( + any(), anyInt(), anyBoolean()); + + // Run the test. + final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer + .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); + assertEquals(aInfoSecondary.name, resolvedInfo.first.name); + assertEquals(aInfoSecondary.applicationInfo.packageName, + resolvedInfo.first.applicationInfo.packageName); } /** - * Tests that secondary home should be selected if default home not support secondary displays - * or there is no matched activity in the same package as selected default home. + * Tests that secondary home should be selected if primary home not support secondary displays + * or there is no matched activity in the same package as selected primary home. */ @Test - public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay() { - mockResolveHomeActivity(); - + public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSupportMultiDisplay() { + // Setup: there is no matched activity in the same package as selected primary home. + mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); final List<ResolveInfo> resolutions = new ArrayList<>(); doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); + // Setup: set secondary home. + mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */); - final String secondaryHomeComponent = mService.mContext.getResources().getString( - com.android.internal.R.string.config_secondaryHomeComponent); - final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent); - final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null); - final ActivityInfo aInfoSecondary = new ActivityInfo(); - aInfoSecondary.name = comp.getClassName(); - doReturn(aInfoSecondary).when(mRootWindowContainer).resolveHomeActivity(anyInt(), - refEq(secondaryHomeIntent)); - - // Should fallback to secondary home if selected default home not support secondary displays - // or there is no matched activity in the same package as selected default home. + // Run the test. final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); - - assertEquals(comp.getClassName(), resolvedInfo.first.name); + final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/); + assertEquals(aInfoSecondary.name, resolvedInfo.first.name); + assertEquals(aInfoSecondary.applicationInfo.packageName, + resolvedInfo.first.applicationInfo.packageName); } - /** - * Tests that default home activity should be selected if it already support secondary displays. + * Tests that primary home activity should be selected if it already support secondary displays. */ @Test - public void testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay() { - final ActivityInfo aInfoDefault = mockResolveHomeActivity(); - + public void testResolveSecondaryHomeActivityWhenPrimaryHomeSupportMultiDisplay() { + // SetUp: set primary home. + mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); + // SetUp: put primary home info on 2nd item final List<ResolveInfo> resolutions = new ArrayList<>(); final ResolveInfo infoFake1 = new ResolveInfo(); infoFake1.activityInfo = new ActivityInfo(); @@ -779,7 +703,8 @@ public class RootActivityContainerTests extends ActivityTestsBase { infoFake1.activityInfo.applicationInfo = new ApplicationInfo(); infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1"; final ResolveInfo infoFake2 = new ResolveInfo(); - infoFake2.activityInfo = aInfoDefault; + final ActivityInfo aInfoPrimary = getFakeHomeActivityInfo(true /* primaryHome */); + infoFake2.activityInfo = aInfoPrimary; resolutions.add(infoFake1); resolutions.add(infoFake2); doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); @@ -787,13 +712,12 @@ public class RootActivityContainerTests extends ActivityTestsBase { doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay( any(), anyInt(), anyBoolean()); - // Use default home activity if it support secondary displays. + // Run the test. final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); - - assertEquals(aInfoDefault.applicationInfo.packageName, + assertEquals(aInfoPrimary.name, resolvedInfo.first.name); + assertEquals(aInfoPrimary.applicationInfo.packageName, resolvedInfo.first.applicationInfo.packageName); - assertEquals(aInfoDefault.name, resolvedInfo.first.name); } /** @@ -801,8 +725,9 @@ public class RootActivityContainerTests extends ActivityTestsBase { */ @Test public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() { - mockResolveHomeActivity(); - + // SetUp: set primary home. + mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); + // Setup: prepare two eligible activity info. final List<ResolveInfo> resolutions = new ArrayList<>(); final ResolveInfo infoFake1 = new ResolveInfo(); infoFake1.activityInfo = new ActivityInfo(); @@ -821,7 +746,7 @@ public class RootActivityContainerTests extends ActivityTestsBase { doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay( any(), anyInt(), anyBoolean()); - // Use the first one of matched activities in the same package as selected default home. + // Use the first one of matched activities in the same package as selected primary home. final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); @@ -884,32 +809,48 @@ public class RootActivityContainerTests extends ActivityTestsBase { /** * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity - * info for test cases (the original implementation will resolve from the real package manager). + * info for test cases. + * + * @param primaryHome Indicate to use primary home intent as parameter, otherwise, use + * secondary home intent. + * @param forceSystemProvided Indicate to force using system provided home activity. */ - private ActivityInfo mockResolveHomeActivity() { - final Intent homeIntent = mService.getHomeIntent(); - final ActivityInfo aInfoDefault = new ActivityInfo(); - aInfoDefault.name = "fakeHomeActivity"; - aInfoDefault.applicationInfo = new ApplicationInfo(); - aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; - doReturn(aInfoDefault).when(mRootWindowContainer).resolveHomeActivity(anyInt(), - refEq(homeIntent)); - return aInfoDefault; + private void mockResolveHomeActivity(boolean primaryHome, boolean forceSystemProvided) { + ActivityInfo targetActivityInfo = getFakeHomeActivityInfo(primaryHome); + Intent targetIntent; + if (primaryHome) { + targetIntent = mService.getHomeIntent(); + } else { + Resources resources = mContext.getResources(); + spyOn(resources); + doReturn(targetActivityInfo.applicationInfo.packageName).when(resources).getString( + com.android.internal.R.string.config_secondaryHomePackage); + doReturn(forceSystemProvided).when(resources).getBoolean( + com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary); + targetIntent = mService.getSecondaryHomeIntent(null /* preferredPackage */); + } + doReturn(targetActivityInfo).when(mRootWindowContainer).resolveHomeActivity(anyInt(), + refEq(targetIntent)); } /** * Mock {@link RootWindowContainer#resolveSecondaryHomeActivity} for returning consistent - * activity info for test cases (the original implementation will resolve from the real package - * manager). + * activity info for test cases. */ private void mockResolveSecondaryHomeActivity() { final Intent secondaryHomeIntent = mService .getSecondaryHomeIntent(null /* preferredPackage */); - final ActivityInfo aInfoSecondary = new ActivityInfo(); - aInfoSecondary.name = "fakeSecondaryHomeActivity"; - aInfoSecondary.applicationInfo = new ApplicationInfo(); - aInfoSecondary.applicationInfo.packageName = "fakeSecondaryHomePackage"; + final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false); doReturn(Pair.create(aInfoSecondary, secondaryHomeIntent)).when(mRootWindowContainer) .resolveSecondaryHomeActivity(anyInt(), anyInt()); } + + private ActivityInfo getFakeHomeActivityInfo(boolean primaryHome) { + final ActivityInfo aInfo = new ActivityInfo(); + aInfo.name = primaryHome ? "fakeHomeActivity" : "fakeSecondaryHomeActivity"; + aInfo.applicationInfo = new ApplicationInfo(); + aInfo.applicationInfo.packageName = + primaryHome ? "fakeHomePackage" : "fakeSecondaryHomePackage"; + return aInfo; + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java index bd8aacb6cb96..20d9aff5f3bf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -25,6 +25,7 @@ import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_APP_THE import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_REAL; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; @@ -36,6 +37,7 @@ import android.content.res.Configuration; import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.util.ArraySet; @@ -138,6 +140,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { final int orientation = Configuration.ORIENTATION_PORTRAIT; final float scaleFraction = 0.25f; final Rect contentInsets = new Rect(1, 2, 3, 4); + final Point taskSize = new Point(5, 6); try { ActivityManager.TaskSnapshot.Builder builder = @@ -147,14 +150,13 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { builder.setSystemUiVisibility(systemUiVisibility); builder.setWindowingMode(windowingMode); builder.setColorSpace(sRGB); - builder.setIsLowResolution(true); builder.setOrientation(orientation); builder.setContentInsets(contentInsets); builder.setIsTranslucent(true); - builder.setScaleFraction(0.25f); builder.setSnapshot(buffer); builder.setIsRealSnapshot(true); builder.setPixelFormat(pixelFormat); + builder.setTaskSize(taskSize); // Not part of TaskSnapshot itself, used in screenshot process assertEquals(pixelFormat, builder.getPixelFormat()); @@ -165,13 +167,15 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { assertEquals(systemUiVisibility, snapshot.getSystemUiVisibility()); assertEquals(windowingMode, snapshot.getWindowingMode()); assertEquals(sRGB, snapshot.getColorSpace()); - assertTrue(snapshot.isLowResolution()); + // Snapshots created with the Builder class are always high-res. The only way to get a + // low-res snapshot is to load it from the disk in TaskSnapshotLoader. + assertFalse(snapshot.isLowResolution()); assertEquals(orientation, snapshot.getOrientation()); assertEquals(contentInsets, snapshot.getContentInsets()); assertTrue(snapshot.isTranslucent()); - assertEquals(scaleFraction, builder.getScaleFraction(), 0.001f); assertSame(buffer, snapshot.getSnapshot()); assertTrue(snapshot.isRealSnapshot()); + assertEquals(taskSize, snapshot.getTaskSize()); } finally { if (buffer != null) { buffer.destroy(); @@ -188,11 +192,9 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { final ActivityManager.TaskSnapshot.Builder builder = new ActivityManager.TaskSnapshot.Builder(); - final float scaleFraction = 0.8f; mWm.mTaskSnapshotController.prepareTaskSnapshot(mAppWindow.mActivityRecord.getTask(), - scaleFraction, PixelFormat.UNKNOWN, builder); + PixelFormat.UNKNOWN, builder); - assertEquals(scaleFraction, builder.getScaleFraction(), 0 /* delta */); // The pixel format should be selected automatically. assertNotEquals(PixelFormat.UNKNOWN, builder.getPixelFormat()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java index 0b16e5ce8b97..40f15b7e9d39 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java @@ -19,12 +19,16 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; +import android.app.ActivityManager; import android.app.ActivityManager.TaskSnapshot; import android.content.res.Configuration; import android.graphics.Rect; @@ -36,10 +40,12 @@ import android.view.View; import androidx.test.filters.MediumTest; +import com.android.server.wm.TaskSnapshotLoader.PreRLegacySnapshotConfig; import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.MockitoSession; import java.io.File; import java.util.function.Predicate; @@ -55,6 +61,8 @@ import java.util.function.Predicate; @RunWith(WindowTestRunner.class) public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBase { + private static final float DELTA = 0.00001f; + private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40); @Test @@ -148,29 +156,172 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa } @Test - public void testLowResolutionPersistAndLoadSnapshot() { + public void testLegacyPLowRamConfig() throws Exception { + MockitoSession mockSession = mockitoSession() + .initMocks(this) + .mockStatic(ActivityManager.class) + .startMocking(); + + when(ActivityManager.isLowRamDeviceStatic()).thenReturn(true); + + // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, + // for any P low_ram device + final int taskWidth = 0; + final float legacyScale = 0f; + final boolean hasHighResFile = false; + + PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); + assertNotNull(highResConf); + assertEquals(highResConf.mScale, 0.6f, DELTA); + assertTrue(highResConf.mForceLoadReducedJpeg); + + PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); + assertNotNull(lowResConf); + assertEquals(lowResConf.mScale, 0.6f, DELTA); + assertTrue(lowResConf.mForceLoadReducedJpeg); + + mockSession.finishMocking(); + } + + @Test + public void testLegacyPNonLowRamConfig() throws Exception { + MockitoSession mockSession = mockitoSession() + .initMocks(this) + .mockStatic(ActivityManager.class) + .startMocking(); + + when(ActivityManager.isLowRamDeviceStatic()).thenReturn(false); + + // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, + // for any O device, or a P non-low_ram device + final int taskWidth = 0; + final float legacyScale = 0f; + final boolean hasHighResFile = true; + + PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); + assertNotNull(highResConf); + assertEquals(highResConf.mScale, 1.0f, DELTA); + assertFalse(highResConf.mForceLoadReducedJpeg); + + PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); + assertNotNull(lowResConf); + assertEquals(lowResConf.mScale, 0.5f, DELTA); + assertFalse(lowResConf.mForceLoadReducedJpeg); + + mockSession.finishMocking(); + } + + @Test + public void testLegacyQLowRamConfig() throws Exception { + MockitoSession mockSession = mockitoSession() + .initMocks(this) + .mockStatic(ActivityManager.class) + .startMocking(); + + when(ActivityManager.isLowRamDeviceStatic()).thenReturn(true); + + // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, + // for any Q low_ram device + final int taskWidth = 0; + final float legacyScale = 0.6f; + final boolean hasHighResFile = false; + + PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); + assertNotNull(highResConf); + assertEquals(highResConf.mScale, legacyScale, DELTA); + assertEquals(highResConf.mScale, 0.6f, DELTA); + assertTrue(highResConf.mForceLoadReducedJpeg); + + PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); + assertNotNull(lowResConf); + assertEquals(lowResConf.mScale, legacyScale, DELTA); + assertEquals(lowResConf.mScale, 0.6f, DELTA); + assertTrue(lowResConf.mForceLoadReducedJpeg); + + mockSession.finishMocking(); + } + + @Test + public void testLegacyQNonLowRamConfig() throws Exception { + MockitoSession mockSession = mockitoSession() + .initMocks(this) + .mockStatic(ActivityManager.class) + .startMocking(); + + when(ActivityManager.isLowRamDeviceStatic()).thenReturn(false); + + // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, + // for any Q non-low_ram device + final int taskWidth = 0; + final float legacyScale = 0.8f; + final boolean hasHighResFile = true; + + PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); + assertNotNull(highResConf); + assertEquals(highResConf.mScale, legacyScale, DELTA); + assertEquals(highResConf.mScale, 0.8f, DELTA); + assertFalse(highResConf.mForceLoadReducedJpeg); + + PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); + assertNotNull(lowResConf); + assertEquals(lowResConf.mScale, 0.5f * legacyScale, DELTA); + assertEquals(lowResConf.mScale, 0.5f * 0.8f, DELTA); + assertFalse(lowResConf.mForceLoadReducedJpeg); + + mockSession.finishMocking(); + } + + @Test + public void testNonLegacyRConfig() throws Exception { + // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, + // for any R device + final int taskWidth = 1440; + final float legacyScale = 0f; + final boolean hasHighResFile = true; + + PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); + assertNull(highResConf); + + PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); + assertNull(lowResConf); + } + + @Test + public void testDisabledLowResolutionPersistAndLoadSnapshot() { + mPersister.setEnableLowResSnapshots(false); + TaskSnapshot a = new TaskSnapshotBuilder() - .setScale(0.5f) + .setScaleFraction(0.5f) .setIsLowResolution(true) .build(); assertTrue(a.isLowResolution()); mPersister.persistSnapshot(1, mTestUserId, a); mPersister.waitForQueueEmpty(); final File[] files = new File[]{new File(FILES_DIR.getPath() + "/snapshots/1.proto"), - new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")}; + new File(FILES_DIR.getPath() + "/snapshots/1.jpg")}; final File[] nonExistsFiles = new File[]{ - new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), + new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"), }; assertTrueForFiles(files, File::exists, " must exist"); assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist"); - final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, true /* isLowResolution */); + final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */); assertNotNull(snapshot); assertEquals(TEST_INSETS, snapshot.getContentInsets()); assertNotNull(snapshot.getSnapshot()); assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation()); final TaskSnapshot snapshotNotExist = mLoader.loadTask(1, mTestUserId, - false /* isLowResolution */); + true /* isLowResolution */); assertNull(snapshotNotExist); } @@ -271,13 +422,11 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa @Test public void testScalePersistAndLoadSnapshot() { TaskSnapshot a = new TaskSnapshotBuilder() - .setScale(0.25f) + .setScaleFraction(0.25f) .build(); TaskSnapshot b = new TaskSnapshotBuilder() - .setScale(0.75f) + .setScaleFraction(0.75f) .build(); - assertEquals(0.25f, a.getScale(), 1E-5); - assertEquals(0.75f, b.getScale(), 1E-5); mPersister.persistSnapshot(1, mTestUserId, a); mPersister.persistSnapshot(2, mTestUserId, b); mPersister.waitForQueueEmpty(); @@ -287,8 +436,6 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa false /* isLowResolution */); assertNotNull(snapshotA); assertNotNull(snapshotB); - assertEquals(0.25f, snapshotA.getScale(), 1E-5); - assertEquals(0.75f, snapshotB.getScale(), 1E-5); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java index 4612dbab0a59..fa6663c06371 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java @@ -30,6 +30,7 @@ import android.graphics.Color; import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.Rect; import android.os.UserManager; import android.view.Surface; @@ -87,8 +88,10 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase { * Builds a TaskSnapshot. */ static class TaskSnapshotBuilder { + private static final int SNAPSHOT_WIDTH = 100; + private static final int SNAPSHOT_HEIGHT = 100; - private float mScale = 1f; + private float mScaleFraction = 1f; private boolean mIsLowResolution = false; private boolean mIsRealSnapshot = true; private boolean mIsTranslucent = false; @@ -96,8 +99,11 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase { private int mSystemUiVisibility = 0; private int mRotation = Surface.ROTATION_0; - TaskSnapshotBuilder setScale(float scale) { - mScale = scale; + TaskSnapshotBuilder() { + } + + TaskSnapshotBuilder setScaleFraction(float scale) { + mScaleFraction = scale; return this; } @@ -132,15 +138,20 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase { } TaskSnapshot build() { - final GraphicBuffer buffer = GraphicBuffer.create(100, 100, PixelFormat.RGBA_8888, + // To satisfy existing tests, ensure the graphics buffer is always 100x100, and + // compute the ize of the task according to mScaleFraction. + Point taskSize = new Point((int) (SNAPSHOT_WIDTH / mScaleFraction), + (int) (SNAPSHOT_HEIGHT / mScaleFraction)); + final GraphicBuffer buffer = GraphicBuffer.create(SNAPSHOT_WIDTH, SNAPSHOT_HEIGHT, + PixelFormat.RGBA_8888, USAGE_HW_TEXTURE | USAGE_SW_READ_RARELY | USAGE_SW_READ_RARELY); Canvas c = buffer.lockCanvas(); c.drawColor(Color.RED); buffer.unlockCanvasAndPost(c); return new TaskSnapshot(MOCK_SNAPSHOT_ID, new ComponentName("", ""), buffer, ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT, - mRotation, TEST_INSETS, - mIsLowResolution, mScale, mIsRealSnapshot, + mRotation, taskSize, TEST_INSETS, + mIsLowResolution, mIsRealSnapshot, mWindowingMode, mSystemUiVisibility, mIsTranslucent); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java index bb0e5aec8e2e..2164de9ea191 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java @@ -38,6 +38,7 @@ import android.graphics.Color; import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.view.Surface; @@ -67,12 +68,22 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { int windowFlags, Rect taskBounds) { final GraphicBuffer buffer = GraphicBuffer.create(width, height, PixelFormat.RGBA_8888, GraphicBuffer.USAGE_SW_READ_RARELY | GraphicBuffer.USAGE_SW_WRITE_NEVER); + + // Previously when constructing TaskSnapshots for this test, scale was 1.0f, so to mimic + // this behavior set the taskSize to be the same as the taskBounds width and height. The + // taskBounds passed here are assumed to be the same task bounds as when the snapshot was + // taken. We assume there is no aspect ratio mismatch between the screenshot and the + // taskBounds + assertEquals(width, taskBounds.width()); + assertEquals(height, taskBounds.height()); + Point taskSize = new Point(taskBounds.width(), taskBounds.height()); + final TaskSnapshot snapshot = new TaskSnapshot( System.currentTimeMillis(), new ComponentName("", ""), buffer, ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT, - Surface.ROTATION_0, contentInsets, false, - 1.0f, true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, + Surface.ROTATION_0, taskSize, contentInsets, false, + true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, 0 /* systemUiVisibility */, false /* isTranslucent */); mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test", createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), sysuiVis, windowFlags, 0, @@ -152,7 +163,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { @Test public void testCalculateSnapshotCrop_taskNotOnTop() { - setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 100)); + setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 150)); assertEquals(new Rect(0, 10, 100, 90), mSurface.calculateSnapshotCrop()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 2c68cc7a19bf..85e4a1668a35 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -251,9 +251,11 @@ public class WindowStateTests extends WindowTestsBase { // b/145812508: special legacy use-case for transparent/translucent windows. appWindow.mAttrs.format = PixelFormat.TRANSPARENT; + appWindow.mAttrs.alpha = 0; assertTrue(appWindow.canBeImeTarget()); appWindow.mAttrs.format = PixelFormat.OPAQUE; + appWindow.mAttrs.alpha = 1; appWindow.mAttrs.flags &= ~FLAG_ALT_FOCUSABLE_IM; assertFalse(appWindow.canBeImeTarget()); appWindow.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE; @@ -276,8 +278,7 @@ public class WindowStateTests extends WindowTestsBase { spyOn(appWindow); spyOn(controller); spyOn(stack); - doReturn(true).when(controller).isMinimizedDock(); - doReturn(true).when(controller).isHomeStackResizable(); + stack.setFocusable(false); doReturn(stack).when(appWindow).getRootTask(); // Make sure canBeImeTarget is false due to shouldIgnoreInput is true; @@ -619,9 +620,10 @@ public class WindowStateTests extends WindowTestsBase { } @Test - public void testCantReceiveTouchWhenShouldIgnoreInput() { + public void testCantReceiveTouchWhenNotFocusable() { final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0"); - win0.mActivityRecord.getStack().setAdjustedForMinimizedDock(1 /* Any non 0 value works */); + win0.mActivityRecord.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + win0.mActivityRecord.getStack().setFocusable(false); assertTrue(win0.cantReceiveTouchInput()); } } diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 07cc2d49d9e5..8e85bb23b5c7 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -1917,7 +1917,19 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser return; } try { - // Adbd will be started by AdbService once Global.ADB_ENABLED is set. + if ((config & UsbManager.FUNCTION_ADB) != 0) { + /** + * Start adbd if ADB function is included in the configuration. + */ + LocalServices.getService(AdbManagerInternal.class) + .startAdbdForTransport(AdbTransportType.USB); + } else { + /** + * Stop adbd otherwise + */ + LocalServices.getService(AdbManagerInternal.class) + .stopAdbdForTransport(AdbTransportType.USB); + } UsbGadgetCallback usbGadgetCallback = new UsbGadgetCallback(mCurrentRequest, config, chargingFunctions); mGadgetProxy.setCurrentUsbFunctions(config, usbGadgetCallback, diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index 61f2c50ccecf..14c7f04b9e82 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -110,18 +110,18 @@ public class UsbService extends IUsbManager.Stub { } @Override - public void onSwitchUser(TargetUser from, TargetUser to) { + public void onUserSwitching(TargetUser from, TargetUser to) { FgThread.getHandler() .postAtFrontOfQueue(() -> mUsbService.onSwitchUser(to.getUserIdentifier())); } @Override - public void onStopUser(TargetUser userInfo) { + public void onUserStopping(TargetUser userInfo) { mUsbService.onStopUser(userInfo.getUserHandle()); } @Override - public void onUnlockUser(TargetUser userInfo) { + public void onUserUnlocking(TargetUser userInfo) { mUsbService.onUnlockUser(userInfo.getUserIdentifier()); } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 8378d8ed9f68..3c0e0af67969 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -167,7 +167,7 @@ public class VoiceInteractionManagerService extends SystemService { } @Override - public boolean isSupportedUser(TargetUser user) { + public boolean isUserSupported(TargetUser user) { return isSupported(user.getUserInfo()); } diff --git a/startop/scripts/app_startup/parse_metrics b/startop/scripts/app_startup/parse_metrics index 036609ff02be..3fa1462bc56e 100755 --- a/startop/scripts/app_startup/parse_metrics +++ b/startop/scripts/app_startup/parse_metrics @@ -42,12 +42,14 @@ Usage: launch_application package activity | parse_metrics --package <name> --ti -h, --help usage information (this) -v, --verbose enable extra verbose printing -t, --timeout <sec> how many seconds to timeout when trying to wait for logcat to change + -rfd, --reportfullydrawn wait for report fully drawn (default: off) EOF } DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source "$DIR/lib/common" +report_fully_drawn="n" package="" activity="" timeout=5 @@ -81,6 +83,11 @@ parse_arguments() { -s|--simulate) simulate="y" ;; + -rfd|--reportfullydrawn) + report_fully_drawn="y" + ;; + + *) echo "Invalid argument: $1" >&2 exit 1 @@ -190,12 +197,15 @@ re_pattern='.*Displayed[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\ parse_metric_from_logcat "Displayed_ms" "$pattern" "$re_pattern" -# 01-16 17:31:44.550 11172 11204 I ActivityTaskManager: Fully drawn com.google.android.GoogleCamera/com.android.camera.CameraLauncher: +10s897ms -pattern="ActivityTaskManager: Fully drawn ${package}" -#re_pattern='.*Fully drawn[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+\).*' -re_pattern='.*Fully drawn[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+ms\|[[:digit:]]\+s[[:digit:]]\+ms\).*' +# Only track ReportFullyDrawn with --reportfullydrawn/-rfd flags +if [[ $report_fully_drawn == y ]]; then + # 01-16 17:31:44.550 11172 11204 I ActivityTaskManager: Fully drawn com.google.android.GoogleCamera/com.android.camera.CameraLauncher: +10s897ms + pattern="ActivityTaskManager: Fully drawn ${package}" + #re_pattern='.*Fully drawn[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+\).*' + re_pattern='.*Fully drawn[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+ms\|[[:digit:]]\+s[[:digit:]]\+ms\).*' -parse_metric_from_logcat "Fully_drawn_ms" "$pattern" "$re_pattern" + parse_metric_from_logcat "Fully_drawn_ms" "$pattern" "$re_pattern" +fi # also call into package-specific scripts if there are additional metrics if [[ -x "$DIR/metrics/$package" ]]; then diff --git a/startop/scripts/app_startup/run_app_with_prefetch b/startop/scripts/app_startup/run_app_with_prefetch index 92a31c30a12d..31f625334b1e 100755 --- a/startop/scripts/app_startup/run_app_with_prefetch +++ b/startop/scripts/app_startup/run_app_with_prefetch @@ -35,6 +35,7 @@ EOF DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source "$DIR/../iorap/common" +report_fully_drawn="n" needs_trace_file="n" input_file="" package="" @@ -70,6 +71,10 @@ parse_arguments() { mode="$2" shift ;; + -rfd|--reportfullydrawn) + report_fully_drawn="y" + shift + ;; -c|--count) count="$2" ((count+=1)) @@ -403,7 +408,11 @@ parse_metrics_header() { join_by ',' "${all_metrics[@]}" } -metrics_header="$("$DIR/parse_metrics" --package "$package" --activity "$activity" --simulate | parse_metrics_header)" +if [[ $report_fully_drawn == y ]]; then + metrics_header="$("$DIR/parse_metrics" --package "$package" --activity "$activity" --simulate --reportfullydrawn | parse_metrics_header)" +else + metrics_header="$("$DIR/parse_metrics" --package "$package" --activity "$activity" --simulate | parse_metrics_header)" +fi # TODO: This loop logic could probably be moved into app_startup_runner.py for ((i=0;i<count;++i)) do @@ -411,6 +420,9 @@ for ((i=0;i<count;++i)) do verbose_print "==== ITERATION $i ====" verbose_print "==========================================" if [[ $mode != "warm" ]]; then + # The package must be killed **before** we drop caches, otherwise pages will stay resident. + verbose_print "Kill package for non-warm start." + remote_pkill "$package" verbose_print "Drop caches for non-warm start." # Drop all caches to get cold starts. adb shell "echo 3 > /proc/sys/vm/drop_caches" @@ -423,7 +435,12 @@ for ((i=0;i<count;++i)) do pre_launch_timestamp="$(logcat_save_timestamp)" # TODO: multiple metrics output. + +if [[ $report_fully_drawn == y ]]; then + total_time="$(timeout $timeout "$DIR/launch_application" "$package" "$activity" | "$DIR/parse_metrics" --package "$package" --activity "$activity" --timestamp "$pre_launch_timestamp" --reportfullydrawn | parse_metrics_output)" +else total_time="$(timeout $timeout "$DIR/launch_application" "$package" "$activity" | "$DIR/parse_metrics" --package "$package" --activity "$activity" --timestamp "$pre_launch_timestamp" | parse_metrics_output)" +fi if [[ $? -ne 0 ]]; then echo "WARNING: Skip bad result, try iteration again." >&2 diff --git a/startop/scripts/app_startup/run_app_with_prefetch.py b/startop/scripts/app_startup/run_app_with_prefetch.py index 2f1eff2c41f6..2f1eff2c41f6 100644..100755 --- a/startop/scripts/app_startup/run_app_with_prefetch.py +++ b/startop/scripts/app_startup/run_app_with_prefetch.py diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 4604cd2e2e75..5f33a3d6f1de 100755 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -3358,7 +3358,6 @@ public abstract class Connection extends Conferenceable { private boolean mImmutable = false; public FailureSignalingConnection(DisconnectCause disconnectCause) { setDisconnected(disconnectCause); - mImmutable = true; } public void checkImmutable() { diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 5d7d6490ba3e..7f4fcc0ea63c 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -330,6 +330,14 @@ public class TelecomManager { "android.telecom.extra.CALL_CREATED_TIME_MILLIS"; /** + * Optional extra for incoming and outgoing calls containing a long which specifies the Epoch + * time the call was created. + * @hide + */ + public static final String EXTRA_CALL_CREATED_EPOCH_TIME_MILLIS = + "android.telecom.extra.CALL_CREATED_EPOCH_TIME_MILLIS"; + + /** * Optional extra for incoming and outgoing calls containing a long which specifies the time * telecom began routing the call. This value is in milliseconds since boot. * @hide diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index a7e52ea21758..6a40487f44eb 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -3549,6 +3549,30 @@ public class CarrierConfigManager { public static final String KEY_SHOW_FORWARDED_NUMBER_BOOL = "show_forwarded_number_bool"; + /** + * The list of originating address of missed incoming call SMS. If the SMS has originator + * matched, the SMS will be treated as special SMS for notifying missed incoming call to the + * user. + * + * @hide + */ + public static final String KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY = + "missed_incoming_call_sms_originator_string_array"; + + /** + * The patterns of missed incoming call sms. This is the regular expression used for + * matching the missed incoming call's date, time, and caller id. The pattern should match + * fields for at least month, day, hour, and minute. Year is optional although it is encouraged. + * + * An usable pattern should look like this: + * ^(?<month>0[1-9]|1[012])\/(?<day>0[1-9]|1[0-9]|2[0-9]|3[0-1]) (?<hour>[0-1][0-9]|2[0-3]): + * (?<minute>[0-5][0-9])\s*(?<callerId>[0-9]+)\s*$ + * + * @hide + */ + public static final String KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY = + "missed_incoming_call_sms_pattern_string_array"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -4057,6 +4081,9 @@ public class CarrierConfigManager { sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false); sDefaults.putBoolean(KEY_SHOW_FORWARDED_NUMBER_BOOL, false); sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, TimeUnit.DAYS.toMillis(1)); + sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY, + new String[0]); + sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]); } /** @@ -4069,7 +4096,8 @@ public class CarrierConfigManager { /** Prefix of all Wifi.KEY_* constants. */ public static final String KEY_PREFIX = "wifi."; /** - * It contains the maximum client count definition that the carrier owns. + * It contains the maximum client count definition that the carrier sets. + * The default is 0, which means that the carrier hasn't set a requirement. */ public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT = KEY_PREFIX + "hotspot_maximum_client_count"; diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java index e4198d1c519c..d672c77bed01 100644 --- a/telephony/java/android/telephony/CellIdentityLte.java +++ b/telephony/java/android/telephony/CellIdentityLte.java @@ -25,6 +25,7 @@ import android.telephony.gsm.GsmCellLocation; import android.text.TextUtils; import android.util.ArraySet; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -54,6 +55,8 @@ public final class CellIdentityLte extends CellIdentity { private final int mEarfcn; // cell bandwidth, in kHz private final int mBandwidth; + // cell bands + private final List<Integer> mBands; // a list of additional PLMN-IDs reported for this cell private final ArraySet<String> mAdditionalPlmns; @@ -70,6 +73,7 @@ public final class CellIdentityLte extends CellIdentity { mPci = CellInfo.UNAVAILABLE; mTac = CellInfo.UNAVAILABLE; mEarfcn = CellInfo.UNAVAILABLE; + mBands = Collections.emptyList(); mBandwidth = CellInfo.UNAVAILABLE; mAdditionalPlmns = new ArraySet<>(); mCsgInfo = null; @@ -87,8 +91,9 @@ public final class CellIdentityLte extends CellIdentity { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) { - this(ci, pci, tac, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, String.valueOf(mcc), - String.valueOf(mnc), null, null, new ArraySet<>(), null); + this(ci, pci, tac, CellInfo.UNAVAILABLE, Collections.emptyList(), CellInfo.UNAVAILABLE, + String.valueOf(mcc), String.valueOf(mnc), null, null, new ArraySet<>(), + null); } /** @@ -107,7 +112,7 @@ public final class CellIdentityLte extends CellIdentity { * * @hide */ - public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth, + public CellIdentityLte(int ci, int pci, int tac, int earfcn, List<Integer> bands, int bandwidth, @Nullable String mccStr, @Nullable String mncStr, @Nullable String alphal, @Nullable String alphas, @NonNull Collection<String> additionalPlmns, @Nullable ClosedSubscriberGroupInfo csgInfo) { @@ -116,6 +121,7 @@ public final class CellIdentityLte extends CellIdentity { mPci = inRangeOrUnavailable(pci, 0, MAX_PCI); mTac = inRangeOrUnavailable(tac, 0, MAX_TAC); mEarfcn = inRangeOrUnavailable(earfcn, 0, MAX_EARFCN); + mBands = new ArrayList<>(bands); mBandwidth = inRangeOrUnavailable(bandwidth, 0, MAX_BANDWIDTH); mAdditionalPlmns = new ArraySet<>(additionalPlmns.size()); for (String plmn : additionalPlmns) { @@ -128,28 +134,28 @@ public final class CellIdentityLte extends CellIdentity { /** @hide */ public CellIdentityLte(@NonNull android.hardware.radio.V1_0.CellIdentityLte cid) { - this(cid.ci, cid.pci, cid.tac, cid.earfcn, + this(cid.ci, cid.pci, cid.tac, cid.earfcn, Collections.emptyList(), CellInfo.UNAVAILABLE, cid.mcc, cid.mnc, "", "", new ArraySet<>(), null); } /** @hide */ public CellIdentityLte(@NonNull android.hardware.radio.V1_2.CellIdentityLte cid) { - this(cid.base.ci, cid.base.pci, cid.base.tac, cid.base.earfcn, cid.bandwidth, - cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong, + this(cid.base.ci, cid.base.pci, cid.base.tac, cid.base.earfcn, Collections.emptyList(), + cid.bandwidth, cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort, new ArraySet<>(), null); } /** @hide */ public CellIdentityLte(@NonNull android.hardware.radio.V1_5.CellIdentityLte cid) { this(cid.base.base.ci, cid.base.base.pci, cid.base.base.tac, cid.base.base.earfcn, - cid.base.bandwidth, cid.base.base.mcc, cid.base.base.mnc, + cid.bands, cid.base.bandwidth, cid.base.base.mcc, cid.base.base.mnc, cid.base.operatorNames.alphaLong, cid.base.operatorNames.alphaShort, cid.additionalPlmns, cid.optionalCsgInfo.csgInfo() != null ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo()) : null); } private CellIdentityLte(@NonNull CellIdentityLte cid) { - this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBandwidth, cid.mMccStr, + this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBands, cid.mBandwidth, cid.mMccStr, cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort, cid.mAdditionalPlmns, cid.mCsgInfo); } @@ -157,7 +163,7 @@ public final class CellIdentityLte extends CellIdentity { @Override public @NonNull CellIdentityLte sanitizeLocationInfo() { return new CellIdentityLte(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, - CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, + CellInfo.UNAVAILABLE, mBands, CellInfo.UNAVAILABLE, mMccStr, mMncStr, mAlphaLong, mAlphaShort, mAdditionalPlmns, null); } @@ -226,8 +232,7 @@ public final class CellIdentityLte extends CellIdentity { */ @NonNull public List<Integer> getBands() { - // Todo: Add actual support - return Collections.emptyList(); + return Collections.unmodifiableList(mBands); } /** @@ -343,6 +348,7 @@ public final class CellIdentityLte extends CellIdentity { .append(" mPci=").append(mPci) .append(" mTac=").append(mTac) .append(" mEarfcn=").append(mEarfcn) + .append(" mBands=").append(mBands) .append(" mBandwidth=").append(mBandwidth) .append(" mMcc=").append(mMccStr) .append(" mMnc=").append(mMncStr) @@ -362,6 +368,7 @@ public final class CellIdentityLte extends CellIdentity { dest.writeInt(mPci); dest.writeInt(mTac); dest.writeInt(mEarfcn); + dest.writeList(mBands); dest.writeInt(mBandwidth); dest.writeArraySet(mAdditionalPlmns); dest.writeParcelable(mCsgInfo, flags); @@ -374,6 +381,7 @@ public final class CellIdentityLte extends CellIdentity { mPci = in.readInt(); mTac = in.readInt(); mEarfcn = in.readInt(); + mBands = in.readArrayList(null); mBandwidth = in.readInt(); mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null); mCsgInfo = in.readParcelable(null); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 0660776c3868..31d965463a7e 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -2788,39 +2788,22 @@ public class TelephonyManager { /** * Returns the ISO-3166 country code equivalent of the MCC (Mobile Country Code) of the current * registered operator or the cell nearby, if available. - * <p> - * The ISO-3166 country code is provided in lowercase 2 character format. - * <p> - * Note: In multi-sim, this returns a shared emergency network country iso from other - * subscription if the subscription used to create the TelephonyManager doesn't camp on - * a network due to some reason (e.g. pin/puk locked), or sim is absent in the corresponding - * slot. + * * Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine * if on a CDMA network). * <p> * @return the lowercase 2 character ISO-3166 country code, or empty string if not available. */ public String getNetworkCountryIso() { - try { - ITelephony telephony = getITelephony(); - if (telephony == null) return ""; - return telephony.getNetworkCountryIsoForPhone(getPhoneId(), - null /* no permission check */, null); - } catch (RemoteException ex) { - return ""; - } + return getNetworkCountryIso(getSlotIndex()); } /** * Returns the ISO-3166 country code equivalent of the MCC (Mobile Country Code) of the current - * registered operator or the cell nearby, if available. - * <p> - * The ISO-3166 country code is provided in lowercase 2 character format. - * <p> - * Note: In multi-sim, this returns a shared emergency network country iso from other - * subscription if the subscription used to create the TelephonyManager doesn't camp on - * a network due to some reason (e.g. pin/puk locked), or sim is absent in the corresponding - * slot. + * registered operator or the cell nearby, if available. This is same as + * {@link #getNetworkCountryIso()} but allowing specifying the SIM slot index. This is used for + * accessing network country info from the SIM slot that does not have SIM inserted. + * * Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine * if on a CDMA network). * <p> @@ -2831,22 +2814,18 @@ public class TelephonyManager { * * @throws IllegalArgumentException when the slotIndex is invalid. * - * {@hide} */ - @SystemApi - @TestApi @NonNull - @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getNetworkCountryIso(int slotIndex) { try { - if (!SubscriptionManager.isValidSlotIndex(slotIndex)) { + if (slotIndex != SubscriptionManager.DEFAULT_SIM_SLOT_INDEX + && !SubscriptionManager.isValidSlotIndex(slotIndex)) { throw new IllegalArgumentException("invalid slot index " + slotIndex); } ITelephony telephony = getITelephony(); if (telephony == null) return ""; - return telephony.getNetworkCountryIsoForPhone(slotIndex, getOpPackageName(), - getFeatureId()); + return telephony.getNetworkCountryIsoForPhone(slotIndex); } catch (RemoteException ex) { return ""; } @@ -11092,7 +11071,6 @@ public class TelephonyManager { * @param enabled True if enabling the data, otherwise disabling. * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setPolicyDataEnabled(boolean enabled) { try { @@ -11195,7 +11173,6 @@ public class TelephonyManager { * @param isEnabled {@code true} for enabling; {@code false} for disabling. * @hide */ - @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAlwaysReportSignalStrength(boolean isEnabled) { try { diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 861925fd66e5..af5089f8e0d9 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -281,7 +281,7 @@ interface ITelephony { * operator's MCC (Mobile Country Code). * @see android.telephony.TelephonyManager#getNetworkCountryIso */ - String getNetworkCountryIsoForPhone(int phoneId, String callingPkg, String callingFeatureId); + String getNetworkCountryIsoForPhone(int phoneId); /** * Returns the neighboring cell information of the device. diff --git a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java index 1c8b6be49547..61f3dbaff1a3 100644 --- a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java +++ b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java @@ -30,6 +30,7 @@ public class BootImageProfileTest implements IDeviceTest { private ITestDevice mTestDevice; private static final String SYSTEM_SERVER_PROFILE = "/data/misc/profiles/cur/0/android/primary.prof"; + private static final boolean USE_PHENOTYPE = false; @Override public void setDevice(ITestDevice testDevice) { @@ -41,16 +42,33 @@ public class BootImageProfileTest implements IDeviceTest { return mTestDevice; } + private String getProperty(String property) throws Exception { + if (USE_PHENOTYPE) { + return mTestDevice.getProperty("persist.device_config.runtime_native_boot." + + property); + } else { + return mTestDevice.executeShellCommand("getprop dalvik.vm." + property).trim(); + } + } + + private String setProperty(String property, String value) throws Exception { + if (USE_PHENOTYPE) { + return mTestDevice.executeShellCommand( + "device_config put runtime_native_boot " + property + " " + value); + } else { + return mTestDevice.executeShellCommand( + "setprop dalvik.vm." + property + " " + value); + } + } + /** * Validate that the boot image profile properties are set. */ public void validateProperties() throws Exception { - String res = mTestDevice.getProperty( - "persist.device_config.runtime_native_boot.profilebootclasspath"); - assertTrue("profile boot class path not enabled", res != null && res.equals("true")); - res = mTestDevice.getProperty( - "persist.device_config.runtime_native_boot.profilesystemserver"); - assertTrue("profile system server not enabled", res != null && res.equals("true")); + String res = getProperty("profilebootclasspath"); + assertTrue("profile boot class path not enabled: " + res, "true".equals(res)); + res = getProperty("profilesystemserver"); + assertTrue("profile system server not enabled: " + res, "true".equals(res)); } private boolean forceSaveProfile(String pkg) throws Exception { @@ -67,33 +85,48 @@ public class BootImageProfileTest implements IDeviceTest { @Test public void testSystemServerProfile() throws Exception { final int numIterations = 20; + String res; + // Set properties and wait for them to be readable. for (int i = 1; i <= numIterations; ++i) { - String res; - res = mTestDevice.getProperty( - "persist.device_config.runtime_native_boot.profilebootclasspath"); - boolean profileBootClassPath = res != null && res.equals("true"); - res = mTestDevice.getProperty( - "persist.device_config.runtime_native_boot.profilesystemserver"); - boolean profileSystemServer = res != null && res.equals("true"); + String pbcp = getProperty("profilebootclasspath"); + boolean profileBootClassPath = "true".equals(pbcp); + String pss = getProperty("profilesystemserver"); + boolean profileSystemServer = "true".equals(pss); if (profileBootClassPath && profileSystemServer) { break; } if (i == numIterations) { - assertTrue("profile system server not enabled", profileSystemServer); - assertTrue("profile boot class path not enabled", profileSystemServer); + assertTrue("profile system server not enabled: " + pss, profileSystemServer); + assertTrue("profile boot class path not enabled: " + pbcp, profileBootClassPath); } - res = mTestDevice.executeShellCommand( - "device_config put runtime_native_boot profilebootclasspath true"); - res = mTestDevice.executeShellCommand( - "device_config put runtime_native_boot profilesystemserver true"); - res = mTestDevice.executeShellCommand("stop"); - res = mTestDevice.executeShellCommand("start"); - Thread.sleep(5000); + setProperty("profilebootclasspath", "true"); + setProperty("profilesystemserver", "true"); + Thread.sleep(1000); } + + // Restart shell and wait for system boot. + res = mTestDevice.executeShellCommand("stop"); + assertTrue("stop shell: " + res, res.length() == 0); + res = mTestDevice.executeShellCommand("start"); + assertTrue("start shell: " + res, res.length() == 0); + for (int i = 1; i <= numIterations; ++i) { + String pbcp = getProperty("profilebootclasspath"); + boolean profileBootClassPath = "true".equals(pbcp); + String pss = getProperty("profilesystemserver"); + boolean profileSystemServer = "true".equals(pss); + if (profileBootClassPath && profileSystemServer) { + break; + } + if (i == numIterations) { + assertTrue("profile system server not enabled: " + pss, profileSystemServer); + assertTrue("profile boot class path not enabled: " + pbcp, profileBootClassPath); + } + Thread.sleep(1000); + } + // Trunacte the profile before force it to be saved to prevent previous profiles // causing the test to pass. - String res; res = mTestDevice.executeShellCommand("truncate -s 0 " + SYSTEM_SERVER_PROFILE).trim(); assertTrue(res, res.length() == 0); // Wait up to 20 seconds for the profile to be saved. diff --git a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java index d4e34f97a8d2..f6dcff49c80c 100644 --- a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java +++ b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java @@ -53,6 +53,7 @@ public class NetworkStagedRollbackTest extends BaseHostJUnit4Test { private static final String ROLLBACK_INITIATE = "ROLLBACK_INITIATE"; private static final String ROLLBACK_BOOT_TRIGGERED = "ROLLBACK_BOOT_TRIGGERED"; + private static final String ROLLBACK_SUCCESS = "ROLLBACK_SUCCESS"; private WatchdogEventLogger mLogger = new WatchdogEventLogger(); @@ -93,6 +94,7 @@ public class NetworkStagedRollbackTest extends BaseHostJUnit4Test { REASON_EXPLICIT_HEALTH_CHECK, null)); assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null, null, null)); + assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null)); } finally { // Reconnect internet again so we won't break tests which assume internet available getDevice().executeShellCommand("svc wifi enable"); 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 43759cf4b761..4afebb58c105 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 @@ -76,10 +76,10 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { private static final String REASON_APP_CRASH = "REASON_APP_CRASH"; private static final String REASON_NATIVE_CRASH = "REASON_NATIVE_CRASH"; - private static final String REASON_EXPLICIT_HEALTH_CHECK = "REASON_EXPLICIT_HEALTH_CHECK"; private static final String ROLLBACK_INITIATE = "ROLLBACK_INITIATE"; private static final String ROLLBACK_BOOT_TRIGGERED = "ROLLBACK_BOOT_TRIGGERED"; + private static final String ROLLBACK_SUCCESS = "ROLLBACK_SUCCESS"; private WatchdogEventLogger mLogger = new WatchdogEventLogger(); @@ -146,6 +146,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { REASON_APP_CRASH, TESTAPP_A)); assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null, null, null)); + assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null)); } @Test @@ -179,6 +180,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { REASON_NATIVE_CRASH, null)); assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null, null, null)); + assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null)); } @Test @@ -219,6 +221,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { REASON_NATIVE_CRASH, null)); assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null, null, null)); + assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null)); } /** @@ -290,6 +293,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { REASON_APP_CRASH, TESTAPP_A)); assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null, null, null)); + assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null)); } /** diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java index 26916bcffcfb..aed62d046896 100644 --- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java +++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java @@ -46,6 +46,7 @@ public final class FrameworksTestsFilter extends SelectTest { "android.view.InsetsSourceTest", "android.view.InsetsSourceConsumerTest", "android.view.InsetsStateTest", + "android.view.WindowMetricsTest" }; public FrameworksTestsFilter(Bundle testArgs) { diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp index ded4b916c452..786223a430ae 100644 --- a/tools/incident_section_gen/main.cpp +++ b/tools/incident_section_gen/main.cpp @@ -415,7 +415,7 @@ static bool generateSectionListCpp(Descriptor const* descriptor) { } const SectionFlags s = getSectionFlags(field); - if (s.userdebug_and_eng_only()) { + if (s.userdebug_and_eng_only() || s.type() == SECTION_TEXT_DUMPSYS) { printf("#if ALLOW_RESTRICTED_SECTIONS\n"); } @@ -449,8 +449,13 @@ static bool generateSectionListCpp(Descriptor const* descriptor) { printf(" new TombstoneSection(%d, \"%s\"),\n", field->number(), s.args().c_str()); break; + case SECTION_TEXT_DUMPSYS: + printf(" new TextDumpsysSection(%d, ", field->number()); + splitAndPrint(s.args()); + printf(" NULL),\n"); + break; } - if (s.userdebug_and_eng_only()) { + if (s.userdebug_and_eng_only() || s.type() == SECTION_TEXT_DUMPSYS) { printf("#endif\n"); } } diff --git a/wifi/Android.bp b/wifi/Android.bp index e253d6d4f6cb..91174d3c3be2 100644 --- a/wifi/Android.bp +++ b/wifi/Android.bp @@ -47,7 +47,7 @@ filegroup { // framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache // to a separate package. "java/android/net/wifi/WifiNetworkScoreCache.java", - "java/android/net/wifi/WifiOemMigrationHook.java", + "java/android/net/wifi/WifiMigration.java", "java/android/net/wifi/nl80211/*.java", ":libwificond_ipc_aidl", ], diff --git a/wifi/java/android/net/wifi/IScoreChangeCallback.aidl b/wifi/java/android/net/wifi/IScoreChangeCallback.aidl index 462a97844d76..d691f41b2858 100644 --- a/wifi/java/android/net/wifi/IScoreChangeCallback.aidl +++ b/wifi/java/android/net/wifi/IScoreChangeCallback.aidl @@ -16,16 +16,14 @@ package android.net.wifi; -import android.net.NetworkScore; - /** - * Interface for Wi-Fi network score callback. + * Interface for Wi-Fi score callback. * * @hide */ oneway interface IScoreChangeCallback { - void onScoreChange(int sessionId, in NetworkScore score); + void onScoreChange(int sessionId, int score); void onTriggerUpdateOfWifiUsabilityStats(int sessionId); } diff --git a/wifi/java/android/net/wifi/SoftApCapability.java b/wifi/java/android/net/wifi/SoftApCapability.java index a831984f3968..18b26db1b020 100644 --- a/wifi/java/android/net/wifi/SoftApCapability.java +++ b/wifi/java/android/net/wifi/SoftApCapability.java @@ -100,12 +100,12 @@ public final class SoftApCapability implements Parcelable { } /** - * Returns true when feature supported, otherwise false. + * Returns true when all of the queried features are supported, otherwise false. * - * @param feature one of feature from {@link HotspotFeatures} + * @param features One or combination of the features from {@link @HotspotFeatures} */ - public boolean isFeatureSupported(@HotspotFeatures long feature) { - return (mSupportedFeatures & feature) == feature; + public boolean areFeaturesSupported(@HotspotFeatures long features) { + return (mSupportedFeatures & features) == features; } /** @@ -122,7 +122,7 @@ public final class SoftApCapability implements Parcelable { * Constructor with combination of the feature. * Zero to no supported feature. * - * @param features One or combination of the feature from {@link @HotspotFeatures}. + * @param features One or combination of the features from {@link @HotspotFeatures}. * @hide */ public SoftApCapability(@HotspotFeatures long features) { diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java index ae5bf7d5ae84..2b4762320f52 100644 --- a/wifi/java/android/net/wifi/SoftApConfiguration.java +++ b/wifi/java/android/net/wifi/SoftApConfiguration.java @@ -762,7 +762,8 @@ public final class SoftApConfiguration implements Parcelable { * {@link #setBand(@BandType int)}. * * The channel auto selection will offload to driver when - * {@link SoftApCapability#isFeatureSupported(SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD)} + * {@link SoftApCapability#areFeaturesSupported( + * SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD)} * return true. Driver will auto select best channel which based on environment * interference to get best performance. Check {@link SoftApCapability} to get more detail. * @@ -807,7 +808,7 @@ public final class SoftApConfiguration implements Parcelable { * * <p> * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and - * {@link SoftApCapability#isFeatureSupported(int)} + * {@link SoftApCapability#areFeaturesSupported(int)} * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} to determine whether * or not this feature is supported. * @@ -882,7 +883,7 @@ public final class SoftApConfiguration implements Parcelable { * <p> * This method requires hardware support. Hardware support can be determined using * {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and - * {@link SoftApCapability#isFeatureSupported(int)} + * {@link SoftApCapability#areFeaturesSupported(int)} * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} * * <p> diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 7d4b63285eea..dece8559d70c 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -39,7 +39,6 @@ import android.net.ConnectivityManager; import android.net.DhcpInfo; import android.net.MacAddress; import android.net.Network; -import android.net.NetworkScore; import android.net.NetworkStack; import android.net.wifi.hotspot2.IProvisioningCallback; import android.net.wifi.hotspot2.OsuProvider; @@ -3769,11 +3768,19 @@ public class WifiManager { } /** - * Registers a callback for Soft AP. See {@link SoftApCallback}. Caller will receive the current - * soft AP state and number of connected devices immediately after a successful call to this API - * via callback. Note that receiving an immediate WIFI_AP_STATE_FAILED value for soft AP state - * indicates that the latest attempt to start soft AP has failed. Caller can unregister a - * previously registered callback using {@link #unregisterSoftApCallback} + * Registers a callback for Soft AP. See {@link SoftApCallback}. Caller will receive the + * following callbacks on registration: + * <ul> + * <li> {@link SoftApCallback#onStateChanged(int, int)}</li> + * <li> {@link SoftApCallback#onConnectedClientsChanged(List<WifiClient>)}</li> + * <li> {@link SoftApCallback#onInfoChanged(SoftApInfo)}</li> + * <li> {@link SoftApCallback#onCapabilityChanged(SoftApCapability)}</li> + * </ul> + * These will be dispatched on registration to provide the caller with the current state + * (and are not an indication of any current change). Note that receiving an immediate + * WIFI_AP_STATE_FAILED value for soft AP state indicates that the latest attempt to start + * soft AP has failed. Caller can unregister a previously registered callback using + * {@link #unregisterSoftApCallback} * <p> * Applications should have the * {@link android.Manifest.permission#NETWORK_SETTINGS NETWORK_SETTINGS} permission. Callers @@ -5075,7 +5082,7 @@ public class WifiManager { } /** - * Returns soft ap config from the backed up data. + * Returns soft ap config from the backed up data or null if data is invalid. * @param data byte stream in the same format produced by {@link #retrieveSoftApBackupData()} * * @hide @@ -5986,11 +5993,10 @@ public class WifiManager { * * @param sessionId The ID to indicate current Wi-Fi network connection obtained from * {@link WifiConnectedNetworkScorer#start(int)}. - * @param score The {@link android.net.NetworkScore} object representing the - * characteristics of current Wi-Fi network. Populated by connected network - * scorer in applications. + * @param score The score representing link quality of current Wi-Fi network connection. + * Populated by connected network scorer in applications.. */ - void onScoreChange(int sessionId, @NonNull NetworkScore score); + void onScoreChange(int sessionId, int score); /** * Called by applications to trigger an update of {@link WifiUsabilityStatsEntry}. @@ -6016,7 +6022,7 @@ public class WifiManager { } @Override - public void onScoreChange(int sessionId, @NonNull NetworkScore score) { + public void onScoreChange(int sessionId, int score) { try { mScoreChangeCallback.onScoreChange(sessionId, score); } catch (RemoteException e) { diff --git a/wifi/java/android/net/wifi/WifiOemMigrationHook.java b/wifi/java/android/net/wifi/WifiMigration.java index 5301dd013363..a3482d732a1b 100755 --- a/wifi/java/android/net/wifi/WifiOemMigrationHook.java +++ b/wifi/java/android/net/wifi/WifiMigration.java @@ -34,9 +34,9 @@ import java.util.List; * @hide */ @SystemApi -public final class WifiOemMigrationHook { +public final class WifiMigration { - private WifiOemMigrationHook() { } + private WifiMigration() { } /** * Container for all the wifi config data to migrate. @@ -161,16 +161,16 @@ public final class WifiOemMigrationHook { * Load data from OEM's config store. * <p> * Note: - * <li> OEM's need to implement {@link #loadFromConfigStore()} ()} only if their - * existing config store format or file locations differs from the vanilla AOSP implementation ( - * which is what the wifi mainline module understands). + * <li>OEMs need to implement {@link #loadFromConfigStore()} ()} only if their + * existing config store format or file locations differs from the vanilla AOSP implementation. * </li> - * <li> The wifi mainline module will invoke {@link #loadFromConfigStore()} method on every + * <li>The wifi mainline module will invoke {@link #loadFromConfigStore()} method on every * bootup, its the responsibility of the OEM implementation to ensure that this method returns * non-null data only on the first bootup. Once the migration is done, the OEM can safely delete - * their config store files and then return null on any subsequent reboots. The first & only - * relevant invocation of {@link #loadFromConfigStore()} occurs when a previously released - * device upgrades to the wifi mainline module from an OEM implementation of the wifi stack. + * their config store files when {@link #removeConfigStore()} is invoked. + * <li>The first & only relevant invocation of {@link #loadFromConfigStore()} occurs when a + * previously released device upgrades to the wifi mainline module from an OEM implementation + * of the wifi stack. * </li> * * @return Instance of {@link ConfigStoreMigrationData} for migrating data, null if no @@ -178,11 +178,27 @@ public final class WifiOemMigrationHook { */ @Nullable public static ConfigStoreMigrationData loadFromConfigStore() { - // Note: OEM's should add code to parse data from their config store format here! + // Note: OEMs should add code to parse data from their config store format here! return null; } /** + * Remove OEM's config store. + * <p> + * Note: + * <li>OEMs need to implement {@link #removeConfigStore()} only if their + * existing config store format or file locations differs from the vanilla AOSP implementation ( + * which is what the wifi mainline module understands). + * </li> + * <li> The wifi mainline module will invoke {@link #removeConfigStore()} after it migrates + * all the existing data retrieved from {@link #loadFromConfigStore()}. + * </li> + */ + public static void removeConfigStore() { + // Note: OEMs should remove their custom config store files here! + } + + /** * Container for all the wifi settings data to migrate. */ public static final class SettingsMigrationData implements Parcelable { diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index b4eb30b8cfe6..5e4891950466 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -36,6 +36,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.WorkSource; +import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; @@ -744,6 +745,25 @@ public class WifiScanner { public PnoNetwork(String ssid) { this.ssid = ssid; } + + @Override + public int hashCode() { + return Objects.hash(ssid, flags, authBitField); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof PnoNetwork)) { + return false; + } + PnoNetwork lhs = (PnoNetwork) obj; + return TextUtils.equals(this.ssid, lhs.ssid) + && this.flags == lhs.flags + && this.authBitField == lhs.authBitField; + } } /** Connected vs Disconnected PNO flag {@hide} */ |