diff options
240 files changed, 10936 insertions, 7152 deletions
diff --git a/Android.bp b/Android.bp index 7e038ce8d3ae..a80a5d3c6996 100644 --- a/Android.bp +++ b/Android.bp @@ -92,6 +92,7 @@ java_defaults { "core/java/android/app/IWallpaperManagerCallback.aidl", "core/java/android/app/admin/IDeviceAdminService.aidl", "core/java/android/app/admin/IDevicePolicyManager.aidl", + "core/java/android/app/admin/StartInstallingUpdateCallback.aidl", "core/java/android/app/trust/IStrongAuthTracker.aidl", "core/java/android/app/trust/ITrustManager.aidl", "core/java/android/app/trust/ITrustListener.aidl", @@ -347,6 +348,7 @@ java_defaults { "core/java/android/view/accessibility/IAccessibilityManagerClient.aidl", "core/java/android/view/autofill/IAutoFillManager.aidl", "core/java/android/view/autofill/IAutoFillManagerClient.aidl", + "core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl", "core/java/android/view/autofill/IAutofillWindowPresenter.aidl", "core/java/android/view/intelligence/IIntelligenceManager.aidl", "core/java/android/view/IApplicationToken.aidl", diff --git a/api/current.txt b/api/current.txt index e84bc8db67da..8adc4dd73e0f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1319,6 +1319,7 @@ package android { field public static final int supportsAssist = 16844016; // 0x10104f0 field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1 field public static final int supportsLocalInteraction = 16844047; // 0x101050f + field public static final int supportsMultipleDisplays = 16844183; // 0x1010597 field public static final int supportsPictureInPicture = 16844023; // 0x10104f7 field public static final int supportsRtl = 16843695; // 0x10103af field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb @@ -5229,6 +5230,7 @@ package android.app { ctor public Notification(android.os.Parcel); method public android.app.Notification clone(); method public int describeContents(); + method public boolean getAllowSystemGeneratedContextualActions(); method public android.app.PendingIntent getAppOverlayIntent(); method public int getBadgeIconType(); method public java.lang.String getChannelId(); @@ -5460,6 +5462,7 @@ package android.app { method public android.app.Notification.Style getStyle(); method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification); method public android.app.Notification.Builder setActions(android.app.Notification.Action...); + method public android.app.Notification.Builder setAllowSystemGeneratedContextualActions(boolean); method public android.app.Notification.Builder setAppOverlayIntent(android.app.PendingIntent); method public android.app.Notification.Builder setAutoCancel(boolean); method public android.app.Notification.Builder setBadgeIconType(int); @@ -6352,6 +6355,7 @@ package android.app { method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager); method public android.graphics.drawable.Drawable loadThumbnail(android.content.pm.PackageManager); method public boolean supportsAmbientMode(); + method public boolean supportsMultipleDisplays(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.app.WallpaperInfo> CREATOR; } @@ -6598,6 +6602,7 @@ package android.app.admin { method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String); method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean); method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, int); + method public void installSystemUpdate(android.content.ComponentName, android.net.Uri, java.util.concurrent.Executor, android.app.admin.DevicePolicyManager.InstallUpdateCallback); method public boolean isActivePasswordSufficient(); method public boolean isAdminActive(android.content.ComponentName); method public boolean isAffiliatedUser(); @@ -6840,6 +6845,16 @@ package android.app.admin { field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2 } + public static abstract class DevicePolicyManager.InstallUpdateCallback { + ctor public DevicePolicyManager.InstallUpdateCallback(); + method public void onInstallUpdateError(int, java.lang.String); + field public static final int UPDATE_ERROR_BATTERY_LOW = 5; // 0x5 + field public static final int UPDATE_ERROR_FILE_NOT_FOUND = 4; // 0x4 + field public static final int UPDATE_ERROR_INCORRECT_OS_VERSION = 2; // 0x2 + field public static final int UPDATE_ERROR_UNKNOWN = 1; // 0x1 + field public static final int UPDATE_ERROR_UPDATE_FILE_INVALID = 3; // 0x3 + } + public static abstract interface DevicePolicyManager.OnClearApplicationUserDataListener { method public abstract void onApplicationUserDataCleared(java.lang.String, boolean); } @@ -10118,6 +10133,7 @@ package android.content { field public static final java.lang.String ACTION_TIMEZONE_CHANGED = "android.intent.action.TIMEZONE_CHANGED"; field public static final java.lang.String ACTION_TIME_CHANGED = "android.intent.action.TIME_SET"; field public static final java.lang.String ACTION_TIME_TICK = "android.intent.action.TIME_TICK"; + field public static final java.lang.String ACTION_TRANSLATE = "android.intent.action.TRANSLATE"; field public static final java.lang.String ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED"; field public static final deprecated java.lang.String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED"; field public static final deprecated java.lang.String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED"; @@ -22628,7 +22644,7 @@ package android.location { method public abstract void onLocationChanged(android.location.Location); method public abstract void onProviderDisabled(java.lang.String); method public abstract void onProviderEnabled(java.lang.String); - method public abstract void onStatusChanged(java.lang.String, int, android.os.Bundle); + method public abstract deprecated void onStatusChanged(java.lang.String, int, android.os.Bundle); } public class LocationManager { @@ -22640,7 +22656,7 @@ package android.location { method public void addTestProvider(java.lang.String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int); method public void clearTestProviderEnabled(java.lang.String); method public void clearTestProviderLocation(java.lang.String); - method public void clearTestProviderStatus(java.lang.String); + method public deprecated void clearTestProviderStatus(java.lang.String); method public java.util.List<java.lang.String> getAllProviders(); method public java.lang.String getBestProvider(android.location.Criteria, boolean); method public java.lang.String getGnssHardwareModelName(); @@ -22677,7 +22693,7 @@ package android.location { method public boolean sendExtraCommand(java.lang.String, java.lang.String, android.os.Bundle); method public void setTestProviderEnabled(java.lang.String, boolean); method public void setTestProviderLocation(java.lang.String, android.location.Location); - method public void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long); + method public deprecated void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long); method public void unregisterGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback); method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback); method public void unregisterGnssStatusCallback(android.location.GnssStatus.Callback); @@ -22685,7 +22701,7 @@ package android.location { field public static final java.lang.String KEY_LOCATION_CHANGED = "location"; field public static final java.lang.String KEY_PROVIDER_ENABLED = "providerEnabled"; field public static final java.lang.String KEY_PROXIMITY_ENTERING = "entering"; - field public static final java.lang.String KEY_STATUS_CHANGED = "status"; + field public static final deprecated java.lang.String KEY_STATUS_CHANGED = "status"; field public static final java.lang.String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED"; field public static final java.lang.String NETWORK_PROVIDER = "network"; field public static final java.lang.String PASSIVE_PROVIDER = "passive"; @@ -22704,9 +22720,9 @@ package android.location { method public boolean supportsAltitude(); method public boolean supportsBearing(); method public boolean supportsSpeed(); - field public static final int AVAILABLE = 2; // 0x2 - field public static final int OUT_OF_SERVICE = 0; // 0x0 - field public static final int TEMPORARILY_UNAVAILABLE = 1; // 0x1 + field public static final deprecated int AVAILABLE = 2; // 0x2 + field public static final deprecated int OUT_OF_SERVICE = 0; // 0x0 + field public static final deprecated int TEMPORARILY_UNAVAILABLE = 1; // 0x1 } public abstract interface OnNmeaMessageListener { @@ -22935,7 +22951,7 @@ package android.media { method public void adjustSuggestedStreamVolume(int, int, int); method public void adjustVolume(int, int); method public void dispatchMediaKeyEvent(android.view.KeyEvent); - method public static int generateAudioSessionId(); + method public int generateAudioSessionId(); method public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations(); method public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations(); method public android.media.AudioDeviceInfo[] getDevices(int); @@ -24575,6 +24591,7 @@ package android.media { field public static final java.lang.String KEY_WIDTH = "width"; field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm"; field public static final java.lang.String MIMETYPE_AUDIO_AC3 = "audio/ac3"; + field public static final java.lang.String MIMETYPE_AUDIO_AC4 = "audio/ac4"; field public static final java.lang.String MIMETYPE_AUDIO_AMR_NB = "audio/3gpp"; field public static final java.lang.String MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb"; field public static final java.lang.String MIMETYPE_AUDIO_EAC3 = "audio/eac3"; @@ -29117,15 +29134,18 @@ package android.net.wifi { method public android.net.wifi.WifiNetworkSuggestion buildNetworkSuggestion(); method public android.net.wifi.WifiNetworkConfigBuilder setBssid(android.net.MacAddress); method public android.net.wifi.WifiNetworkConfigBuilder setBssidPattern(android.net.MacAddress, android.net.MacAddress); - method public android.net.wifi.WifiNetworkConfigBuilder setEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig); method public android.net.wifi.WifiNetworkConfigBuilder setIsAppInteractionRequired(); + method public android.net.wifi.WifiNetworkConfigBuilder setIsEnhancedOpen(); method public android.net.wifi.WifiNetworkConfigBuilder setIsHiddenSsid(); method public android.net.wifi.WifiNetworkConfigBuilder setIsMetered(); method public android.net.wifi.WifiNetworkConfigBuilder setIsUserInteractionRequired(); method public android.net.wifi.WifiNetworkConfigBuilder setPriority(int); - method public android.net.wifi.WifiNetworkConfigBuilder setPskPassphrase(java.lang.String); method public android.net.wifi.WifiNetworkConfigBuilder setSsid(java.lang.String); method public android.net.wifi.WifiNetworkConfigBuilder setSsidPattern(android.os.PatternMatcher); + method public android.net.wifi.WifiNetworkConfigBuilder setWpa2EnterpriseConfig(android.net.wifi.WifiEnterpriseConfig); + method public android.net.wifi.WifiNetworkConfigBuilder setWpa2Passphrase(java.lang.String); + method public android.net.wifi.WifiNetworkConfigBuilder setWpa3EnterpriseConfig(android.net.wifi.WifiEnterpriseConfig); + method public android.net.wifi.WifiNetworkConfigBuilder setWpa3Passphrase(java.lang.String); } public final class WifiNetworkSuggestion implements android.os.Parcelable { @@ -42871,6 +42891,7 @@ package android.telephony { field public static final java.lang.String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool"; field public static final java.lang.String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool"; field public static final java.lang.String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool"; + field public static final java.lang.String KEY_HIDE_PRESET_APN_DETAILS_BOOL = "hide_preset_apn_details_bool"; field public static final java.lang.String KEY_HIDE_SIM_LOCK_SETTINGS_BOOL = "hide_sim_lock_settings_bool"; field public static final java.lang.String KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL = "ignore_sim_network_locked_events_bool"; field public static final java.lang.String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int"; @@ -43218,9 +43239,9 @@ package android.telephony { public class MbmsGroupCallSession implements java.lang.AutoCloseable { method public void close(); - method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsGroupCallSessionCallback); + method public static android.telephony.MbmsGroupCallSession create(android.content.Context, int, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback); method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback); - method public android.telephony.mbms.GroupCall startGroupCall(java.util.concurrent.Executor, long, int[], int[], android.telephony.mbms.GroupCallCallback); + method public android.telephony.mbms.GroupCall startGroupCall(long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, java.util.concurrent.Executor, android.telephony.mbms.GroupCallCallback); } public class MbmsStreamingSession implements java.lang.AutoCloseable { @@ -44225,7 +44246,7 @@ package android.telephony.mbms { public class GroupCall implements java.lang.AutoCloseable { method public void close(); method public long getTmgi(); - method public void updateGroupCall(int[], int[]); + method public void updateGroupCall(java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>); field public static final int REASON_BY_USER_REQUEST = 1; // 0x1 field public static final int REASON_FREQUENCY_CONFLICT = 3; // 0x3 field public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6; // 0x6 @@ -44237,11 +44258,10 @@ package android.telephony.mbms { field public static final int STATE_STOPPED = 1; // 0x1 } - public class GroupCallCallback { - ctor public GroupCallCallback(); - method public void onBroadcastSignalStrengthUpdated(int); - method public void onError(int, java.lang.String); - method public void onGroupCallStateChanged(int, int); + public abstract interface GroupCallCallback { + method public abstract void onBroadcastSignalStrengthUpdated(int); + method public abstract void onError(int, java.lang.String); + method public abstract void onGroupCallStateChanged(int, int); field public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; // 0xffffffff } @@ -44281,6 +44301,11 @@ package android.telephony.mbms { field public static final int ERROR_UNABLE_TO_READ_SIM = 206; // 0xce } + public static class MbmsErrors.GroupCallErrors { + field public static final int ERROR_DUPLICATE_START_GROUP_CALL = 502; // 0x1f6 + field public static final int ERROR_UNABLE_TO_START_SERVICE = 501; // 0x1f5 + } + public static class MbmsErrors.InitializationErrors { field public static final int ERROR_APP_PERMISSIONS_NOT_GRANTED = 102; // 0x66 field public static final int ERROR_DUPLICATE_INITIALIZE = 101; // 0x65 @@ -44293,12 +44318,11 @@ package android.telephony.mbms { field public static final int ERROR_UNABLE_TO_START_SERVICE = 302; // 0x12e } - public class MbmsGroupCallSessionCallback { - ctor public MbmsGroupCallSessionCallback(); - method public void onAvailableSaisUpdated(java.util.List<java.lang.Integer>, java.util.List<java.util.List<java.lang.Integer>>); - method public void onError(int, java.lang.String); - method public void onMiddlewareReady(); - method public void onServiceInterfaceAvailable(java.lang.String, int); + public abstract interface MbmsGroupCallSessionCallback { + method public abstract void onAvailableSaisUpdated(java.util.List<java.lang.Integer>, java.util.List<java.util.List<java.lang.Integer>>); + method public abstract void onError(int, java.lang.String); + method public abstract void onMiddlewareReady(); + method public abstract void onServiceInterfaceAvailable(java.lang.String, int); } public class MbmsStreamingSessionCallback { @@ -49088,7 +49112,7 @@ package android.view { method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent); method public void onProvideAutofillStructure(android.view.ViewStructure, int); method public void onProvideAutofillVirtualStructure(android.view.ViewStructure, int); - method public boolean onProvideContentCaptureStructure(android.view.ViewStructure, int); + method public void onProvideContentCaptureStructure(android.view.ViewStructure, int); method public void onProvideStructure(android.view.ViewStructure); method public void onProvideVirtualStructure(android.view.ViewStructure); method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int); diff --git a/api/system-current.txt b/api/system-current.txt index 79333c70f5fd..f963c10e6272 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -441,10 +441,10 @@ package android.app { } public class KeyguardManager { - method public void setPrivateNotificationsAllowed(boolean); - method public boolean getPrivateNotificationsAllowed(); method public android.content.Intent createConfirmFactoryResetCredentialIntent(java.lang.CharSequence, java.lang.CharSequence, java.lang.CharSequence); + method public boolean getPrivateNotificationsAllowed(); method public void requestDismissKeyguard(android.app.Activity, java.lang.CharSequence, android.app.KeyguardManager.KeyguardDismissCallback); + method public void setPrivateNotificationsAllowed(boolean); } public class Notification implements android.os.Parcelable { @@ -1225,6 +1225,7 @@ package android.content.pm { method public abstract java.util.List<android.content.pm.InstantAppInfo> getInstantApps(); method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String); method public abstract int getIntentVerificationStatusAsUser(java.lang.String, int); + method public android.content.pm.PackageInfo getPackageInfoAsUser(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle); method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle); method public abstract int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; @@ -3938,6 +3939,7 @@ package android.os { public final class ConfigUpdate { field public static final java.lang.String ACTION_UPDATE_CARRIER_ID_DB = "android.os.action.UPDATE_CARRIER_ID_DB"; field public static final java.lang.String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS"; + field public static final java.lang.String ACTION_UPDATE_CONVERSATION_ACTIONS = "android.intent.action.UPDATE_CONVERSATION_ACTIONS"; field public static final java.lang.String ACTION_UPDATE_CT_LOGS = "android.intent.action.UPDATE_CT_LOGS"; field public static final java.lang.String ACTION_UPDATE_INTENT_FIREWALL = "android.intent.action.UPDATE_INTENT_FIREWALL"; field public static final java.lang.String ACTION_UPDATE_LANG_ID = "android.intent.action.UPDATE_LANG_ID"; @@ -4936,12 +4938,47 @@ package android.service.euicc { package android.service.intelligence { + public final class FillCallback { + method public void onSuccess(android.service.intelligence.FillResponse); + } + + public final class FillController { + method public void autofill(java.util.List<android.util.Pair<android.view.autofill.AutofillId, android.view.autofill.AutofillValue>>); + } + + public final class FillRequest { + method public android.view.autofill.AutofillId getFocusedId(); + method public android.service.intelligence.PresentationParams getPresentationParams(); + method public android.service.intelligence.InteractionSessionId getSessionId(); + } + + public final class FillResponse implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.intelligence.FillResponse> CREATOR; + } + + public static class FillResponse.Builder { + ctor public FillResponse.Builder(); + method public android.service.intelligence.FillResponse build(); + method public android.service.intelligence.FillResponse.Builder setFillWindow(android.service.intelligence.FillWindow); + method public android.service.intelligence.FillResponse.Builder setIgnoredIds(java.util.List<android.view.autofill.AutofillId>); + } + + public final class FillWindow { + ctor public FillWindow(); + method public void destroy(); + method public boolean update(android.service.intelligence.PresentationParams.Area, android.view.View, long); + field public static final long FLAG_METADATA_ADDRESS = 1L; // 0x1L + } + public abstract class IntelligenceService extends android.app.Service { ctor public IntelligenceService(); method public void onActivitySnapshot(android.service.intelligence.InteractionSessionId, android.service.intelligence.SnapshotData); method public abstract void onContentCaptureEvent(android.service.intelligence.InteractionSessionId, java.util.List<android.view.intelligence.ContentCaptureEvent>); method public void onCreateInteractionSession(android.service.intelligence.InteractionContext, android.service.intelligence.InteractionSessionId); method public void onDestroyInteractionSession(android.service.intelligence.InteractionSessionId); + method public void onFillRequest(android.service.intelligence.InteractionSessionId, android.service.intelligence.FillRequest, android.os.CancellationSignal, android.service.intelligence.FillController, android.service.intelligence.FillCallback); field public static final java.lang.String SERVICE_INTERFACE = "android.service.intelligence.IntelligenceService"; } @@ -4963,6 +5000,23 @@ package android.service.intelligence { field public static final android.os.Parcelable.Creator<android.service.intelligence.InteractionSessionId> CREATOR; } + public abstract class PresentationParams { + method public int getFlags(); + method public android.service.intelligence.PresentationParams.Area getFullArea(); + method public android.service.intelligence.PresentationParams.Area getSuggestionArea(); + field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2 + field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4 + field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8 + field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1 + field public static final int FLAG_HOST_IME = 16; // 0x10 + field public static final int FLAG_HOST_SYSTEM = 32; // 0x20 + } + + public static abstract class PresentationParams.Area { + method public android.graphics.Rect getBounds(); + method public android.service.intelligence.PresentationParams.Area getSubArea(android.graphics.Rect); + } + public final class SnapshotData implements android.os.Parcelable { method public int describeContents(); method public android.app.assist.AssistContent getAssistContent(); @@ -7023,9 +7077,9 @@ package android.telephony.mbms.vendor { method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException; method public void onAppCallbackDied(int, int); method public android.os.IBinder onBind(android.content.Intent); - method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback); + method public int startGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, android.telephony.mbms.GroupCallCallback); method public void stopGroupCall(int, long); - method public void updateGroupCall(int, long, int[], int[]); + method public void updateGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>); } public class MbmsStreamingServiceBase extends android.os.Binder { @@ -7517,7 +7571,7 @@ package android.webkit { method public default void onMovedToDisplay(int, android.content.res.Configuration); method public abstract void onOverScrolled(int, int, boolean, boolean); method public default void onProvideAutofillVirtualStructure(android.view.ViewStructure, int); - method public default boolean onProvideContentCaptureStructure(android.view.ViewStructure, int); + method public default void onProvideContentCaptureStructure(android.view.ViewStructure, int); method public abstract void onProvideVirtualStructure(android.view.ViewStructure); method public abstract void onScrollChanged(int, int, int, int); method public abstract void onSizeChanged(int, int, int, int); diff --git a/api/test-current.txt b/api/test-current.txt index 0fd9e8df2811..0fa83f11a4ba 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -985,6 +985,7 @@ package android.provider { public static final class Settings.Global extends android.provider.Settings.NameValueTable { field public static final java.lang.String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages"; + field public static final java.lang.String AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS = "autofill_smart_suggestion_emulation_flags"; field public static final java.lang.String AUTOMATIC_POWER_SAVER_MODE = "automatic_power_saver_mode"; field public static final java.lang.String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD = "dynamic_power_savings_disable_threshold"; field public static final java.lang.String DYNAMIC_POWER_SAVINGS_ENABLED = "dynamic_power_savings_enabled"; @@ -1320,9 +1321,9 @@ package android.telephony.mbms.vendor { method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException; method public void onAppCallbackDied(int, int); method public android.os.IBinder onBind(android.content.Intent); - method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback); + method public int startGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, android.telephony.mbms.GroupCallCallback); method public void stopGroupCall(int, long); - method public void updateGroupCall(int, long, int[], int[]); + method public void updateGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>); } public class MbmsStreamingServiceBase extends android.os.Binder { diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index a826ec7c717e..3723fce9d4f0 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -17,6 +17,7 @@ package com.android.commands.bmgr; import android.annotation.IntDef; +import android.annotation.UserIdInt; import android.app.backup.BackupManager; import android.app.backup.BackupManagerMonitor; import android.app.backup.BackupProgress; @@ -29,6 +30,7 @@ import android.app.backup.IRestoreSession; import android.app.backup.ISelectBackupTransportCallback; import android.app.backup.RestoreSet; import android.content.ComponentName; +import android.content.Context; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.os.Bundle; @@ -37,8 +39,10 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.util.ArraySet; +import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -49,20 +53,34 @@ import java.util.List; import java.util.Set; import java.util.concurrent.CountDownLatch; -public final class Bmgr { - IBackupManager mBmgr; - IRestoreSession mRestore; +/** + * Adb shell command for {@link android.app.backup.IBackupManager}. + */ +public class Bmgr { + public static final String TAG = "Bmgr"; + + private final IBackupManager mBmgr; + private IRestoreSession mRestore; - static final String BMGR_NOT_RUNNING_ERR = + private static final String BMGR_NOT_RUNNING_ERR = "Error: Could not access the Backup Manager. Is the system running?"; - static final String TRANSPORT_NOT_RUNNING_ERR = + private static final String TRANSPORT_NOT_RUNNING_ERR = "Error: Could not access the backup transport. Is the system running?"; - static final String PM_NOT_RUNNING_ERR = + private static final String PM_NOT_RUNNING_ERR = "Error: Could not access the Package Manager. Is the system running?"; private String[] mArgs; private int mNextArg; + @VisibleForTesting + Bmgr(IBackupManager bmgr) { + mBmgr = bmgr; + } + + Bmgr() { + mBmgr = IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE)); + } + public static void main(String[] args) { try { new Bmgr().run(args); @@ -78,71 +96,73 @@ public final class Bmgr { return; } - if (!isBmgrActive()) { + mArgs = args; + mNextArg = 0; + int userId = parseUserId(); + String op = nextArg(); + Slog.v(TAG, "Running " + op + " for user:" + userId); + + if (!isBmgrActive(userId)) { return; } - mArgs = args; - String op = args[0]; - mNextArg = 1; - if ("enabled".equals(op)) { - doEnabled(); + doEnabled(userId); return; } if ("enable".equals(op)) { - doEnable(); + doEnable(userId); return; } if ("run".equals(op)) { - doRun(); + doRun(userId); return; } if ("backup".equals(op)) { - doBackup(); + doBackup(userId); return; } if ("init".equals(op)) { - doInit(); + doInit(userId); return; } if ("list".equals(op)) { - doList(); + doList(userId); return; } if ("restore".equals(op)) { - doRestore(); + doRestore(userId); return; } if ("transport".equals(op)) { - doTransport(); + doTransport(userId); return; } if ("wipe".equals(op)) { - doWipe(); + doWipe(userId); return; } if ("fullbackup".equals(op)) { - doFullTransportBackup(); + doFullTransportBackup(userId); return; } if ("backupnow".equals(op)) { - doBackupNow(); + doBackupNow(userId); return; } if ("cancel".equals(op)) { - doCancel(); + doCancel(userId); return; } @@ -155,15 +175,14 @@ public final class Bmgr { showUsage(); } - private boolean isBmgrActive() { - mBmgr = IBackupManager.Stub.asInterface(ServiceManager.getService("backup")); + boolean isBmgrActive(@UserIdInt int userId) { if (mBmgr == null) { System.err.println(BMGR_NOT_RUNNING_ERR); return false; } try { - if (!mBmgr.isBackupServiceActive(UserHandle.USER_SYSTEM)) { + if (!mBmgr.isBackupServiceActive(userId)) { System.err.println(BMGR_NOT_RUNNING_ERR); return false; } @@ -180,7 +199,7 @@ public final class Bmgr { return enabled ? "enabled" : "disabled"; } - private void doEnabled() { + private void doEnabled(@UserIdInt int userId) { try { boolean isEnabled = mBmgr.isBackupEnabled(); System.out.println("Backup Manager currently " @@ -191,7 +210,7 @@ public final class Bmgr { } } - private void doEnable() { + private void doEnable(@UserIdInt int userId) { String arg = nextArg(); if (arg == null) { showUsage(); @@ -211,7 +230,7 @@ public final class Bmgr { } } - private void doRun() { + void doRun(@UserIdInt int userId) { try { mBmgr.backupNow(); } catch (RemoteException e) { @@ -220,7 +239,7 @@ public final class Bmgr { } } - private void doBackup() { + private void doBackup(@UserIdInt int userId) { String pkg = nextArg(); if (pkg == null) { showUsage(); @@ -235,7 +254,7 @@ public final class Bmgr { } } - private void doFullTransportBackup() { + private void doFullTransportBackup(@UserIdInt int userId) { System.out.println("Performing full transport backup"); String pkg; @@ -354,8 +373,8 @@ public final class Bmgr { } } - private void backupNowAllPackages(boolean nonIncrementalBackup, @Monitor int monitorState) { - int userId = UserHandle.USER_SYSTEM; + private void backupNowAllPackages(@UserIdInt int userId, boolean nonIncrementalBackup, + @Monitor int monitorState) { IPackageManager mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); if (mPm == null) { @@ -379,11 +398,13 @@ public final class Bmgr { System.err.println(e.toString()); System.err.println(BMGR_NOT_RUNNING_ERR); } - backupNowPackages(Arrays.asList(filteredPackages), nonIncrementalBackup, monitorState); + backupNowPackages(userId, Arrays.asList(filteredPackages), nonIncrementalBackup, + monitorState); } } private void backupNowPackages( + @UserIdInt int userId, List<String> packages, boolean nonIncrementalBackup, @Monitor int monitorState) { int flags = 0; if (nonIncrementalBackup) { @@ -412,7 +433,7 @@ public final class Bmgr { } } - private void doBackupNow() { + private void doBackupNow(@UserIdInt int userId) { String pkg; boolean backupAll = false; boolean nonIncrementalBackup = false; @@ -439,20 +460,20 @@ public final class Bmgr { if (allPkgs.size() == 0) { System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") + "incremental backup for all packages."); - backupNowAllPackages(nonIncrementalBackup, monitor); + backupNowAllPackages(userId, nonIncrementalBackup, monitor); } else { System.err.println("Provide only '--all' flag or list of packages."); } } else if (allPkgs.size() > 0) { System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") + "incremental backup for " + allPkgs.size() +" requested packages."); - backupNowPackages(allPkgs, nonIncrementalBackup, monitor); + backupNowPackages(userId, allPkgs, nonIncrementalBackup, monitor); } else { System.err.println("Provide '--all' flag or list of packages."); } } - private void doCancel() { + private void doCancel(@UserIdInt int userId) { String arg = nextArg(); if ("backups".equals(arg)) { try { @@ -467,7 +488,7 @@ public final class Bmgr { System.err.println("Unknown command."); } - private void doTransport() { + private void doTransport(@UserIdInt int userId) { try { String which = nextArg(); if (which == null) { @@ -531,7 +552,7 @@ public final class Bmgr { } } - private void doWipe() { + private void doWipe(@UserIdInt int userId) { String transport = nextArg(); if (transport == null) { showUsage(); @@ -563,7 +584,7 @@ public final class Bmgr { } } - private void doInit() { + private void doInit(@UserIdInt int userId) { ArraySet<String> transports = new ArraySet<>(); String transport; while ((transport = nextArg()) != null) { @@ -586,7 +607,7 @@ public final class Bmgr { } } - private void doList() { + private void doList(@UserIdInt int userId) { String arg = nextArg(); // sets, transports, packages set# if ("transports".equals(arg)) { doListTransports(); @@ -603,8 +624,6 @@ public final class Bmgr { if ("sets".equals(arg)) { doListRestoreSets(); - } else if ("transports".equals(arg)) { - doListTransports(); } mRestore.endRestoreSession(); @@ -717,7 +736,7 @@ public final class Bmgr { } } - private void doRestore() { + private void doRestore(@UserIdInt int userId) { String arg = nextArg(); if (arg == null) { showUsage(); @@ -830,8 +849,18 @@ public final class Bmgr { return arg; } + private int parseUserId() { + String arg = nextArg(); + if ("--user".equals(arg)) { + return UserHandle.parseUserArg(nextArg()); + } else { + mNextArg--; + return UserHandle.USER_SYSTEM; + } + } + private static void showUsage() { - System.err.println("usage: bmgr [backup|restore|list|transport|run]"); + System.err.println("usage: bmgr [--user <userId>] [backup|restore|list|transport|run]"); System.err.println(" bmgr backup PACKAGE"); System.err.println(" bmgr enable BOOL"); System.err.println(" bmgr enabled"); @@ -847,6 +876,10 @@ public final class Bmgr { System.err.println(" bmgr cancel backups"); System.err.println(" bmgr init TRANSPORT..."); System.err.println(""); + System.err.println("The '--user' option specifies the user on which the operation is run."); + System.err.println("It must be the first argument before the operation."); + System.err.println("The default value is 0 which is the system user."); + System.err.println(""); System.err.println("The 'backup' command schedules a backup pass for the named package."); System.err.println("Note that the backup pass will effectively be a no-op if the package"); System.err.println("does not actually have changed data to store."); diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index 5a6c813fd202..7d675cebfb33 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -18,7 +18,7 @@ cc_library { tidy: true, tidy_flags: [ "-system-headers", - "-warnings-as-errors=*", +// b/120024673 "-warnings-as-errors=*", ], srcs: [ "libidmap2/BinaryStreamVisitor.cpp", @@ -64,7 +64,7 @@ cc_test { tidy: true, tidy_flags: [ "-system-headers", - "-warnings-as-errors=*", +// b/120024673 "-warnings-as-errors=*", ], srcs: [ "tests/BinaryStreamVisitorTests.cpp", @@ -118,7 +118,7 @@ cc_binary { tidy: true, tidy_flags: [ "-system-headers", - "-warnings-as-errors=*", +// b/120024673 "-warnings-as-errors=*", ], srcs: [ "idmap2/Create.cpp", @@ -165,7 +165,7 @@ cc_binary { ], tidy_flags: [ "-system-headers", - "-warnings-as-errors=*", +// b/120024673 "-warnings-as-errors=*", ], srcs: [ ":idmap2_aidl", diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 8a770b936563..ab1bf3611155 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -189,10 +189,10 @@ Landroid/app/IUiModeManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app Landroid/app/IUiModeManager;->disableCarMode(I)V Landroid/app/IUserSwitchObserver$Stub;-><init>()V Landroid/app/IWallpaperManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IWallpaperManager; -Landroid/app/IWallpaperManager;->getHeightHint()I +Landroid/app/IWallpaperManager;->getHeightHint(I)I Landroid/app/IWallpaperManager;->getWallpaper(Ljava/lang/String;Landroid/app/IWallpaperManagerCallback;ILandroid/os/Bundle;I)Landroid/os/ParcelFileDescriptor; Landroid/app/IWallpaperManager;->getWallpaperInfo(I)Landroid/app/WallpaperInfo; -Landroid/app/IWallpaperManager;->getWidthHint()I +Landroid/app/IWallpaperManager;->getWidthHint(I)I Landroid/app/IWallpaperManager;->hasNamedWallpaper(Ljava/lang/String;)Z Landroid/app/IWallpaperManager;->setWallpaperComponent(Landroid/content/ComponentName;)V Landroid/app/IWallpaperManagerCallback$Stub;-><init>()V diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 805fb6829129..41166dd40e56 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -157,7 +157,6 @@ import com.android.internal.os.RuntimeInit; import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; -import com.android.internal.util.Preconditions; import com.android.internal.util.function.pooled.PooledLambda; import com.android.org.conscrypt.OpenSSLSocketImpl; import com.android.org.conscrypt.TrustedCertificateStore; @@ -5324,16 +5323,6 @@ public final class ActivityThread extends ClientTransactionHandler { } } - /** - * Updates the application info. - * - * This only works in the system process. Must be called on the main thread. - */ - public void handleSystemApplicationInfoChanged(@NonNull ApplicationInfo ai) { - Preconditions.checkState(mSystemThread, "Must only be called in the system process"); - handleApplicationInfoChanged(ai); - } - void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) { // Updates triggered by package installation go through a package update // receiver. Here we try to capture ApplicationInfo changes that are diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index 5ef4be18ef2f..00547b4a5ce4 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -87,24 +87,24 @@ interface IWallpaperManager { /** * Sets the dimension hint for the wallpaper. These hints indicate the desired - * minimum width and height for the wallpaper. + * minimum width and height for the wallpaper in a particular display. */ - void setDimensionHints(in int width, in int height, in String callingPackage); + void setDimensionHints(in int width, in int height, in String callingPackage, int displayId); /** - * Returns the desired minimum width for the wallpaper. + * Returns the desired minimum width for the wallpaper in a particular display. */ - int getWidthHint(); + int getWidthHint(int displayId); /** - * Returns the desired minimum height for the wallpaper. + * Returns the desired minimum height for the wallpaper in a particular display. */ - int getHeightHint(); + int getHeightHint(int displayId); /** * Sets extra padding that we would like the wallpaper to have outside of the display. */ - void setDisplayPadding(in Rect padding, in String callingPackage); + void setDisplayPadding(in Rect padding, in String callingPackage, int displayId); /** * Returns the name of the wallpaper. Private API. diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index f2a3e441ce95..75b56f31c887 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1338,6 +1338,11 @@ public class Notification implements Parcelable private int mBadgeIcon = BADGE_ICON_NONE; /** + * Determines whether the platform can generate contextual actions for a notification. + */ + private boolean mAllowSystemGeneratedContextualActions = true; + + /** * Structure to encapsulate a named action that can be shown as part of this notification. * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is * selected by the user. @@ -2238,6 +2243,8 @@ public class Notification implements Parcelable if (parcel.readInt() != 0) { mAppOverlayIntent = PendingIntent.CREATOR.createFromParcel(parcel); } + + mAllowSystemGeneratedContextualActions = parcel.readBoolean(); } @Override @@ -2353,6 +2360,7 @@ public class Notification implements Parcelable that.mSettingsText = this.mSettingsText; that.mGroupAlertBehavior = this.mGroupAlertBehavior; that.mAppOverlayIntent = this.mAppOverlayIntent; + that.mAllowSystemGeneratedContextualActions = this.mAllowSystemGeneratedContextualActions; if (!heavy) { that.lightenPayload(); // will clean out extras @@ -2681,6 +2689,8 @@ public class Notification implements Parcelable parcel.writeInt(0); } + parcel.writeBoolean(mAllowSystemGeneratedContextualActions); + // mUsesStandardHeader is not written because it should be recomputed in listeners } @@ -3101,6 +3111,10 @@ public class Notification implements Parcelable return mAppOverlayIntent; } + public boolean getAllowSystemGeneratedContextualActions() { + return mAllowSystemGeneratedContextualActions; + } + /** * The small icon representing this notification in the status bar and content view. * @@ -5657,6 +5671,15 @@ public class Notification implements Parcelable } /** + * Determines whether the platform can generate contextual actions for a notification. + * By default this is true. + */ + public Builder setAllowSystemGeneratedContextualActions(boolean allowed) { + mN.mAllowSystemGeneratedContextualActions = allowed; + return this; + } + + /** * @deprecated Use {@link #build()} instead. */ @Deprecated diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java index e33d1fed4b4c..3ea3da25e2fc 100644 --- a/core/java/android/app/WallpaperInfo.java +++ b/core/java/android/app/WallpaperInfo.java @@ -36,6 +36,7 @@ import android.service.wallpaper.WallpaperService; import android.util.AttributeSet; import android.util.Printer; import android.util.Xml; +import android.view.SurfaceHolder; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -79,6 +80,7 @@ public final class WallpaperInfo implements Parcelable { final boolean mShowMetadataInPreview; final boolean mSupportsAmbientMode; final String mSettingsSliceUri; + final boolean mSupportMultipleDisplays; /** * Constructor. @@ -143,6 +145,9 @@ public final class WallpaperInfo implements Parcelable { false); mSettingsSliceUri = sa.getString( com.android.internal.R.styleable.Wallpaper_settingsSliceUri); + mSupportMultipleDisplays = sa.getBoolean( + com.android.internal.R.styleable.Wallpaper_supportsMultipleDisplays, + false); sa.recycle(); } catch (NameNotFoundException e) { @@ -163,6 +168,7 @@ public final class WallpaperInfo implements Parcelable { mShowMetadataInPreview = source.readInt() != 0; mSupportsAmbientMode = source.readInt() != 0; mSettingsSliceUri = source.readString(); + mSupportMultipleDisplays = source.readInt() != 0; mService = ResolveInfo.CREATOR.createFromParcel(source); } @@ -358,6 +364,19 @@ public final class WallpaperInfo implements Parcelable { return Uri.parse(mSettingsSliceUri); } + /** + * Returns whether this wallpaper service can support multiple engines to render on each surface + * independently. An example use case is a multi-display set-up where the wallpaper service can + * render surfaces to each of the connected displays. + * + * @see WallpaperService#onCreateEngine() + * @see WallpaperService.Engine#onCreate(SurfaceHolder) + * @return {@code true} if multiple engines can render independently on each surface. + */ + public boolean supportsMultipleDisplays() { + return mSupportMultipleDisplays; + } + public void dump(Printer pw, String prefix) { pw.println(prefix + "Service:"); mService.dump(pw, prefix + " "); @@ -387,6 +406,7 @@ public final class WallpaperInfo implements Parcelable { dest.writeInt(mShowMetadataInPreview ? 1 : 0); dest.writeInt(mSupportsAmbientMode ? 1 : 0); dest.writeString(mSettingsSliceUri); + dest.writeInt(mSupportMultipleDisplays ? 1 : 0); mService.writeToParcel(dest, flags); } diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index bebe79e41e5b..27471cac10e9 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -1485,7 +1485,7 @@ public class WallpaperManager { throw new RuntimeException(new DeadSystemException()); } try { - return sGlobals.mService.getWidthHint(); + return sGlobals.mService.getWidthHint(mContext.getDisplayId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1511,7 +1511,7 @@ public class WallpaperManager { throw new RuntimeException(new DeadSystemException()); } try { - return sGlobals.mService.getHeightHint(); + return sGlobals.mService.getHeightHint(mContext.getDisplayId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1572,7 +1572,7 @@ public class WallpaperManager { throw new RuntimeException(new DeadSystemException()); } else { sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight, - mContext.getOpPackageName()); + mContext.getOpPackageName(), mContext.getDisplayId()); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1597,7 +1597,8 @@ public class WallpaperManager { Log.w(TAG, "WallpaperService not running"); throw new RuntimeException(new DeadSystemException()); } else { - sGlobals.mService.setDisplayPadding(padding, mContext.getOpPackageName()); + sGlobals.mService.setDisplayPadding(padding, mContext.getOpPackageName(), + mContext.getDisplayId()); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 24ee7f757f55..00c1863a1ef6 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -53,6 +53,7 @@ import android.net.ProxyInfo; import android.net.Uri; import android.os.Binder; import android.os.Bundle; +import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.os.PersistableBundle; import android.os.Process; @@ -87,6 +88,7 @@ import com.android.internal.util.Preconditions; import com.android.org.conscrypt.TrustedCertificateStore; import java.io.ByteArrayInputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -1930,6 +1932,48 @@ public class DevicePolicyManager { public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3; /** + * Callback used in {@link #installSystemUpdate} to indicate that there was an error while + * trying to install an update. + */ + public abstract static class InstallUpdateCallback { + /** Represents an unknown error while trying to install an update. */ + public static final int UPDATE_ERROR_UNKNOWN = 1; + + /** Represents the update file being intended for different OS version. */ + public static final int UPDATE_ERROR_INCORRECT_OS_VERSION = 2; + + /** + * Represents the update file being wrong, i.e. payloads are mismatched, wrong compressions + * method. + */ + public static final int UPDATE_ERROR_UPDATE_FILE_INVALID = 3; + + /** Represents that the file could not be found. */ + public static final int UPDATE_ERROR_FILE_NOT_FOUND = 4; + + /** Represents the battery being too low to apply an update. */ + public static final int UPDATE_ERROR_BATTERY_LOW = 5; + + /** Method invoked when there was an error while installing an update. */ + public void onInstallUpdateError( + @InstallUpdateCallbackErrorConstants int errorCode, String errorMessage) { + } + } + + /** + * @hide + */ + @IntDef(prefix = { "UPDATE_ERROR_" }, value = { + InstallUpdateCallback.UPDATE_ERROR_UNKNOWN, + InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION, + InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID, + InstallUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND, + InstallUpdateCallback.UPDATE_ERROR_BATTERY_LOW + }) + @Retention(RetentionPolicy.SOURCE) + public @interface InstallUpdateCallbackErrorConstants {} + + /** * Return true if the given administrator component is currently active (enabled) in the system. * * @param admin The administrator component to check for. @@ -6796,7 +6840,6 @@ public class DevicePolicyManager { @Retention(RetentionPolicy.SOURCE) public @interface CreateAndManageUserFlags {} - /** * Called by a device owner to create a user with the specified name and a given component of * the calling package as profile owner. The UserHandle returned by this method should not be @@ -9827,6 +9870,62 @@ public class DevicePolicyManager { } /** + * Called by device owner to install a system update from the given file. The device will be + * rebooted in order to finish installing the update. Note that if the device is rebooted, this + * doesn't necessarily mean that the update has been applied successfully. The caller should + * additionally check the system version with {@link android.os.Build#FINGERPRINT} or {@link + * android.os.Build.VERSION}. If an error occurs during processing the OTA before the reboot, + * the caller will be notified by {@link InstallUpdateCallback}. If device does not have + * sufficient battery level, the installation will fail with error {@link + * InstallUpdateCallback#UPDATE_ERROR_BATTERY_LOW}. + * + * @param admin The {@link DeviceAdminReceiver} that this request is associated with. + * @param updateFilePath An Uri of the file that contains the update. The file should be + * readable by the calling app. + * @param executor The executor through which the callback should be invoked. + * @param callback A callback object that will inform the caller when installing an update + * fails. + */ + public void installSystemUpdate( + @NonNull ComponentName admin, @NonNull Uri updateFilePath, + @NonNull @CallbackExecutor Executor executor, + @NonNull InstallUpdateCallback callback) { + throwIfParentInstance("installUpdate"); + if (mService == null) { + return; + } + try (ParcelFileDescriptor fileDescriptor = mContext.getContentResolver() + .openFileDescriptor(updateFilePath, "r")) { + mService.installUpdateFromFile( + admin, fileDescriptor, new StartInstallingUpdateCallback.Stub() { + @Override + public void onStartInstallingUpdateError( + int errorCode, String errorMessage) { + executeCallback(errorCode, errorMessage, executor, callback); + } + }); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } catch (FileNotFoundException e) { + Log.w(TAG, e); + executeCallback( + InstallUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND, Log.getStackTraceString(e), + executor, callback); + } catch (IOException e) { + Log.w(TAG, e); + executeCallback( + InstallUpdateCallback.UPDATE_ERROR_UNKNOWN, Log.getStackTraceString(e), + executor, callback); + } + } + + private void executeCallback(int errorCode, String errorMessage, + @NonNull @CallbackExecutor Executor executor, + @NonNull InstallUpdateCallback callback) { + executor.execute(() -> callback.onInstallUpdateError(errorCode, errorMessage)); + } + + /** * Returns the system-wide Private DNS mode. * * @param admin which {@link DeviceAdminReceiver} this request is associated with. diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 918c1278a2fe..60f79d62873b 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -20,6 +20,7 @@ package android.app.admin; import android.app.admin.NetworkEvent; import android.app.IApplicationThread; import android.app.IServiceConnection; +import android.app.admin.StartInstallingUpdateCallback; import android.app.admin.SystemUpdateInfo; import android.app.admin.SystemUpdatePolicy; import android.app.admin.PasswordMetrics; @@ -419,4 +420,6 @@ interface IDevicePolicyManager { String getGlobalPrivateDnsHost(in ComponentName admin); void grantDeviceIdsAccessToProfileOwner(in ComponentName who, int userId); + + void installUpdateFromFile(in ComponentName admin, in ParcelFileDescriptor updateFileDescriptor, in StartInstallingUpdateCallback listener); } diff --git a/core/java/android/app/admin/StartInstallingUpdateCallback.aidl b/core/java/android/app/admin/StartInstallingUpdateCallback.aidl new file mode 100644 index 000000000000..df04707e4446 --- /dev/null +++ b/core/java/android/app/admin/StartInstallingUpdateCallback.aidl @@ -0,0 +1,27 @@ +/* +** +** Copyright 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 android.app.admin; + +/** +* Callback used between {@link DevicePolicyManager} and {@link DevicePolicyManagerService} to +* indicate that starting installing an update is finished. +* {@hide} +*/ +oneway interface StartInstallingUpdateCallback { + void onStartInstallingUpdateError(int errorCode, String errorMessage); +}
\ No newline at end of file diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 55148511ed10..3f348033a8aa 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -193,10 +193,6 @@ public final class UsageStatsManager { /** @hide */ public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_START = 0x000D; /** @hide */ - public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_START = 0x000E; - /** @hide */ - public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP = 0x000F; - /** @hide */ public static final int REASON_SUB_PREDICTED_RESTORED = 0x0001; diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 949cdd6878c0..437039dcbccf 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -42,7 +42,6 @@ import android.graphics.ImageDecoder.Source; import android.graphics.Point; import android.graphics.drawable.Drawable; import android.net.Uri; -import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; import android.os.DeadObjectException; @@ -3244,18 +3243,8 @@ public abstract class ContentResolver { Objects.requireNonNull(uri); Objects.requireNonNull(size); - // Older apps might be relying on mutable results, so only consider - // giving them hardware bitmaps once they target Q or higher. If modern - // apps need mutable thumbnails, they can always roll their own logic. - final int allocator; - if (getTargetSdkVersion() >= Build.VERSION_CODES.Q) { - allocator = ImageDecoder.ALLOCATOR_DEFAULT; - } else { - allocator = ImageDecoder.ALLOCATOR_SOFTWARE; - } - try (ContentProviderClient client = acquireContentProviderClient(uri)) { - return loadThumbnail(client, uri, size, signal, allocator); + return loadThumbnail(client, uri, size, signal, ImageDecoder.ALLOCATOR_SOFTWARE); } } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 10c6bc6f191c..6bfddb06ca88 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1767,6 +1767,9 @@ public class Intent implements Parcelable, Cloneable { * that should be managed by the launched UI. * </p> * <p> + * <li> {@link #EXTRA_USER} specifies the UserHandle of the user that owns the app. + * </p> + * <p> * Output: Nothing. * </p> * @@ -3586,6 +3589,17 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE"; + + /** + * Activity Action: Perform text translation. + * <p> + * Input: {@link #EXTRA_TEXT getCharSequence(EXTRA_TEXT)} is the text to translate. + * <p> + * Output: nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_TRANSLATE = "android.intent.action.TRANSLATE"; + /** * Broadcast Action: List of dynamic sensor is changed due to new sensor being connected or * exisiting sensor being disconnected. diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 361bebaec3a2..ad06be3b026b 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3143,6 +3143,33 @@ public abstract class PackageManager { @PackageInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException; /** + * Retrieve overall information about an application package that is + * installed on the system. + * + * @param packageName The full name (i.e. com.google.apps.contacts) of the + * desired package. + * @param flags Additional option flags to modify the data returned. + * @param userHandle The user. + * @return A PackageInfo object containing information about the package. If + * flag {@code MATCH_UNINSTALLED_PACKAGES} is set and if the package + * is not found in the list of installed applications, the package + * information is retrieved from the list of uninstalled + * applications (which includes installed applications as well as + * applications with data directory i.e. applications which had been + * deleted with {@code DONT_DELETE_DATA} flag set). + * @throws NameNotFoundException if a package with the given name cannot be + * found on the system. + * @hide + */ + @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) + @SystemApi + public @NonNull PackageInfo getPackageInfoAsUser(@NonNull String packageName, + @PackageInfoFlags int flags, + @NonNull UserHandle userHandle) throws NameNotFoundException { + return getPackageInfoAsUser(packageName, flags, userHandle.getIdentifier()); + } + + /** * Map from the current package names in use on the device to whatever * the current canonical name of that package is. * @param names Array of current names to be mapped. diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java index 8599f47c6245..355265598365 100644 --- a/core/java/android/net/IpSecConfig.java +++ b/core/java/android/net/IpSecConfig.java @@ -65,10 +65,13 @@ public final class IpSecConfig implements Parcelable { // An interval, in seconds between the NattKeepalive packets private int mNattKeepaliveInterval; - // XFRM mark and mask + // XFRM mark and mask; defaults to 0 (no mark/mask) private int mMarkValue; private int mMarkMask; + // XFRM interface id + private int mXfrmInterfaceId; + /** Set the mode for this IPsec transform */ public void setMode(int mode) { mMode = mode; @@ -125,14 +128,30 @@ public final class IpSecConfig implements Parcelable { mNattKeepaliveInterval = interval; } + /** + * Sets the mark value + * + * <p>Internal (System server) use only. Marks passed in by users will be overwritten or + * ignored. + */ public void setMarkValue(int mark) { mMarkValue = mark; } + /** + * Sets the mark mask + * + * <p>Internal (System server) use only. Marks passed in by users will be overwritten or + * ignored. + */ public void setMarkMask(int mask) { mMarkMask = mask; } + public void setXfrmInterfaceId(int xfrmInterfaceId) { + mXfrmInterfaceId = xfrmInterfaceId; + } + // Transport or Tunnel public int getMode() { return mMode; @@ -190,6 +209,10 @@ public final class IpSecConfig implements Parcelable { return mMarkMask; } + public int getXfrmInterfaceId() { + return mXfrmInterfaceId; + } + // Parcelable Methods @Override @@ -213,6 +236,7 @@ public final class IpSecConfig implements Parcelable { out.writeInt(mNattKeepaliveInterval); out.writeInt(mMarkValue); out.writeInt(mMarkMask); + out.writeInt(mXfrmInterfaceId); } @VisibleForTesting @@ -235,6 +259,7 @@ public final class IpSecConfig implements Parcelable { mNattKeepaliveInterval = c.mNattKeepaliveInterval; mMarkValue = c.mMarkValue; mMarkMask = c.mMarkMask; + mXfrmInterfaceId = c.mXfrmInterfaceId; } private IpSecConfig(Parcel in) { @@ -255,6 +280,7 @@ public final class IpSecConfig implements Parcelable { mNattKeepaliveInterval = in.readInt(); mMarkValue = in.readInt(); mMarkMask = in.readInt(); + mXfrmInterfaceId = in.readInt(); } @Override @@ -289,6 +315,8 @@ public final class IpSecConfig implements Parcelable { .append(mMarkValue) .append(", mMarkMask=") .append(mMarkMask) + .append(", mXfrmInterfaceId=") + .append(mXfrmInterfaceId) .append("}"); return strBuilder.toString(); @@ -320,10 +348,10 @@ public final class IpSecConfig implements Parcelable { && lhs.mNattKeepaliveInterval == rhs.mNattKeepaliveInterval && lhs.mSpiResourceId == rhs.mSpiResourceId && IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption) - && IpSecAlgorithm.equals( - lhs.mAuthenticatedEncryption, rhs.mAuthenticatedEncryption) + && IpSecAlgorithm.equals(lhs.mAuthenticatedEncryption, rhs.mAuthenticatedEncryption) && IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication) && lhs.mMarkValue == rhs.mMarkValue - && lhs.mMarkMask == rhs.mMarkMask); + && lhs.mMarkMask == rhs.mMarkMask + && lhs.mXfrmInterfaceId == rhs.mXfrmInterfaceId); } } diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java index 720c16723c63..97d72f01dbb3 100644 --- a/core/java/android/os/BinderProxy.java +++ b/core/java/android/os/BinderProxy.java @@ -360,6 +360,16 @@ public final class BinderProxy implements IBinder { } /** + * Returns the number of binder proxies held in this process. + * @return number of binder proxies in this process + */ + public static int getProxyCount() { + synchronized (sProxyMap) { + return sProxyMap.size(); + } + } + + /** * Dump proxy debug information. * * @hide diff --git a/core/java/android/os/ConfigUpdate.java b/core/java/android/os/ConfigUpdate.java index 7858c5986bcb..767c15caefd0 100644 --- a/core/java/android/os/ConfigUpdate.java +++ b/core/java/android/os/ConfigUpdate.java @@ -83,6 +83,14 @@ public final class ConfigUpdate { = "android.intent.action.UPDATE_SMART_SELECTION"; /** + * Update conversation actions model file. + * @hide + */ + @SystemApi + public static final String ACTION_UPDATE_CONVERSATION_ACTIONS + = "android.intent.action.UPDATE_CONVERSATION_ACTIONS"; + + /** * Update network watchlist config file. * @hide */ diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 16d454dd0179..e032c1896a4b 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -1232,7 +1232,7 @@ public final class DocumentsContract { public static Bitmap getDocumentThumbnail(ContentProviderClient client, Uri documentUri, Point size, CancellationSignal signal) throws IOException { return ContentResolver.loadThumbnail(client, documentUri, Point.convert(size), signal, - ImageDecoder.ALLOCATOR_DEFAULT); + ImageDecoder.ALLOCATOR_SOFTWARE); } /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 30bcbf45b711..dbdeb7048705 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1710,6 +1710,15 @@ public final class Settings { /** @hide - Private call() method to reset to defaults the 'secure' table */ public static final String CALL_METHOD_RESET_SECURE = "RESET_secure"; + /** @hide - Private call() method to query the 'system' table */ + public static final String CALL_METHOD_LIST_SYSTEM = "LIST_system"; + + /** @hide - Private call() method to query the 'secure' table */ + public static final String CALL_METHOD_LIST_SECURE = "LIST_secure"; + + /** @hide - Private call() method to query the 'global' table */ + public static final String CALL_METHOD_LIST_GLOBAL = "LIST_global"; + /** * Activity Extra: Limit available options in launched activity based on the given authority. * <p> @@ -9330,6 +9339,13 @@ public final class Settings { "location_background_throttle_package_whitelist"; /** + * Whether to disable location status callbacks in preparation for deprecation. + * @hide + */ + public static final String LOCATION_DISABLE_STATUS_CALLBACKS = + "location_disable_status_callbacks"; + + /** * Maximum staleness allowed for last location when returned to clients with only foreground * location permissions. * @hide @@ -12129,6 +12145,20 @@ public final class Settings { "smart_selection_metadata_url"; /** + * URL for conversation actions model updates + * @hide + */ + public static final String CONVERSATION_ACTIONS_UPDATE_CONTENT_URL = + "conversation_actions_content_url"; + + /** + * URL for conversation actions model update metadata + * @hide + */ + public static final String CONVERSATION_ACTIONS_UPDATE_METADATA_URL = + "conversation_actions_metadata_url"; + + /** * SELinux enforcement status. If 0, permissive; if 1, enforcing. * @hide */ @@ -12162,7 +12192,7 @@ public final class Settings { /** * Defines global runtime overrides to window policy. * - * See {@link com.android.server.policy.PolicyControl} for value format. + * See {@link com.android.server.wm.PolicyControl} for value format. * * @hide */ @@ -12678,6 +12708,17 @@ public final class Settings { public static final String AUTOFILL_MAX_VISIBLE_DATASETS = "autofill_max_visible_datasets"; /** + * Used to emulate Smart Suggestion for Augmented Autofill during development + * + * <p>Valid values: {@code 0x1} for IME and/or {@code 0x2} for popup window. + * + * @hide + */ + @TestApi + public static final String AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS = + "autofill_smart_suggestion_emulation_flags"; + + /** * Exemptions to the hidden API blacklist. * * @hide diff --git a/core/java/android/service/intelligence/FillCallback.java b/core/java/android/service/intelligence/FillCallback.java new file mode 100644 index 000000000000..af2da79170ef --- /dev/null +++ b/core/java/android/service/intelligence/FillCallback.java @@ -0,0 +1,45 @@ +/* + * 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 android.service.intelligence; + +import android.annotation.Nullable; +import android.annotation.SystemApi; + +/** + * Callback used to indicate at {@link FillRequest} has been fulfilled. + * + * @hide + */ +@SystemApi +public final class FillCallback { + + FillCallback() {} + + /** + * Sets the response associated with the request. + * + * @param response response associated with the request, or {@code null} if the service + * could not provide autofill for the request. + */ + public void onSuccess(@Nullable FillResponse response) { + final FillWindow fillWindow = response.getFillWindow(); + if (fillWindow != null) { + fillWindow.show(); + } + // TODO(b/111330312): properly implement on server-side by updating the Session state + // accordingly (and adding CTS tests) + } +} diff --git a/core/java/android/service/intelligence/FillController.java b/core/java/android/service/intelligence/FillController.java new file mode 100644 index 000000000000..c5e1242842bb --- /dev/null +++ b/core/java/android/service/intelligence/FillController.java @@ -0,0 +1,71 @@ +/* + * 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 android.service.intelligence; + +import static android.service.intelligence.IntelligenceService.DEBUG; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.RemoteException; +import android.service.intelligence.IntelligenceService.AutofillProxy; +import android.util.Log; +import android.util.Pair; +import android.view.autofill.AutofillId; +import android.view.autofill.AutofillValue; + +import com.android.internal.util.Preconditions; + +import java.util.List; + +/** + * Object used to interact with the autofill system. + * + * @hide + */ +@SystemApi +public final class FillController { + private static final String TAG = "FillController"; + + private final AutofillProxy mProxy; + + FillController(@NonNull AutofillProxy proxy) { + mProxy = proxy; + } + + /** + * Fills the activity with the provided values. + * + * <p>As a side effect, the {@link FillWindow} associated with the {@link FillResponse} will be + * automatically {@link FillWindow#destroy() destroyed}. + */ + public void autofill(@NonNull List<Pair<AutofillId, AutofillValue>> values) { + Preconditions.checkNotNull(values); + + if (DEBUG) { + Log.d(TAG, "autofill() with " + values.size() + " values"); + } + + try { + mProxy.autofill(values); + final FillWindow fillWindow = mProxy.getFillWindow(); + if (fillWindow != null) { + fillWindow.destroy(); + } + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } +} diff --git a/core/java/android/service/intelligence/FillRequest.java b/core/java/android/service/intelligence/FillRequest.java new file mode 100644 index 000000000000..95e922487906 --- /dev/null +++ b/core/java/android/service/intelligence/FillRequest.java @@ -0,0 +1,68 @@ +/* + * 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 android.service.intelligence; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.service.intelligence.IntelligenceService.AutofillProxy; +import android.view.autofill.AutofillId; + +/** + * Represents a request to augment-fill an activity. + * @hide + */ +@SystemApi +public final class FillRequest { + + final AutofillProxy mProxy; + + /** @hide */ + FillRequest(@NonNull AutofillProxy proxy) { + mProxy = proxy; + } + + /** + * Gets the session associated with this request. + */ + @NonNull + public InteractionSessionId getSessionId() { + return mProxy.sessionId; + } + + /** + * Gets the id of the field that triggered the request. + */ + @NonNull + public AutofillId getFocusedId() { + return mProxy.focusedId; + } + + /** + * Gets the Smart Suggestions object used to embed the autofill UI. + * + * @return object used to embed the autofill UI, or {@code null} if not supported. + */ + @Nullable + public PresentationParams getPresentationParams() { + return mProxy.getSmartSuggestionParams(); + } + + @Override + public String toString() { + return "FillRequest[id=" + mProxy.focusedId + "]"; + } +} diff --git a/core/java/android/service/intelligence/FillResponse.java b/core/java/android/service/intelligence/FillResponse.java new file mode 100644 index 000000000000..860c0275732a --- /dev/null +++ b/core/java/android/service/intelligence/FillResponse.java @@ -0,0 +1,129 @@ +/* + * 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 android.service.intelligence; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.autofill.AutofillId; + +import java.util.List; + +/** + * Response to a {@link FillRequest}. + * + * @hide + */ +@SystemApi +public final class FillResponse implements Parcelable { + + private final FillWindow mFillWindow; + + private FillResponse(@NonNull Builder builder) { + mFillWindow = builder.mFillWindow; + } + + /** @hide */ + @Nullable + FillWindow getFillWindow() { + return mFillWindow; + } + + /** + * Builder for {@link FillResponse} objects. + * + * @hide + */ + @SystemApi + public static class Builder { + + private FillWindow mFillWindow; + + /** + * Sets the {@link FillWindow} used to display the Autofill UI. + * + * <p>Must be called when the service is handling the request so the Android System can + * properly synchronize the UI. + * + * @return this builder + */ + public Builder setFillWindow(@NonNull FillWindow fillWindow) { + // TODO(b/111330312): implement / check not null / unit test + // TODO(b/111330312): throw exception if FillWindow not updated yet + mFillWindow = fillWindow; + return this; + } + + /** + * Tells the Android System that the given {@code ids} should not trigger further + * {@link FillRequest requests} when focused. + * + * @param ids ids of the fields that should be ignored + * + * @return this builder + */ + public Builder setIgnoredIds(@NonNull List<AutofillId> ids) { + // TODO(b/111330312): implement / check not null / unit test + return this; + } + + /** + * Builds a new {@link FillResponse} instance. + * + * @throws IllegalStateException if any of the following conditions occur: + * <ol> + * <li>{@link #build()} was already called. + * <li>No call was made to {@link #setFillWindow(FillWindow)} or + * {@ling #setIgnoredIds(List<AutofillId>)}. + * </ol> + * + * @return A built response. + */ + public FillResponse build() { + // TODO(b/111330312): check conditions / add unit test + return new FillResponse(this); + } + + // TODO(b/111330312): add methods to disable app / activity, either here or on manager + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + // TODO(b/111330312): implement + } + + public static final Parcelable.Creator<FillResponse> CREATOR = + new Parcelable.Creator<FillResponse>() { + + @Override + public FillResponse createFromParcel(Parcel parcel) { + // TODO(b/111330312): implement + return null; + } + + @Override + public FillResponse[] newArray(int size) { + return new FillResponse[size]; + } + }; +} diff --git a/core/java/android/service/intelligence/FillWindow.java b/core/java/android/service/intelligence/FillWindow.java new file mode 100644 index 000000000000..4ea07bfc86cf --- /dev/null +++ b/core/java/android/service/intelligence/FillWindow.java @@ -0,0 +1,215 @@ +/* + * 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 android.service.intelligence; + +import static android.service.intelligence.IntelligenceService.DEBUG; + +import android.annotation.LongDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.app.Dialog; +import android.graphics.Rect; +import android.service.intelligence.PresentationParams.Area; +import android.util.Log; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; + +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Handle to a window used to display the augmented autofill UI. + * + * <p>The steps to create an augmented autofill UI are: + * + * <ol> + * <li>Gets the {@link PresentationParams} from the {@link FillRequest}. + * <li>Gets the {@link Area} to display the UI (for example, through + * {@link PresentationParams#getSuggestionArea()}. + * <li>Creates a {@link View} that must fit in the {@link Area#getBounds() area boundaries}. + * <li>Set the proper listeners to the view (for example, a click listener that + * triggers {@link FillController#autofill(java.util.List)} + * <li>Call {@link #update(Area, View, long)} with these arguments. + * <li>Create a {@link FillResponse} with the {@link FillWindow}. + * <li>Pass such {@link FillResponse} to {@link FillCallback#onSuccess(FillResponse)}. + * </ol> + * + * @hide + */ +@SystemApi +public final class FillWindow { + private static final String TAG = "FillWindow"; + + /** Indicates the data being shown is a physical address */ + public static final long FLAG_METADATA_ADDRESS = 0x1; + + // TODO(b/111330312): add moar flags + + /** @hide */ + @LongDef(prefix = { "FLAG" }, value = { + FLAG_METADATA_ADDRESS, + }) + @Retention(RetentionPolicy.SOURCE) + @interface Flags{} + + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private Dialog mDialog; + + @GuardedBy("mLock") + private boolean mDestroyed; + + /** + * Updates the content of the window. + * + * @param rootView new root view + * @param area coordinates to render the view. + * @param flags optional flags such as metadata of what will be rendered in the window. The + * Smart Suggestion host might decide whether or not to render the UI based on them. + * + * @return boolean whether the window was updated or not. + * + * @throws IllegalArgumentException if the area is not compatible with this window + */ + public boolean update(@NonNull Area area, @NonNull View rootView, @Flags long flags) { + if (DEBUG) { + Log.d(TAG, "Updating " + area + " + with " + rootView); + } + // TODO(b/111330312): add test case for null + Preconditions.checkNotNull(area); + Preconditions.checkNotNull(rootView); + // TODO(b/111330312): must check the area is a valid object returned by + // SmartSuggestionParams, throw IAE if not + + // TODO(b/111330312): must some how pass metadata to the SmartSuggestiongs provider + + + // TODO(b/111330312): use a SurfaceControl approach; for now, we're manually creating + // the window underneath the existing view. + + final PresentationParams smartSuggestion = area.proxy.getSmartSuggestionParams(); + if (smartSuggestion == null) { + Log.w(TAG, "No SmartSuggestionParams"); + return false; + } + + final Rect rect = area.getBounds(); + if (rect == null) { + Log.wtf(TAG, "No Rect on SmartSuggestionParams"); + return false; + } + + synchronized (mLock) { + checkNotDestroyedLocked(); + + // TODO(b/111330312): once we have the SurfaceControl approach, we should update the + // window instead of destroying. In fact, it might be better to allocate a full window + // initially, which is transparent (and let touches get through) everywhere but in the + // rect boundaries. + destroy(); + + // TODO(b/111330312): make sure all touch events are handled, window is always closed, + // etc. + + mDialog = new Dialog(rootView.getContext()); + final Window window = mDialog.getWindow(); + window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); + + final int height = rect.bottom - rect.top; + final int width = rect.right - rect.left; + final WindowManager.LayoutParams windowParams = window.getAttributes(); + windowParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; + windowParams.y = rect.top - height; + windowParams.height = height; + windowParams.x = rect.left; + windowParams.width = width; + + window.setAttributes(windowParams); + window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); + + mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + final ViewGroup.LayoutParams diagParams = new ViewGroup.LayoutParams(width, height); + mDialog.setContentView(rootView, diagParams); + + if (DEBUG) { + Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView); + } + + area.proxy.setFillWindow(this); + return true; + } + } + + /** @hide */ + void show() { + // TODO(b/111330312): check if updated first / throw exception + if (DEBUG) Log.d(TAG, "show()"); + + synchronized (mLock) { + checkNotDestroyedLocked(); + if (mDialog == null) { + throw new IllegalStateException("update() not called yet, or already destroyed()"); + } + + mDialog.show(); + } + } + + /** + * Destroys the window. + * + * <p>Once destroyed, this window cannot be used anymore + */ + public void destroy() { + if (DEBUG) Log.d(TAG, "destroy(): mDestroyed = " + mDestroyed); + + synchronized (this) { + if (mDestroyed) return; + + if (mDialog != null) { + mDialog.dismiss(); + mDialog = null; + } + } + } + + private void checkNotDestroyedLocked() { + if (mDestroyed) { + throw new IllegalStateException("already destroyed()"); + } + } + + /** @hide */ + public void dump(@NonNull String prefix, @NonNull PrintWriter pw) { + synchronized (this) { + pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed); + if (mDialog != null) { + pw.print(prefix); pw.print("dialog: "); + pw.println(mDialog.isShowing() ? "shown" : "hidden"); + pw.print(prefix); pw.print("window: "); + pw.println(mDialog.getWindow().getAttributes()); + } + } + } +} diff --git a/core/java/android/service/intelligence/IIntelligenceService.aidl b/core/java/android/service/intelligence/IIntelligenceService.aidl index 709c3b720579..e2260d7d3d76 100644 --- a/core/java/android/service/intelligence/IIntelligenceService.aidl +++ b/core/java/android/service/intelligence/IIntelligenceService.aidl @@ -16,10 +16,12 @@ package android.service.intelligence; +import android.os.IBinder; import android.service.intelligence.InteractionSessionId; import android.service.intelligence.InteractionContext; import android.service.intelligence.SnapshotData; +import android.view.autofill.AutofillId; import android.view.intelligence.ContentCaptureEvent; import java.util.List; @@ -40,4 +42,9 @@ oneway interface IIntelligenceService { void onActivitySnapshot(in InteractionSessionId sessionId, in SnapshotData snapshotData); + + void onAutofillRequest(in InteractionSessionId sessionId, in IBinder autofillManagerClient, + int autofilSessionId, in AutofillId focusedId); + + void onDestroyAutofillWindowsRequest(in InteractionSessionId sessionId); } diff --git a/core/java/android/service/intelligence/IntelligenceService.java b/core/java/android/service/intelligence/IntelligenceService.java index 27569b6003c5..040e25e977e4 100644 --- a/core/java/android/service/intelligence/IntelligenceService.java +++ b/core/java/android/service/intelligence/IntelligenceService.java @@ -22,13 +22,26 @@ import android.annotation.NonNull; import android.annotation.SystemApi; import android.app.Service; import android.content.Intent; +import android.graphics.Rect; +import android.os.CancellationSignal; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; +import android.service.intelligence.PresentationParams.SystemPopupPresentationParams; +import android.util.ArrayMap; import android.util.Log; +import android.util.Pair; +import android.view.autofill.AutofillId; +import android.view.autofill.AutofillValue; +import android.view.autofill.IAugmentedAutofillManagerClient; import android.view.intelligence.ContentCaptureEvent; +import com.android.internal.annotations.GuardedBy; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; import java.util.List; /** @@ -44,6 +57,9 @@ public abstract class IntelligenceService extends Service { private static final String TAG = "IntelligenceService"; + // TODO(b/111330312): STOPSHIP use dynamic value, or change to false + static final boolean DEBUG = true; + /** * The {@link Intent} that must be declared as handled by the service. * To be supported, the service must also require the @@ -55,6 +71,8 @@ public abstract class IntelligenceService extends Service { private Handler mHandler; + private ArrayMap<InteractionSessionId, AutofillProxy> mAutofillProxies; + private final IIntelligenceService mInterface = new IIntelligenceService.Stub() { @Override @@ -87,6 +105,20 @@ public abstract class IntelligenceService extends Service { obtainMessage(IntelligenceService::onActivitySnapshot, IntelligenceService.this, sessionId, snapshotData)); } + + @Override + public void onAutofillRequest(InteractionSessionId sessionId, IBinder client, + int autofilSessionId, AutofillId focusedId) { + mHandler.sendMessage(obtainMessage(IntelligenceService::handleOnAutofillRequest, + IntelligenceService.this, sessionId, client, autofilSessionId, focusedId)); + } + + @Override + public void onDestroyAutofillWindowsRequest(InteractionSessionId sessionId) { + mHandler.sendMessage( + obtainMessage(IntelligenceService::handleOnDestroyAutofillWindowsRequest, + IntelligenceService.this, sessionId)); + } }; @CallSuper @@ -122,10 +154,93 @@ public abstract class IntelligenceService extends Service { * @param sessionId the session's Id * @param events the events */ - // TODO(b/111276913): rename to onContentCaptureEvents + // TODO(b/111276913): rename to onContentCaptureEvents or something like that; also, pass a + // Request object so it can be extended public abstract void onContentCaptureEvent(@NonNull InteractionSessionId sessionId, @NonNull List<ContentCaptureEvent> events); + private void handleOnAutofillRequest(@NonNull InteractionSessionId sessionId, + @NonNull IBinder client, int autofillSessionId, @NonNull AutofillId focusedId) { + if (mAutofillProxies == null) { + mAutofillProxies = new ArrayMap<>(); + } + AutofillProxy proxy = mAutofillProxies.get(sessionId); + if (proxy == null) { + proxy = new AutofillProxy(sessionId, client, autofillSessionId, focusedId); + mAutofillProxies.put(sessionId, proxy); + } else { + // TODO(b/111330312): figure out if it's ok to reuse the proxy; add logging + if (DEBUG) Log.d(TAG, "Reusing proxy for session " + sessionId); + } + // TODO(b/111330312): set cancellation signal + final CancellationSignal cancellationSignal = null; + onFillRequest(sessionId, new FillRequest(proxy), cancellationSignal, + new FillController(proxy), new FillCallback()); + } + + /** + * Asks the service to handle an "augmented" autofill request. + * + * <p>This method is called when the "stantard" autofill service cannot handle a request, which + * typically occurs when: + * <ul> + * <li>Service does not recognize what should be autofilled. + * <li>Service does not have data to fill the request. + * <li>Service blacklisted that app (or activity) for autofill. + * <li>App disabled itself for autofill. + * </ul> + * + * <p>Differently from the standard autofill workflow, on augmented autofill the service is + * responsible to generate the autofill UI and request the Android system to autofill the + * activity when the user taps an action in that UI (through the + * {@link FillController#autofill(List)} method). + * + * <p>The service <b>MUST</b> call {@link + * FillCallback#onSuccess(android.service.intelligence.FillResponse)} as soon as possible, + * passing {@code null} when it cannot fulfill the request. + * + * @param sessionId the session's id + * @param request the request to handle. + * @param cancellationSignal signal for observing cancellation requests. The system will use + * this to notify you that the fill result is no longer needed and you should stop + * handling this fill request in order to save resources. + * @param controller object used to interact with the autofill system. + * @param callback object used to notify the result of the request. Service <b>must</b> call + * {@link FillCallback#onSuccess(android.service.intelligence.FillResponse)}. + */ + public void onFillRequest(@NonNull InteractionSessionId sessionId, @NonNull FillRequest request, + @NonNull CancellationSignal cancellationSignal, @NonNull FillController controller, + @NonNull FillCallback callback) { + } + + private void handleOnDestroyAutofillWindowsRequest(@NonNull InteractionSessionId sessionId) { + AutofillProxy proxy = null; + if (mAutofillProxies != null) { + proxy = mAutofillProxies.get(sessionId); + } + if (proxy == null) { + // TODO(b/111330312): this might be fine, in which case we should logv it + Log.w(TAG, "No proxy for session " + sessionId); + return; + } + proxy.destroy(); + mAutofillProxies.remove(sessionId); + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mAutofillProxies != null) { + final int size = mAutofillProxies.size(); + pw.print("Number proxies: "); pw.println(size); + for (int i = 0; i < size; i++) { + final InteractionSessionId sessionId = mAutofillProxies.keyAt(i); + final AutofillProxy proxy = mAutofillProxies.valueAt(i); + pw.print(i); pw.print(") SessionId="); pw.print(sessionId); pw.println(":"); + proxy.dump(" ", pw); + } + } + } + /** * Notifies the service of {@link IntelligenceSnapshotData snapshot data} associated with a * session. @@ -142,4 +257,99 @@ public abstract class IntelligenceService extends Service { * @param sessionId the id of the session to destroy */ public void onDestroyInteractionSession(@NonNull InteractionSessionId sessionId) {} + + /** @hide */ + static final class AutofillProxy { + private final Object mLock = new Object(); + private final IAugmentedAutofillManagerClient mClient; + private final int mAutofillSessionId; + public final InteractionSessionId sessionId; + public final AutofillId focusedId; + + @GuardedBy("mLock") + private SystemPopupPresentationParams mSmartSuggestion; + + @GuardedBy("mLock") + private FillWindow mFillWindow; + + private AutofillProxy(@NonNull InteractionSessionId sessionId, @NonNull IBinder client, + int autofillSessionId, @NonNull AutofillId focusedId) { + this.sessionId = sessionId; + mClient = IAugmentedAutofillManagerClient.Stub.asInterface(client); + mAutofillSessionId = autofillSessionId; + this.focusedId = focusedId; + // TODO(b/111330312): linkToDeath + } + + @NonNull + public SystemPopupPresentationParams getSmartSuggestionParams() { + synchronized (mLock) { + if (mSmartSuggestion != null) { + return mSmartSuggestion; + } + Rect rect; + try { + rect = mClient.getViewCoordinates(focusedId); + } catch (RemoteException e) { + Log.w(TAG, "Could not get coordinates for " + focusedId); + return null; + } + if (rect == null) { + if (DEBUG) Log.d(TAG, "getViewCoordinates(" + focusedId + ") returned null"); + return null; + } + mSmartSuggestion = new SystemPopupPresentationParams(this, rect); + return mSmartSuggestion; + } + } + + public void autofill(@NonNull List<Pair<AutofillId, AutofillValue>> pairs) + throws RemoteException { + final int size = pairs.size(); + final List<AutofillId> ids = new ArrayList<>(size); + final List<AutofillValue> values = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + final Pair<AutofillId, AutofillValue> pair = pairs.get(i); + ids.add(pair.first); + values.add(pair.second); + } + mClient.autofill(mAutofillSessionId, ids, values); + } + + public void setFillWindow(@NonNull FillWindow fillWindow) { + synchronized (mLock) { + mFillWindow = fillWindow; + } + } + + public FillWindow getFillWindow() { + synchronized (mLock) { + return mFillWindow; + } + } + + public void dump(@NonNull String prefix, @NonNull PrintWriter pw) { + pw.print(prefix); pw.print("afSessionId: "); pw.println(mAutofillSessionId); + pw.print(prefix); pw.print("focusedId: "); pw.println(focusedId); + pw.print(prefix); pw.print("client: "); pw.println(mClient); + final String prefix2 = prefix + " "; + if (mFillWindow != null) { + pw.print(prefix); pw.println("window:"); + mFillWindow.dump(prefix2, pw); + } + if (mSmartSuggestion != null) { + pw.print(prefix); pw.println("smartSuggestion:"); + mSmartSuggestion.dump(prefix2, pw); + } + } + + private void destroy() { + synchronized (mLock) { + if (mFillWindow != null) { + if (DEBUG) Log.d(TAG, "destroying window"); + mFillWindow.destroy(); + } + } + } + } } diff --git a/core/java/android/service/intelligence/PresentationParams.java b/core/java/android/service/intelligence/PresentationParams.java new file mode 100644 index 000000000000..b92f8f1ada75 --- /dev/null +++ b/core/java/android/service/intelligence/PresentationParams.java @@ -0,0 +1,225 @@ +/* + * 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 android.service.intelligence; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.graphics.Rect; +import android.service.intelligence.IntelligenceService.AutofillProxy; +import android.util.DebugUtils; +import android.view.View; + +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Abstraction of a "Smart Suggestion" component responsible to embed the autofill UI provided by + * the intelligence service. + * + * <p>The Smart Suggestion can embed the autofill UI in 3 distinct places: + * + * <ul> + * <li>A small area associated with suggestions (like a small strip in the top of the IME), + * returned by {@link #getSuggestionArea()} + * <li>The full area (like the full IME window), returned by {@link #getFullArea()} + * <li>A subset of the aforementioned areas, returned by {@link Area#getSubArea(Rect)} + * </ul> + * + * <p>The Smart Suggestion is represented by a {@link Area} object that contains the + * dimensions the smart suggestion window, so the service can use it to calculate the size of the + * view that will be passed to {@link FillWindow#update(Area, View, long)}. + * + * @hide + */ +@SystemApi +public abstract class PresentationParams { + + /** + * Flag indicating the Smart Suggestion is hosted in the top of its container. + */ + public static final int FLAG_HINT_GRAVITY_TOP = 0x1; + + /** + * Flag indicating the Smart Suggestion is hosted in the bottom of its container. + */ + public static final int FLAG_HINT_GRAVITY_BOTTOM = 0x2; + + /** + * Flag indicating the Smart Suggestion is hosted in the left of its container. + */ + public static final int FLAG_HINT_GRAVITY_LEFT = 0x4; + + /** + * Flag indicating the Smart Suggestion is hosted in the right of its container. + */ + public static final int FLAG_HINT_GRAVITY_RIGHT = 0x8; + + /** + * Flag indicating the Smart Suggestion is hosted by the IME. + */ + public static final int FLAG_HOST_IME = 0x10; + + /** + * Flag indicating the Smart Suggestion is hosted by the Android System as a floating popup + * window. + */ + public static final int FLAG_HOST_SYSTEM = 0x20; + + /** @hide */ + @IntDef(flag = true, prefix = { "FLAG_" }, value = { + FLAG_HINT_GRAVITY_TOP, + FLAG_HINT_GRAVITY_BOTTOM, + FLAG_HINT_GRAVITY_LEFT, + FLAG_HINT_GRAVITY_RIGHT, + FLAG_HOST_IME, + FLAG_HOST_SYSTEM + }) + @Retention(RetentionPolicy.SOURCE) + @interface Flags {} + + + // /** @hide */ + PresentationParams() {} + + /** + * Gets the area of the suggestion strip for the given {@code metadata} + * + * @return strip dimensions, or {@code null} if the Smart Suggestion provider does not support + * suggestions strip. + */ + @Nullable + public Area getSuggestionArea() { + return null; + } + + /** + * Gets the full area for the of the Smart Suggestion provider. + * + * @return full dimensions, or {@code null} if the Smart Suggestion provider does not support + * embeding the UI on its full area. + */ + @Nullable + public Area getFullArea() { + return null; + } + + /** + * Gets flags associated with the Smart Suggestion. + * + * @return any combination of {@link #FLAG_HINT_GRAVITY_TOP}, + * {@link #FLAG_HINT_GRAVITY_BOTTOM}, {@link #FLAG_HINT_GRAVITY_LEFT}, + * {@link #FLAG_HINT_GRAVITY_RIGHT}, {@link #FLAG_HOST_IME}, or + * {@link #FLAG_HOST_SYSTEM}, + */ + public @Flags int getFlags() { + return 0; + } + + /** @hide */ + void dump(@NonNull String prefix, @NonNull PrintWriter pw) { + final int flags = getFlags(); + if (flags > 0) { + pw.print(prefix); pw.print("flags: "); pw.println(flagsToString(flags)); + } + } + + private static String flagsToString(int flags) { + return DebugUtils.flagsToString(PresentationParams.class, "FLAG_", flags); + } + + /** + * Area associated with a {@link PresentationParams Smart Suggestions} provider. + * + * @hide + * */ + @SystemApi + public abstract static class Area { + + /** @hide */ + public final AutofillProxy proxy; + + private final Rect mBounds; + + private Area(@NonNull AutofillProxy proxy, @NonNull Rect bounds) { + this.proxy = proxy; + mBounds = bounds; + } + + /** + * Gets the area boundaries. + */ + @NonNull + public Rect getBounds() { + return mBounds; + } + + /** + * Gets a subarea limited by given boundaries. + * + * @param bounds boundaries relative to this Area. + * + * @throw {@link IllegalArgumentException} if the {@code bounds} is not fully-contained + * inside this full Area. + * + * @return new subarea, or {@code null} if the Smart Suggestion host does not support such + * subaarea. + */ + @Nullable + public Area getSubArea(@NonNull Rect bounds) { + // TODO(b/111330312): implement / check boundaries / throw IAE / add unit test + return null; + } + + @Override + public String toString() { + return mBounds.toString(); + } + } + + /** + * System-provided poup window anchored to a view. + * + * <p>Used just for debugging purposes. + * + * @hide + */ + public static final class SystemPopupPresentationParams extends PresentationParams { + private final Area mSuggestionArea; + + public SystemPopupPresentationParams(@NonNull AutofillProxy proxy, @NonNull Rect rect) { + mSuggestionArea = new Area(proxy, rect) {}; + } + + @Override + public Area getSuggestionArea() { + return mSuggestionArea; + } + + @Override + public int getFlags() { + return FLAG_HOST_SYSTEM | FLAG_HINT_GRAVITY_BOTTOM; + } + + @Override + void dump(@NonNull String prefix, @NonNull PrintWriter pw) { + super.dump(prefix, pw); + pw.print(prefix); pw.print("area: "); pw.println(mSuggestionArea); + } + } +} diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 6f51becfbf03..f6bb762a0bef 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -129,7 +129,7 @@ public abstract class WallpaperService extends Service { Bundle extras; boolean sync; } - + /** * The actual implementation of a wallpaper. A wallpaper service may * have multiple instances running (for example as a real wallpaper @@ -144,7 +144,7 @@ public abstract class WallpaperService extends Service { HandlerCaller mCaller; IWallpaperConnection mConnection; IBinder mWindowToken; - + boolean mInitializing = true; boolean mVisible; boolean mReportedVisible; @@ -208,7 +208,6 @@ public abstract class WallpaperService extends Service { private final Supplier<Long> mClockFunction; private final Handler mHandler; - DisplayManager mDisplayManager; Display mDisplay; private int mDisplayState; @@ -419,7 +418,7 @@ public abstract class WallpaperService extends Service { public int getDesiredMinimumHeight() { return mIWallpaperEngine.mReqHeight; } - + /** * Return whether the wallpaper is currently visible to the user, * this is the last value supplied to @@ -1015,14 +1014,6 @@ public abstract class WallpaperService extends Service { return; } - mDisplayManager = getSystemService(DisplayManager.class); - mDisplay = mDisplayManager.getDisplay(wrapper.mDisplayId); - if (mDisplay == null) { - // TODO(b/115486823) Ignore this engine. - Log.e(TAG, "Attaching to a non-existent display: " + wrapper.mDisplayId); - return; - } - mIWallpaperEngine = wrapper; mCaller = wrapper.mCaller; mConnection = wrapper.mConnection; @@ -1034,13 +1025,16 @@ public abstract class WallpaperService extends Service { mWindow.setSession(mSession); mLayout.packageName = getPackageName(); - mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler()); + mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener, + mCaller.getHandler()); + mDisplay = mIWallpaperEngine.mDisplay; mDisplayState = mDisplay.getState(); if (DEBUG) Log.v(TAG, "onCreate(): " + this); onCreate(mSurfaceHolder); - + mInitializing = false; + mReportedVisible = false; updateSurface(false, false, false); } @@ -1202,8 +1196,8 @@ public abstract class WallpaperService extends Service { mDestroyed = true; - if (mDisplayManager != null) { - mDisplayManager.unregisterDisplayListener(mDisplayListener); + if (mIWallpaperEngine.mDisplayManager != null) { + mIWallpaperEngine.mDisplayManager.unregisterDisplayListener(mDisplayListener); } if (mVisible) { @@ -1272,7 +1266,9 @@ public abstract class WallpaperService extends Service { int mReqWidth; int mReqHeight; final Rect mDisplayPadding = new Rect(); - int mDisplayId; + final int mDisplayId; + final DisplayManager mDisplayManager; + final Display mDisplay; Engine mEngine; @@ -1289,7 +1285,15 @@ public abstract class WallpaperService extends Service { mReqHeight = reqHeight; mDisplayPadding.set(padding); mDisplayId = displayId; - + + // Create a display context before onCreateEngine. + mDisplayManager = getSystemService(DisplayManager.class); + mDisplay = mDisplayManager.getDisplay(mDisplayId); + + if (mDisplay == null) { + // Ignore this engine. + throw new IllegalArgumentException("Cannot find display with id" + mDisplayId); + } Message msg = mCaller.obtainMessage(DO_ATTACH); mCaller.sendMessage(msg); } diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index ec63cd941b3f..7815864ddfe9 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -1009,7 +1009,7 @@ public abstract class TextToSpeechService extends Service { Log.e(TAG, "null synthesis text"); return false; } - if (mText.length() >= TextToSpeech.getMaxSpeechInputLength()) { + if (mText.length() > TextToSpeech.getMaxSpeechInputLength()) { Log.w(TAG, "Text too long: " + mText.length() + " chars"); return false; } @@ -1609,7 +1609,7 @@ public abstract class TextToSpeechService extends Service { synchronized (mCallerToCallback) { mCallerToCallback.remove(caller); } - //mSynthHandler.stopForApp(caller); + mSynthHandler.stopForApp(caller); } @Override diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index c836c9ec0def..308a00020db9 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -271,7 +271,7 @@ interface IWindowManager /** * Called by the status bar to notify Views of changes to System UI visiblity. */ - oneway void statusBarVisibilityChanged(int visibility); + oneway void statusBarVisibilityChanged(int displayId, int visibility); /** * Called by System UI to notify of changes to the visibility of Recents. diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 5f1336f903d6..e0232a74038f 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3993,7 +3993,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #getParent() */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) protected ViewParent mParent; /** @@ -4199,7 +4199,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@hide} */ @ViewDebug.ExportedProperty(category = "layout") - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) protected int mLeft; /** * The distance in pixels from the left edge of this view's parent @@ -4207,7 +4207,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@hide} */ @ViewDebug.ExportedProperty(category = "layout") - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) protected int mRight; /** * The distance in pixels from the top edge of this view's parent @@ -4215,7 +4215,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@hide} */ @ViewDebug.ExportedProperty(category = "layout") - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) protected int mTop; /** * The distance in pixels from the top edge of this view's parent @@ -4223,7 +4223,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@hide} */ @ViewDebug.ExportedProperty(category = "layout") - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) protected int mBottom; /** @@ -4714,7 +4714,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * of this view to at least this amount. */ @ViewDebug.ExportedProperty(category = "measurement") - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private int mMinHeight; /** @@ -4722,7 +4722,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * of this view to at least this amount. */ @ViewDebug.ExportedProperty(category = "measurement") - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private int mMinWidth; /** @@ -8152,6 +8152,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * the user, and the activity rendering the view is enabled for Content Capture) is laid out and * is visible. * + * <p>The populated structure is then passed to the service through + * {@link IntelligenceManager#notifyViewAppeared(ViewStructure)}. + * * <p><b>Note: </b>the following methods of the {@code structure} will be ignored: * <ul> * <li>{@link ViewStructure#setChildCount(int)} @@ -8165,16 +8168,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <li>{@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)} * <li>{@link ViewStructure#setDataIsSensitive(boolean)} * </ul> - * - * @return whether the IntelligenceService should be notified that the view was added (through - * the {@link IntelligenceManager#notifyViewAppeared(ViewStructure)} method) to the view - * hierarchy. Most views should return {@code true} here, but views that contains virtual - * hierarchy might opt to return {@code false} and notify the manager independently, as the - * virtual views are rendered. */ - public boolean onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) { + public void onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) { onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags); - return true; } /** @hide */ @@ -8986,10 +8982,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (type == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) { final ViewStructure structure = im.newViewStructure(this); - boolean notifyMgr = onProvideContentCaptureStructure(structure, /* flags= */ 0); - if (notifyMgr) { - im.notifyViewAppeared(structure); - } + onProvideContentCaptureStructure(structure, /* flags= */ 0); + im.notifyViewAppeared(structure); mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED; } else { if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED) == 0) { diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index d4c7069cdbf4..9227249fc6b1 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -202,6 +202,14 @@ public final class AutofillManager { public static final String EXTRA_RESTORE_SESSION_TOKEN = "android.view.autofill.extra.RESTORE_SESSION_TOKEN"; + /** + * Internal extra used to pass a binder to the {@link IAugmentedAutofillManagerClient}. + * + * @hide + */ + public static final String EXTRA_AUGMENTED_AUTOFILL_CLIENT = + "android.view.autofill.extra.AUGMENTED_AUTOFILL_CLIENT"; + private static final String SESSION_ID_TAG = "android:sessionId"; private static final String STATE_TAG = "android:state"; private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData"; @@ -370,6 +378,9 @@ public final class AutofillManager { private Cleaner mServiceClientCleaner; @GuardedBy("mLock") + private IAugmentedAutofillManagerClient mAugmentedAutofillServiceClient; + + @GuardedBy("mLock") private AutofillCallback mCallback; private final Context mContext; @@ -1664,6 +1675,8 @@ public final class AutofillManager { final IAutoFillManager service = mService; final IAutoFillManagerClient serviceClient = mServiceClient; mServiceClientCleaner = Cleaner.create(this, () -> { + // TODO(b/111330312): call service to also remove reference to + // mAugmentedAutofillServiceClient try { service.removeClient(serviceClient, userId); } catch (RemoteException e) { @@ -1808,6 +1821,7 @@ public final class AutofillManager { if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) { // Reset connection to system mServiceClient = null; + mAugmentedAutofillServiceClient = null; if (mServiceClientCleaner != null) { mServiceClientCleaner.clean(); mServiceClientCleaner = null; @@ -2054,6 +2068,29 @@ public final class AutofillManager { } } + /** + * Gets a {@link AugmentedAutofillManagerClient} for this {@link AutofillManagerClient}. + * + * <p>These are 2 distinct objects because we need to restrict what the Augmented Autofill + * service can do (which is defined by {@code IAugmentedAutofillManagerClient.aidl}). + */ + private void getAugmentedAutofillClient(@NonNull IResultReceiver result) { + synchronized (mLock) { + if (mAugmentedAutofillServiceClient == null) { + mAugmentedAutofillServiceClient = new AugmentedAutofillManagerClient(this); + } + final Bundle resultData = new Bundle(); + resultData.putBinder(EXTRA_AUGMENTED_AUTOFILL_CLIENT, + mAugmentedAutofillServiceClient.asBinder()); + + try { + result.send(0, resultData); + } catch (RemoteException e) { + Log.w(TAG, "Could not send AugmentedAutofillClient back: " + e); + } + } + } + /** @hide */ public void requestHideFillUi() { requestHideFillUi(mIdShownFillUi, true); @@ -2801,7 +2838,7 @@ public final class AutofillManager { private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub { private final WeakReference<AutofillManager> mAfm; - AutofillManagerClient(AutofillManager autofillManager) { + private AutofillManagerClient(AutofillManager autofillManager) { mAfm = new WeakReference<>(autofillManager); } @@ -2904,6 +2941,50 @@ public final class AutofillManager { afm.post(() -> afm.setSessionFinished(newState)); } } + + @Override + public void getAugmentedAutofillClient(IResultReceiver result) { + final AutofillManager afm = mAfm.get(); + if (afm != null) { + afm.post(() -> afm.getAugmentedAutofillClient(result)); + } + } + } + + private static final class AugmentedAutofillManagerClient + extends IAugmentedAutofillManagerClient.Stub { + private final WeakReference<AutofillManager> mAfm; + + private AugmentedAutofillManagerClient(AutofillManager autofillManager) { + mAfm = new WeakReference<>(autofillManager); + } + + @Override + public Rect getViewCoordinates(@NonNull AutofillId id) { + // TODO(b/111330312): use handler / callback? + final AutofillManager afm = mAfm.get(); + if (afm == null) return null; + + final View view = afm.getClient().autofillClientFindViewByAutofillIdTraversal(id); + // TODO(b/111330312): optimize (for example, use temp rect from attach info) and + // fix (for example, take system status bar height into account) logic below + final int[] location = new int[2]; + view.getLocationOnScreen(location); + final Rect rect = new Rect(location[0], location[1], location[0] + view.getWidth(), + location[1] + view.getHeight()); + if (sVerbose) { + Log.v(TAG, "Coordinates for " + id + ": " + rect); + } + return rect; + } + + @Override + public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) { + final AutofillManager afm = mAfm.get(); + if (afm != null) { + afm.post(() -> afm.autofill(sessionId, ids, values)); + } + } } /** diff --git a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl new file mode 100644 index 000000000000..67cd0bf87b99 --- /dev/null +++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl @@ -0,0 +1,34 @@ +/* + * 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 android.view.autofill; + +import java.util.List; + +import android.graphics.Rect; +import android.view.autofill.AutofillId; +import android.view.autofill.AutofillValue; + +/** + * Object running in the application process and responsible to provide the functionalities + * required by an Augmented Autofill service. + * + * @hide + */ +interface IAugmentedAutofillManagerClient { + Rect getViewCoordinates(in AutofillId id); + void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values); +} diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl index 0ff7a0bdb963..63394b42eb5a 100644 --- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl +++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl @@ -27,6 +27,8 @@ import android.view.autofill.AutofillValue; import android.view.autofill.IAutofillWindowPresenter; import android.view.KeyEvent; +import com.android.internal.os.IResultReceiver; + /** * Object running in the application process and responsible for autofilling it. * @@ -93,8 +95,18 @@ oneway interface IAutoFillManagerClient { /** * Marks the state of the session as finished. + * * @param newState STATE_FINISHED (because the autofill service returned a null * FillResponse) or STATE_UNKNOWN (because the session was removed). */ void setSessionFinished(int newState); + + /** + * Gets a reference to the binder object that can be used by the Augmented Autofill service. + * + * @param receiver, whose AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT extra will contain + * the reference. + */ + void getAugmentedAutofillClient(in IResultReceiver result); + } diff --git a/core/java/android/view/intelligence/IntelligenceManager.java b/core/java/android/view/intelligence/IntelligenceManager.java index dfa52d94f4a1..755c54c5d25b 100644 --- a/core/java/android/view/intelligence/IntelligenceManager.java +++ b/core/java/android/view/intelligence/IntelligenceManager.java @@ -391,7 +391,7 @@ public final class IntelligenceManager { } /** - * Called by apps to explicitly enabled or disable content capture. + * Called by apps to explicitly enable or disable content capture. * * <p><b>Note: </b> this call is not persisted accross reboots, so apps should typically call * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}. diff --git a/core/java/android/view/intelligence/ViewNode.java b/core/java/android/view/intelligence/ViewNode.java index cc78e6b4aa6d..ea57461f0933 100644 --- a/core/java/android/view/intelligence/ViewNode.java +++ b/core/java/android/view/intelligence/ViewNode.java @@ -238,6 +238,8 @@ public final class ViewNode extends AssistStructure.ViewNode { @Override public void setText(CharSequence text, int selectionStart, int selectionEnd) { + // TODO(b/111276913): temporarily setting directly; should be done on superclass instead + mNode.mText = text; // TODO(b/111276913): implement or move to superclass } diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index 798a8208e240..3e57e1ded3d4 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -58,7 +58,6 @@ import java.net.URLEncoder; import java.time.Instant; import java.time.ZonedDateTime; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -523,10 +522,10 @@ public final class TextClassifierImpl implements TextClassifier { final TextClassification.Builder builder = new TextClassification.Builder() .setText(classifiedText); - final int size = classifications.length; + final int typeCount = classifications.length; AnnotatorModel.ClassificationResult highestScoringResult = - size > 0 ? classifications[0] : null; - for (int i = 0; i < size; i++) { + typeCount > 0 ? classifications[0] : null; + for (int i = 0; i < typeCount; i++) { builder.setEntityType(classifications[i].getCollection(), classifications[i].getScore()); if (classifications[i].getScore() > highestScoringResult.getScore()) { @@ -534,9 +533,12 @@ public final class TextClassifierImpl implements TextClassifier { } } + // TODO: Make this configurable. + final float foreignTextThreshold = typeCount == 0 ? 0.5f : 0.7f; boolean isPrimaryAction = true; for (LabeledIntent labeledIntent : IntentFactory.create( - mContext, classifiedText, referenceTime, highestScoringResult)) { + mContext, classifiedText, isForeignText(classifiedText, foreignTextThreshold), + referenceTime, highestScoringResult)) { final RemoteAction action = labeledIntent.asRemoteAction(mContext); if (action == null) { continue; @@ -558,6 +560,42 @@ public final class TextClassifierImpl implements TextClassifier { return builder.setId(createId(text, start, end)).build(); } + private boolean isForeignText(String text, float threshold) { + // TODO: Revisit this algorithm. + try { + final LangIdModel.LanguageResult[] langResults = getLangIdImpl().detectLanguages(text); + if (langResults.length <= 0) { + return false; + } + + LangIdModel.LanguageResult highestScoringResult = langResults[0]; + for (int i = 1; i < langResults.length; i++) { + if (langResults[i].getScore() > highestScoringResult.getScore()) { + highestScoringResult = langResults[i]; + } + } + if (highestScoringResult.getScore() < threshold) { + return false; + } + // TODO: Remove + Log.d(LOG_TAG, String.format("Language detected: <%s:%s>", + highestScoringResult.getLanguage(), highestScoringResult.getScore())); + + final Locale detected = new Locale(highestScoringResult.getLanguage()); + final LocaleList deviceLocales = LocaleList.getDefault(); + final int size = deviceLocales.size(); + for (int i = 0; i < size; i++) { + if (deviceLocales.get(i).getLanguage().equals(detected.getLanguage())) { + return false; + } + } + return true; + } catch (Throwable t) { + Log.e(LOG_TAG, "Error detecting foreign text. Ignored.", t); + } + return false; + } + @Override public void dump(@NonNull IndentingPrintWriter printWriter) { synchronized (mLock) { @@ -698,53 +736,67 @@ public final class TextClassifierImpl implements TextClassifier { public static List<LabeledIntent> create( Context context, String text, + boolean foreignText, @Nullable Instant referenceTime, @Nullable AnnotatorModel.ClassificationResult classification) { final String type = classification != null ? classification.getCollection().trim().toLowerCase(Locale.ENGLISH) - : null; + : ""; text = text.trim(); + final List<LabeledIntent> actions; switch (type) { case TextClassifier.TYPE_EMAIL: - return createForEmail(context, text); + actions = createForEmail(context, text); + break; case TextClassifier.TYPE_PHONE: - return createForPhone(context, text); + actions = createForPhone(context, text); + break; case TextClassifier.TYPE_ADDRESS: - return createForAddress(context, text); + actions = createForAddress(context, text); + break; case TextClassifier.TYPE_URL: - return createForUrl(context, text); - case TextClassifier.TYPE_DATE: + actions = createForUrl(context, text); + break; + case TextClassifier.TYPE_DATE: // fall through case TextClassifier.TYPE_DATE_TIME: if (classification.getDatetimeResult() != null) { final Instant parsedTime = Instant.ofEpochMilli( classification.getDatetimeResult().getTimeMsUtc()); - return createForDatetime(context, type, referenceTime, parsedTime); + actions = createForDatetime(context, type, referenceTime, parsedTime); } else { - return new ArrayList<>(); + actions = new ArrayList<>(); } + break; case TextClassifier.TYPE_FLIGHT_NUMBER: - return createForFlight(context, text); + actions = createForFlight(context, text); + break; default: - return new ArrayList<>(); + actions = new ArrayList<>(); + break; + } + if (foreignText) { + insertTranslateAction(actions, context, text); } + return actions; } @NonNull private static List<LabeledIntent> createForEmail(Context context, String text) { - return Arrays.asList( - new LabeledIntent( - context.getString(com.android.internal.R.string.email), - context.getString(com.android.internal.R.string.email_desc), - new Intent(Intent.ACTION_SENDTO) - .setData(Uri.parse(String.format("mailto:%s", text))), - LabeledIntent.DEFAULT_REQUEST_CODE), - new LabeledIntent( - context.getString(com.android.internal.R.string.add_contact), - context.getString(com.android.internal.R.string.add_contact_desc), - new Intent(Intent.ACTION_INSERT_OR_EDIT) - .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE) - .putExtra(ContactsContract.Intents.Insert.EMAIL, text), - text.hashCode())); + final List<LabeledIntent> actions = new ArrayList<>(); + actions.add(new LabeledIntent( + context.getString(com.android.internal.R.string.email), + context.getString(com.android.internal.R.string.email_desc), + new Intent(Intent.ACTION_SENDTO) + .setData(Uri.parse(String.format("mailto:%s", text))), + LabeledIntent.DEFAULT_REQUEST_CODE)); + actions.add(new LabeledIntent( + context.getString(com.android.internal.R.string.add_contact), + context.getString(com.android.internal.R.string.add_contact_desc), + new Intent(Intent.ACTION_INSERT_OR_EDIT) + .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE) + .putExtra(ContactsContract.Intents.Insert.EMAIL, text), + text.hashCode())); + return actions; } @NonNull @@ -801,12 +853,14 @@ public final class TextClassifierImpl implements TextClassifier { if (Uri.parse(text).getScheme() == null) { text = "http://" + text; } - return Arrays.asList(new LabeledIntent( + final List<LabeledIntent> actions = new ArrayList<>(); + actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.browse), context.getString(com.android.internal.R.string.browse_desc), new Intent(Intent.ACTION_VIEW, Uri.parse(text)) .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()), LabeledIntent.DEFAULT_REQUEST_CODE)); + return actions; } @NonNull @@ -828,12 +882,14 @@ public final class TextClassifierImpl implements TextClassifier { @NonNull private static List<LabeledIntent> createForFlight(Context context, String text) { - return Arrays.asList(new LabeledIntent( + final List<LabeledIntent> actions = new ArrayList<>(); + actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.view_flight), context.getString(com.android.internal.R.string.view_flight_desc), new Intent(Intent.ACTION_WEB_SEARCH) .putExtra(SearchManager.QUERY, text), text.hashCode())); + return actions; } @NonNull @@ -864,5 +920,17 @@ public final class TextClassifierImpl implements TextClassifier { parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION), parsedTime.hashCode()); } + + private static void insertTranslateAction( + List<LabeledIntent> actions, Context context, String text) { + actions.add(new LabeledIntent( + context.getString(com.android.internal.R.string.translate), + context.getString(com.android.internal.R.string.translate_desc), + new Intent(Intent.ACTION_TRANSLATE) + // TODO: Probably better to introduce a "translate" scheme instead of + // using EXTRA_TEXT. + .putExtra(Intent.EXTRA_TEXT, text), + text.hashCode())); + } } } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 10937196a74f..4f1417ed23bb 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -2695,8 +2695,8 @@ public class WebView extends AbsoluteLayout } @Override - public boolean onProvideContentCaptureStructure(ViewStructure structure, int flags) { - return mProvider.getViewDelegate().onProvideContentCaptureStructure(structure, flags); + public void onProvideContentCaptureStructure(ViewStructure structure, int flags) { + mProvider.getViewDelegate().onProvideContentCaptureStructure(structure, flags); } @Override diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java index ceada07c715b..95e7a986efd2 100644 --- a/core/java/android/webkit/WebViewProvider.java +++ b/core/java/android/webkit/WebViewProvider.java @@ -341,10 +341,9 @@ public interface WebViewProvider { return true; // true is the default value returned by View.isVisibleToUserForAutofill() } - default boolean onProvideContentCaptureStructure( + default void onProvideContentCaptureStructure( @SuppressWarnings("unused") android.view.ViewStructure structure, @SuppressWarnings("unused") int flags) { - return false; // WebView provides virtual views and is responsible to notify manager } public AccessibilityNodeProvider getAccessibilityNodeProvider(); diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index f2e478d0e072..15910bbd36d8 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -189,7 +189,7 @@ public class LinearLayout extends ViewGroup { @ViewDebug.FlagToString(mask = Gravity.RELATIVE_LAYOUT_DIRECTION, equals = Gravity.RELATIVE_LAYOUT_DIRECTION, name = "RELATIVE") }, formatToHexString = true) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private int mGravity = Gravity.START | Gravity.TOP; @ViewDebug.ExportedProperty(category = "measurement") diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index 74051e2d5a1a..506d615fc077 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -204,7 +204,7 @@ public class RelativeLayout extends ViewGroup { private View mBaselineView = null; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private int mGravity = Gravity.START | Gravity.TOP; private final Rect mContentBounds = new Rect(); private final Rect mSelfBounds = new Rect(); diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index d55c09f7fcb6..79dc6708f9c3 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -1419,27 +1419,10 @@ public class Switch extends CompoundButton { return Switch.class.getName(); } + /** @hide */ @Override - public void onProvideStructure(ViewStructure structure) { - super.onProvideStructure(structure); - onProvideStructureForAssistOrAutofillOrViewCapture(structure); - } - - @Override - public void onProvideAutofillStructure(ViewStructure structure, int flags) { - super.onProvideAutofillStructure(structure, flags); - onProvideStructureForAssistOrAutofillOrViewCapture(structure); - } - - @Override - public boolean onProvideContentCaptureStructure(ViewStructure structure, int flags) { - final boolean notifyManager = super.onProvideContentCaptureStructure(structure, flags); - onProvideStructureForAssistOrAutofillOrViewCapture(structure); - return notifyManager; - } - - // NOTE: currently there is no difference for any type, so it doesn't take flags - private void onProvideStructureForAssistOrAutofillOrViewCapture(ViewStructure structure) { + protected void onProvideStructure(@NonNull ViewStructure structure, + @ViewStructureType int viewFor, int flags) { CharSequence switchText = isChecked() ? mTextOn : mTextOff; if (!TextUtils.isEmpty(switchText)) { CharSequence oldText = structure.getText(); diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java index 3861739228a3..826cd89f1d01 100644 --- a/core/java/com/android/internal/os/KernelCpuThreadReader.java +++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java @@ -109,6 +109,13 @@ public class KernelCpuThreadReader { uid -> 1000 <= uid && uid < 2000; /** + * Do not report any threads that have a total CPU usage (across all frequencies) less than or + * equal to this number. This significantly reduces the amount of reported threads without + * losing any important information + */ + private static final int TOTAL_CPU_USAGE_THRESHOLD_MILLIS = 20; + + /** * Value returned when there was an error getting an integer ID value (e.g. PID, UID) */ private static final int ID_ERROR = -1; @@ -339,6 +346,15 @@ public class KernelCpuThreadReader { } int[] cpuUsages = mFrequencyBucketCreator.getBucketedValues(cpuUsagesLong); + // Filter threads that have low total CPU usage + int cpuUsageSum = 0; + for (int i = 0; i < cpuUsages.length; i++) { + cpuUsageSum += cpuUsages[i]; + } + if (cpuUsageSum <= TOTAL_CPU_USAGE_THRESHOLD_MILLIS) { + return null; + } + return new ThreadCpuUsage(threadId, threadName, cpuUsages); } diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java index 2b661c25194b..cf2a297bb6a5 100644 --- a/core/java/com/android/internal/os/LooperStats.java +++ b/core/java/com/android/internal/os/LooperStats.java @@ -50,6 +50,7 @@ public class LooperStats implements Looper.Observer { private int mSamplingInterval; private CachedDeviceState.Readonly mDeviceState; private long mStartTime = System.currentTimeMillis(); + private boolean mAddDebugEntries = false; public LooperStats(int samplingInterval, int entriesSizeCap) { this.mSamplingInterval = samplingInterval; @@ -60,6 +61,10 @@ public class LooperStats implements Looper.Observer { mDeviceState = deviceState; } + public void setAddDebugEntries(boolean addDebugEntries) { + mAddDebugEntries = addDebugEntries; + } + @Override public Object messageDispatchStarting() { if (deviceStateAllowsCollection() && shouldCollectDetailedData()) { @@ -142,9 +147,22 @@ public class LooperStats implements Looper.Observer { // Add the overflow and collision entries only if they have any data. maybeAddSpecialEntry(exportedEntries, mOverflowEntry); maybeAddSpecialEntry(exportedEntries, mHashCollisionEntry); + // Debug entries added to help validate the data. + if (mAddDebugEntries) { + exportedEntries.add(createDebugEntry("start_time_millis", mStartTime)); + exportedEntries.add(createDebugEntry("end_time_millis", System.currentTimeMillis())); + } return exportedEntries; } + private ExportedEntry createDebugEntry(String variableName, long value) { + final Entry entry = new Entry("__DEBUG_" + variableName); + entry.messageCount = 1; + entry.recordedMessageCount = 1; + entry.maxDelayMillis = value; + return new ExportedEntry(entry); + } + /** Returns a timestamp indicating when the statistics were last reset. */ public long getStartTimeMillis() { return mStartTime; diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index b0dbaa03760e..99f096d57dba 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -97,7 +97,7 @@ message KeyguardServiceDelegateProto { message WindowManagerPolicyProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; - optional int32 last_system_ui_flags = 1; + optional int32 last_system_ui_flags = 1 [deprecated=true]; enum UserRotationMode { USER_ROTATION_FREE = 0; USER_ROTATION_LOCKED = 1; @@ -108,18 +108,18 @@ message WindowManagerPolicyProto { optional bool screen_on_fully = 5; optional bool keyguard_draw_complete = 6; optional bool window_manager_draw_complete = 7; - optional string focused_app_token = 8; - optional IdentifierProto focused_window = 9; - optional IdentifierProto top_fullscreen_opaque_window = 10; - optional IdentifierProto top_fullscreen_opaque_or_dimming_window = 11; + optional string focused_app_token = 8 [deprecated=true]; + optional IdentifierProto focused_window = 9 [deprecated=true]; + optional IdentifierProto top_fullscreen_opaque_window = 10 [deprecated=true]; + optional IdentifierProto top_fullscreen_opaque_or_dimming_window = 11 [deprecated=true]; optional bool keyguard_occluded = 12; optional bool keyguard_occluded_changed = 13; optional bool keyguard_occluded_pending = 14; - optional bool force_status_bar = 15; - optional bool force_status_bar_from_keyguard = 16; - optional BarControllerProto status_bar = 17; - optional BarControllerProto navigation_bar = 18; - optional WindowOrientationListenerProto orientation_listener = 19; + optional bool force_status_bar = 15 [deprecated=true]; + optional bool force_status_bar_from_keyguard = 16 [deprecated=true]; + optional BarControllerProto status_bar = 17 [deprecated=true]; + optional BarControllerProto navigation_bar = 18 [deprecated=true]; + optional WindowOrientationListenerProto orientation_listener = 19 [deprecated=true]; optional KeyguardServiceDelegateProto keyguard_delegate = 20; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index e35b701aadcc..e064423a9bb9 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4500,6 +4500,14 @@ </intent-filter> </receiver> + <receiver android:name="com.android.server.updates.ConversationActionsInstallReceiver" + android:permission="android.permission.UPDATE_CONFIG"> + <intent-filter> + <action android:name="android.intent.action.ACTION_UPDATE_CONVERSATION_ACTIONS" /> + <data android:scheme="content" android:host="*" android:mimeType="*/*" /> + </intent-filter> + </receiver> + <receiver android:name="com.android.server.updates.CarrierIdInstallReceiver" android:permission="android.permission.UPDATE_CONFIG"> <intent-filter> diff --git a/core/res/res/layout/notification_material_action.xml b/core/res/res/layout/notification_material_action.xml index 3c9f6eecc7d6..c024dbed56c0 100644 --- a/core/res/res/layout/notification_material_action.xml +++ b/core/res/res/layout/notification_material_action.xml @@ -16,16 +16,14 @@ --> <Button xmlns:android="http://schemas.android.com/apk/res/android" - style="@android:style/Widget.Material.Light.Button.Borderless.Small" + style="@android:style/NotificationAction" android:id="@+id/action0" android:layout_width="wrap_content" android:layout_height="48dp" android:layout_gravity="center" android:gravity="start|center_vertical" android:layout_marginStart="4dp" - android:textColor="@color/notification_default_color" android:singleLine="true" android:textAlignment="viewStart" android:ellipsize="end" - android:background="@drawable/notification_material_action_background" /> diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml index 07559f439933..425801991927 100644 --- a/core/res/res/layout/notification_material_action_list.xml +++ b/core/res/res/layout/notification_material_action_list.xml @@ -18,6 +18,7 @@ android:id="@+id/actions_container" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginTop="@dimen/notification_action_list_margin_top" android:layout_gravity="bottom"> <com.android.internal.widget.NotificationActionListLayout android:id="@+id/actions" @@ -27,6 +28,7 @@ android:orientation="horizontal" android:gravity="center_vertical" android:visibility="gone" + android:background="@color/notification_action_list_background_color" > <!-- actions will be added here --> </com.android.internal.widget.NotificationActionListLayout> diff --git a/core/res/res/layout/notification_material_action_tombstone.xml b/core/res/res/layout/notification_material_action_tombstone.xml index 9fa7c6a28020..f16572486df1 100644 --- a/core/res/res/layout/notification_material_action_tombstone.xml +++ b/core/res/res/layout/notification_material_action_tombstone.xml @@ -16,7 +16,7 @@ --> <Button xmlns:android="http://schemas.android.com/apk/res/android" - style="@android:style/Widget.Material.Light.Button.Borderless.Small" + style="@android:style/NotificationTombstoneAction" android:id="@+id/action0" android:layout_width="wrap_content" android:layout_height="48dp" diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml index 9de8842ae5da..a2ad3b90e464 100644 --- a/core/res/res/values-night/values.xml +++ b/core/res/res/values-night/values.xml @@ -20,7 +20,7 @@ <!-- Color palette --> <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item> <item name="colorSecondary">@color/secondary_device_default_settings</item> - <item name="colorAccent">@color/white</item> + <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> <item name="colorControlNormal">?attr/textColorPrimary</item> <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 605662a91db2..6c4861b34429 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -7941,6 +7941,11 @@ <!-- Uri that specifies a settings Slice for this wallpaper. --> <attr name="settingsSliceUri" /> + <!-- Indicates that this wallpaper service can support multiple engines to render on each + surface independently. An example use case is a multi-display set-up where the + wallpaper service can render surfaces to each of the connected displays. --> + <attr name="supportsMultipleDisplays" format="boolean" /> + </declare-styleable> <!-- Use <code>dream</code> as the root tag of the XML resource that diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 4122cf02ad20..16c074484e2c 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -146,10 +146,14 @@ <color name="notification_default_color">#757575</color> <!-- Gray 600 --> + <color name="notification_action_button_text_color">@color/notification_default_color</color> + <color name="notification_progress_background_color">@color/secondary_text_material_light</color> <color name="notification_action_list">#ffeeeeee</color> + <color name="notification_action_list_background_color">@null</color> + <!-- Keyguard colors --> <color name="keyguard_avatar_frame_color">#ffffffff</color> <color name="keyguard_avatar_frame_shadow_color">#80000000</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 829d6f57f896..1d809613e2b2 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -364,7 +364,7 @@ <!-- Max number of Bluetooth tethering connections allowed. If this is updated config_tether_dhcp_range has to be updated appropriately. --> - <integer translateable="false" name="config_max_pan_devices">5</integer> + <integer translatable="false" name="config_max_pan_devices">5</integer> <!-- Dhcp range (min, max) to use for tethering purposes --> <string-array translatable="false" name="config_tether_dhcp_range"> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index e902989094f2..f7b9961c39e8 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -200,6 +200,9 @@ <!-- The height of the notification action list --> <dimen name="notification_action_list_height">60dp</dimen> + <!-- The margin of the notification action list at the top --> + <dimen name="notification_action_list_margin_top">0dp</dimen> + <!-- The height of the notification action list --> <dimen name="notification_action_emphasized_height">48dp</dimen> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 73dae0801b8e..3373d14270bb 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2920,6 +2920,7 @@ <public name="shell" /> <public name="interactiveUiTimeout" /> <public name="importantForContentCapture" /> + <public name="supportsMultipleDisplays" /> </public-group> <public-group type="drawable" first-id="0x010800b4"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 200c35d67b17..9204ee438aa5 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2989,6 +2989,12 @@ <!-- Accessibility description for an item in the text selection menu to track a flight [CHAR LIMIT=NONE] --> <string name="view_flight_desc">Track selected flight</string> + <!-- Label for item in the text selection menu to translate selected text with a translation app. Should be a verb. [CHAR LIMIT=30] --> + <string name="translate">Translate</string> + + <!-- Accessibility description for an item in the text selection menu to translate selected text with a translation app. [CHAR LIMIT=NONE] --> + <string name="translate_desc">Translate selected text</string> + <!-- If the device is getting low on internal storage, a notification is shown to the user. This is the title of that notification. --> <string name="low_internal_storage_view_title">Storage space running out</string> <!-- If the device is getting low on internal storage, a notification is shown to the user. This is the message of that notification. --> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index bd53936b596d..18f7e4821a07 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1526,10 +1526,22 @@ please see styles_device_defaults.xml. <item name="gravity">top</item> </style> - <!-- Colored bordered ink button --> + <!-- The style for normal action button on notification --> + <style name="NotificationAction" parent="Widget.Material.Light.Button.Borderless.Small"> + <item name="textColor">@color/notification_action_button_text_color</item> + <item name="background">@drawable/notification_material_action_background</item> + </style> + + <!-- The style for emphasized action button on notification: Colored bordered ink button --> <style name="NotificationEmphasizedAction" parent="Widget.Material.Button"> <item name="background">@drawable/btn_notification_emphasized</item> <item name="stateListAnimator">@anim/flat_button_state_list_anim_material</item> </style> + <!-- The style for disabled action button on notification --> + <style name="NotificationTombstoneAction" parent="NotificationAction"> + <item name="textColor">#555555</item> + </style> + + </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 82c9ff314138..7a78327a73d7 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -574,6 +574,8 @@ <java-symbol type="string" name="add_calendar_event_desc" /> <java-symbol type="string" name="view_flight" /> <java-symbol type="string" name="view_flight_desc" /> + <java-symbol type="string" name="translate" /> + <java-symbol type="string" name="translate_desc" /> <java-symbol type="string" name="textSelectionCABTitle" /> <java-symbol type="string" name="BaMmi" /> <java-symbol type="string" name="CLIRDefaultOffNextCallOff" /> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 002b6a84f592..2c001c973100 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -124,6 +124,7 @@ public class SettingsBackupTest { Settings.Global.AUTOFILL_LOGGING_LEVEL, Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE, Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS, + Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS, Settings.Global.AUTOMATIC_POWER_SAVER_MODE, Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD, Settings.Global.BATTERY_DISCHARGE_THRESHOLD, @@ -181,6 +182,8 @@ public class SettingsBackupTest { Settings.Global.CONNECTIVITY_METRICS_BUFFER_SIZE, Settings.Global.CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS, Settings.Global.CONTACT_METADATA_SYNC_ENABLED, + Settings.Global.CONVERSATION_ACTIONS_UPDATE_CONTENT_URL, + Settings.Global.CONVERSATION_ACTIONS_UPDATE_METADATA_URL, Settings.Global.CONTACTS_DATABASE_WAL_ENABLED, Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE, Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI, @@ -281,6 +284,7 @@ public class SettingsBackupTest { Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS, Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS, Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST, + Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS, Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS, Settings.Global.LOCATION_GLOBAL_KILL_SWITCH, Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED, diff --git a/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java new file mode 100644 index 000000000000..018085698d0c --- /dev/null +++ b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java @@ -0,0 +1,134 @@ +/* + * 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 android.view.textclassifier; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.ComponentName; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.support.test.InstrumentationRegistry; + +import androidx.annotation.Nullable; + +import com.google.common.base.Preconditions; + +import org.mockito.stubbing.Answer; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * A builder used to build a fake context for testing. + */ +// TODO: Consider making public. +final class FakeContextBuilder { + + /** + * A component name that can be used for tests. + */ + public static final ComponentName DEFAULT_COMPONENT = new ComponentName("pkg", "cls"); + + private final PackageManager mPackageManager; + private final ContextWrapper mContext; + private final Map<String, ComponentName> mComponents = new HashMap<>(); + private @Nullable ComponentName mAllIntentComponent; + + FakeContextBuilder() { + mPackageManager = mock(PackageManager.class); + when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(null); + mContext = new ContextWrapper(InstrumentationRegistry.getTargetContext()) { + @Override + public PackageManager getPackageManager() { + return mPackageManager; + } + }; + } + + /** + * Sets the component name of an activity to handle the specified intent action. + * <p> + * <strong>NOTE: </strong>By default, no component is set to handle any intent. + */ + public FakeContextBuilder setIntentComponent( + String intentAction, @Nullable ComponentName component) { + Preconditions.checkNotNull(intentAction); + mComponents.put(intentAction, component); + return this; + } + + + /** + * Sets the component name of an activity to handle all intents. + * <p> + * <strong>NOTE: </strong>By default, no component is set to handle any intent. + */ + public FakeContextBuilder setAllIntentComponent(@Nullable ComponentName component) { + mAllIntentComponent = component; + return this; + } + + /** + * Builds and returns a fake context. + */ + public Context build() { + when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenAnswer( + (Answer<ResolveInfo>) invocation -> { + final String action = ((Intent) invocation.getArgument(0)).getAction(); + final ComponentName component = mComponents.containsKey(action) + ? mComponents.get(action) + : mAllIntentComponent; + return getResolveInfo(component); + }); + return mContext; + } + + /** + * Returns a component name with random package and class names. + */ + public static ComponentName newComponent() { + return new ComponentName(UUID.randomUUID().toString(), UUID.randomUUID().toString()); + } + + private static ResolveInfo getResolveInfo(ComponentName component) { + final ResolveInfo info; + if (component == null) { + info = null; + } else { + // NOTE: If something breaks in TextClassifier because we expect more fields to be set + // in here, just add them. + info = new ResolveInfo(); + info.activityInfo = new ActivityInfo(); + info.activityInfo.packageName = component.getPackageName(); + info.activityInfo.name = component.getClassName(); + info.activityInfo.exported = true; + info.activityInfo.applicationInfo = new ApplicationInfo(); + info.activityInfo.applicationInfo.icon = 0; + } + return info; + } + +} diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java index 46aa5b484c5f..a3f69d93ad44 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java @@ -20,18 +20,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.argThat; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import android.content.Context; -import android.content.ContextWrapper; import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.net.Uri; import android.os.LocaleList; import android.service.textclassifier.TextClassifierService; import android.support.test.InstrumentationRegistry; @@ -41,7 +33,6 @@ import android.support.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentMatcher; @SmallTest @RunWith(AndroidJUnit4.class) @@ -78,23 +69,10 @@ public class TextClassificationManagerTest { @Test public void testCannotResolveIntent() { - final PackageManager fakePackageMgr = mock(PackageManager.class); - - ResolveInfo validInfo = mContext.getPackageManager().resolveActivity( - new Intent(Intent.ACTION_DIAL).setData(Uri.parse("tel:+12122537077")), 0); - // Make packageManager fail when it gets the following intent: - ArgumentMatcher<Intent> toFailIntent = - intent -> intent.getAction().equals(Intent.ACTION_INSERT_OR_EDIT); - - when(fakePackageMgr.resolveActivity(any(Intent.class), anyInt())).thenReturn(validInfo); - when(fakePackageMgr.resolveActivity(argThat(toFailIntent), anyInt())).thenReturn(null); - - ContextWrapper fakeContext = new ContextWrapper(mContext) { - @Override - public PackageManager getPackageManager() { - return fakePackageMgr; - } - }; + Context fakeContext = new FakeContextBuilder() + .setAllIntentComponent(FakeContextBuilder.DEFAULT_COMPONENT) + .setIntentComponent(Intent.ACTION_INSERT_OR_EDIT, null) + .build(); TextClassifier fallback = TextClassifier.NO_OP; TextClassifier classifier = new TextClassifierImpl( diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java index b242a34cc703..385bad5ab3f9 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java @@ -39,7 +39,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Comparator; +import java.util.List; import java.util.function.Predicate; +import java.util.stream.Collectors; @SmallTest @RunWith(AndroidJUnit4.class) @@ -56,8 +58,8 @@ public class KernelCpuThreadReaderTest { 1000, 2000, 3000, 4000, }; private static final int[][] THREAD_CPU_TIMES = { - {1, 0, 0, 1}, - {0, 0, 0, 0}, + {100, 0, 0, 100}, + {0, 0, 9999999, 0}, {1000, 1000, 1000, 1000}, {0, 1, 2, 3}, }; @@ -108,6 +110,42 @@ public class KernelCpuThreadReaderTest { } @Test + public void testReader_filtersLowTotalCpuUsage() throws IOException { + KernelCpuThreadReader.Injector processUtils = + new KernelCpuThreadReader.Injector() { + @Override + public int myPid() { + return PROCESS_ID; + } + + @Override + public int myUid() { + return UID; + } + + @Override + public int getUidForPid(int pid) { + return 0; + } + }; + setupDirectory(mProcDirectory.toPath().resolve("self"), new int[]{1, 2}, PROCESS_NAME, + THREAD_NAMES, new int[]{1000, 2000}, new int[][]{{0, 1}, {100, 0}}); + + final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader( + mProcDirectory.toPath(), + mProcDirectory.toPath().resolve("self/task/1/time_in_state"), + processUtils); + final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = + kernelCpuThreadReader.getCurrentProcessCpuUsage(); + + List<Integer> threadIds = processCpuUsage.threadCpuUsages.stream() + .map(t -> t.threadId) + .collect(Collectors.toList()); + assertEquals(1, threadIds.size()); + assertEquals(2, (long) threadIds.get(0)); + } + + @Test public void testReader_byUids() throws IOException { int[] uids = new int[]{0, 2, 3, 4, 5, 6000}; Predicate<Integer> uidPredicate = uid -> uid == 0 || uid >= 4; @@ -134,7 +172,7 @@ public class KernelCpuThreadReaderTest { setupDirectory(mProcDirectory.toPath().resolve(String.valueOf(uid)), new int[]{uid * 10}, "process" + uid, new String[]{"thread" + uid}, new int[]{1000}, - new int[][]{{uid}}); + new int[][]{{uid + 100}}); } final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader( mProcDirectory.toPath(), @@ -151,7 +189,7 @@ public class KernelCpuThreadReaderTest { int uid = expectedUids[i]; checkResults(processCpuUsage, kernelCpuThreadReader.getCpuFrequenciesKhz(), uid, uid, new int[]{uid * 10}, "process" + uid, new String[]{"thread" + uid}, - new int[]{1000}, new int[][]{{uid}}); + new int[]{1000}, new int[][]{{uid + 100}}); } } diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java index f637b7c7ec32..31dde5c79f27 100644 --- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java @@ -105,7 +105,6 @@ public final class LooperStatsTest { assertThat(entry.recordedDelayMessageCount).isEqualTo(1); assertThat(entry.delayMillis).isEqualTo(30); assertThat(entry.maxDelayMillis).isEqualTo(30); - } @Test @@ -429,6 +428,28 @@ public final class LooperStatsTest { assertThat(entries).hasSize(0); } + @Test + public void testAddsDebugEntries() { + TestableLooperStats looperStats = new TestableLooperStats(1, 100); + looperStats.setAddDebugEntries(true); + + Message message = mHandlerFirst.obtainMessage(1000); + message.when = looperStats.getSystemUptimeMillis(); + Object token = looperStats.messageDispatchStarting(); + looperStats.messageDispatched(token, message); + + List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); + assertThat(entries).hasSize(3); + LooperStats.ExportedEntry debugEntry1 = entries.get(1); + assertThat(debugEntry1.handlerClassName).isEqualTo(""); + assertThat(debugEntry1.messageName).isEqualTo("__DEBUG_start_time_millis"); + assertThat(debugEntry1.maxDelayMillis).isEqualTo(looperStats.getStartTimeMillis()); + LooperStats.ExportedEntry debugEntry2 = entries.get(2); + assertThat(debugEntry2.handlerClassName).isEqualTo(""); + assertThat(debugEntry2.messageName).isEqualTo("__DEBUG_end_time_millis"); + assertThat(debugEntry2.maxDelayMillis).isAtLeast(looperStats.getStartTimeMillis()); + } + private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) { try { r.run(); @@ -450,6 +471,7 @@ public final class LooperStatsTest { super(samplingInterval, sizeCap); this.mSamplingInterval = samplingInterval; this.setDeviceState(mDeviceState.getReadonlyClient()); + this.setAddDebugEntries(false); } void tickRealtime(long micros) { diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java index fdd638adba81..dea2f45e9d33 100644 --- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java +++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java @@ -108,11 +108,10 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback * Scaled mask based on the view bounds. */ private final Path mMask; + private final Path mMaskScaleOnly; private final Matrix mMaskMatrix; private final Region mTransparentRegion; - private Bitmap mMaskBitmap; - /** * Indices used to access {@link #mLayerState.mChildDrawable} array for foreground and * background layer. @@ -156,8 +155,8 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback sMask = PathParser.createPathFromPathData( Resources.getSystem().getString(R.string.config_icon_mask)); } - mMask = PathParser.createPathFromPathData( - Resources.getSystem().getString(R.string.config_icon_mask)); + mMask = new Path(sMask); + mMaskScaleOnly = new Path(mMask); mMaskMatrix = new Matrix(); mCanvas = new Canvas(); mTransparentRegion = new Region(); @@ -329,24 +328,19 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback } private void updateMaskBoundsInternal(Rect b) { + // reset everything that depends on the view bounds mMaskMatrix.setScale(b.width() / MASK_SIZE, b.height() / MASK_SIZE); + sMask.transform(mMaskMatrix, mMaskScaleOnly); + + mMaskMatrix.postTranslate(b.left, b.top); sMask.transform(mMaskMatrix, mMask); - if (mMaskBitmap == null || mMaskBitmap.getWidth() != b.width() || - mMaskBitmap.getHeight() != b.height()) { - mMaskBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ALPHA_8); + if (mLayersBitmap == null || mLayersBitmap.getWidth() != b.width() + || mLayersBitmap.getHeight() != b.height()) { mLayersBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ARGB_8888); } - // mMaskBitmap bound [0, w] x [0, h] - mCanvas.setBitmap(mMaskBitmap); - mPaint.setShader(null); - mCanvas.drawPath(mMask, mPaint); - // mMask bound [left, top, right, bottom] - mMaskMatrix.postTranslate(b.left, b.top); - mMask.reset(); - sMask.transform(mMaskMatrix, mMask); - // reset everything that depends on the view bounds + mPaint.setShader(null); mTransparentRegion.setEmpty(); mLayersShader = null; } @@ -371,9 +365,11 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback mLayersShader = new BitmapShader(mLayersBitmap, TileMode.CLAMP, TileMode.CLAMP); mPaint.setShader(mLayersShader); } - if (mMaskBitmap != null) { + if (mMaskScaleOnly != null) { Rect bounds = getBounds(); - canvas.drawBitmap(mMaskBitmap, bounds.left, bounds.top, mPaint); + canvas.translate(bounds.left, bounds.top); + canvas.drawPath(mMaskScaleOnly, mPaint); + canvas.translate(-bounds.left, -bounds.top); } } @@ -549,7 +545,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback final ChildDrawable[] layers = mLayerState.mChildren; for (int i = 0; i < mLayerState.N_CHILDREN; i++) { - if (layers[i].mDrawable.isProjected()) { + if (layers[i].mDrawable != null && layers[i].mDrawable.isProjected()) { return true; } } @@ -674,7 +670,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback @Override public int getAlpha() { - return PixelFormat.TRANSLUCENT; + return mPaint.getAlpha(); } @Override @@ -718,10 +714,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback @Override public int getOpacity() { - if (mLayerState.mOpacityOverride != PixelFormat.UNKNOWN) { - return mLayerState.mOpacityOverride; - } - return mLayerState.getOpacity(); + return PixelFormat.TRANSLUCENT; } @Override diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 1cb0d25d8c08..365be10f597f 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -349,7 +349,7 @@ bool AssetManager::createIdmap(const char* targetApkPath, const char* overlayApk goto exit; } } - ret = tables[0].createIdmap(tables[1], targetCrc, overlayCrc, + ret = tables[1].createIdmap(tables[0], targetCrc, overlayCrc, targetApkPath, overlayApkPath, (void**)outData, outSize) == NO_ERROR; } diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 76db18de6122..f7fb89e54ef3 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -26,7 +26,9 @@ #include <algorithm> #include <limits> +#include <map> #include <memory> +#include <set> #include <type_traits> #include <android-base/macros.h> @@ -7033,170 +7035,178 @@ status_t DynamicRefTable::lookupResourceValue(Res_value* value) const { return NO_ERROR; } -struct IdmapTypeMap { - ssize_t overlayTypeId; - size_t entryOffset; - Vector<uint32_t> entryMap; +struct IdmapMatchingResources { + void Add(uint32_t targetResId, uint32_t overlayResId) { + uint8_t targetTypeid = Res_GETTYPE(targetResId); + if (typeMappings.find(targetTypeid) == typeMappings.end()) { + typeMappings.emplace(targetTypeid, std::set<std::pair<uint32_t, uint32_t>>()); + } + auto& entries = typeMappings[targetTypeid]; + entries.insert(std::make_pair(targetResId, overlayResId)); + } + + void FixPadding() { + for (auto ti = typeMappings.cbegin(); ti != typeMappings.cend(); ++ti) { + uint32_t last_seen = 0xffffffff; + size_t total_entries = 0; + for (auto ei = ti->second.cbegin(); ei != ti->second.cend(); ++ei) { + assert(last_seen == 0xffffffff || last_seen < ei->first); + entryPadding[ei->first] = (last_seen == 0xffffffff) ? 0 : ei->first - last_seen - 1; + last_seen = ei->first; + total_entries += 1 + entryPadding[ei->first]; + } + numberOfEntriesIncludingPadding[ti->first] = total_entries; + } + } + + // resource type ID in context of target -> set of resource entries mapping target -> overlay + std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>> typeMappings; + + // resource ID in context of target -> trailing padding for that resource (call FixPadding + // before use) + std::map<uint32_t, size_t> entryPadding; + + // resource type ID in context of target -> total number of entries, including padding entries, + // for that type (call FixPadding before use) + std::map<uint8_t, size_t> numberOfEntriesIncludingPadding; }; -status_t ResTable::createIdmap(const ResTable& overlay, +status_t ResTable::createIdmap(const ResTable& targetResTable, uint32_t targetCrc, uint32_t overlayCrc, const char* targetPath, const char* overlayPath, void** outData, size_t* outSize) const { - // see README for details on the format of map - if (mPackageGroups.size() == 0) { - ALOGW("idmap: target package has no package groups, cannot create idmap\n"); + if (targetPath == NULL || overlayPath == NULL || outData == NULL || outSize == NULL) { + ALOGE("idmap: unexpected NULL parameter"); return UNKNOWN_ERROR; } - - if (mPackageGroups[0]->packages.size() == 0) { - ALOGW("idmap: target package has no packages in its first package group, " - "cannot create idmap\n"); + if (strlen(targetPath) > 255) { + ALOGE("idmap: target path exceeds idmap file format limit of 255 chars"); + return UNKNOWN_ERROR; + } + if (strlen(overlayPath) > 255) { + ALOGE("idmap: overlay path exceeds idmap file format limit of 255 chars"); + return UNKNOWN_ERROR; + } + if (mPackageGroups.size() == 0 || mPackageGroups[0]->packages.size() == 0) { + ALOGE("idmap: invalid overlay package"); + return UNKNOWN_ERROR; + } + if (targetResTable.mPackageGroups.size() == 0 || + targetResTable.mPackageGroups[0]->packages.size() == 0) { + ALOGE("idmap: invalid target package"); return UNKNOWN_ERROR; } - // The number of resources overlaid that were not explicitly marked overlayable. - size_t forcedOverlayCount = 0u; - - KeyedVector<uint8_t, IdmapTypeMap> map; - - // overlaid packages are assumed to contain only one package group - const PackageGroup* pg = mPackageGroups[0]; - - // starting size is header - *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES; + const ResTable_package* targetPackageStruct = targetResTable.mPackageGroups[0]->packages[0]->package; + const size_t tmpNameSize = arraysize(targetPackageStruct->name); + char16_t tmpName[tmpNameSize]; + strcpy16_dtoh(tmpName, targetPackageStruct->name, tmpNameSize); + const String16 targetPackageName(tmpName); - // target package id and number of types in map - *outSize += 2 * sizeof(uint16_t); + const PackageGroup* packageGroup = mPackageGroups[0]; - // overlay packages are assumed to contain only one package group - const ResTable_package* overlayPackageStruct = overlay.mPackageGroups[0]->packages[0]->package; - char16_t tmpName[sizeof(overlayPackageStruct->name)/sizeof(overlayPackageStruct->name[0])]; - strcpy16_dtoh(tmpName, overlayPackageStruct->name, sizeof(overlayPackageStruct->name)/sizeof(overlayPackageStruct->name[0])); - const String16 overlayPackage(tmpName); + // the number of resources overlaid that were not explicitly marked overlayable + size_t forcedOverlayCount = 0u; - for (size_t typeIndex = 0; typeIndex < pg->types.size(); ++typeIndex) { - const TypeList& typeList = pg->types[typeIndex]; + // find the resources that exist in both packages + IdmapMatchingResources matchingResources; + for (size_t typeIndex = 0; typeIndex < packageGroup->types.size(); ++typeIndex) { + const TypeList& typeList = packageGroup->types[typeIndex]; if (typeList.isEmpty()) { continue; } - const Type* typeConfigs = typeList[0]; - IdmapTypeMap typeMap; - typeMap.overlayTypeId = -1; - typeMap.entryOffset = 0; - for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) { - uint32_t resID = Res_MAKEID(pg->id - 1, typeIndex, entryIndex); - resource_name resName; - if (!this->getResourceName(resID, false, &resName)) { - if (typeMap.entryMap.isEmpty()) { - typeMap.entryOffset++; - } + uint32_t overlay_resid = Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex); + resource_name current_res; + if (!getResourceName(overlay_resid, false, ¤t_res)) { continue; } uint32_t typeSpecFlags = 0u; - const String16 overlayType(resName.type, resName.typeLen); - const String16 overlayName(resName.name, resName.nameLen); - uint32_t overlayResID = overlay.identifierForName(overlayName.string(), - overlayName.size(), - overlayType.string(), - overlayType.size(), - overlayPackage.string(), - overlayPackage.size(), - &typeSpecFlags); - if (overlayResID == 0) { - // No such target resource was found. - if (typeMap.entryMap.isEmpty()) { - typeMap.entryOffset++; - } + const uint32_t target_resid = targetResTable.identifierForName( + current_res.name, + current_res.nameLen, + current_res.type, + current_res.typeLen, + targetPackageName.string(), + targetPackageName.size(), + &typeSpecFlags); + + if (target_resid == 0) { continue; } - // Now that we know this is being overlaid, check if it can be, and emit a warning if - // it can't. if ((dtohl(typeConfigs->typeSpecFlags[entryIndex]) & ResTable_typeSpec::SPEC_OVERLAYABLE) == 0) { - forcedOverlayCount++; - } - - if (typeMap.overlayTypeId == -1) { - typeMap.overlayTypeId = Res_GETTYPE(overlayResID) + 1; - } - - if (Res_GETTYPE(overlayResID) + 1 != static_cast<size_t>(typeMap.overlayTypeId)) { - ALOGE("idmap: can't mix type ids in entry map. Resource 0x%08x maps to 0x%08x" - " but entries should map to resources of type %02zx", - resID, overlayResID, typeMap.overlayTypeId); - return BAD_TYPE; - } - - if (typeMap.entryOffset + typeMap.entryMap.size() < entryIndex) { - // pad with 0xffffffff's (indicating non-existing entries) before adding this entry - size_t index = typeMap.entryMap.size(); - size_t numItems = entryIndex - (typeMap.entryOffset + index); - if (typeMap.entryMap.insertAt(0xffffffff, index, numItems) < 0) { - return NO_MEMORY; - } + ++forcedOverlayCount; } - typeMap.entryMap.add(Res_GETENTRY(overlayResID)); - } - if (!typeMap.entryMap.isEmpty()) { - if (map.add(static_cast<uint8_t>(typeIndex), typeMap) < 0) { - return NO_MEMORY; - } - *outSize += (4 * sizeof(uint16_t)) + (typeMap.entryMap.size() * sizeof(uint32_t)); + matchingResources.Add(target_resid, overlay_resid); } } - if (map.isEmpty()) { - ALOGW("idmap: no resources in overlay package present in base package"); + if (matchingResources.typeMappings.empty()) { + ALOGE("idmap: no matching resources"); return UNKNOWN_ERROR; } + matchingResources.FixPadding(); + + // write idmap + *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES; // magic, version, target and overlay crc + *outSize += 2 * sizeof(uint16_t); // target package id, type count + const auto typesEnd = matchingResources.typeMappings.cend(); + for (auto ti = matchingResources.typeMappings.cbegin(); ti != typesEnd; ++ti) { + *outSize += 4 * sizeof(uint16_t); // target type, overlay type, entry count, entry offset + *outSize += matchingResources.numberOfEntriesIncludingPadding[ti->first] * + sizeof(uint32_t); // entries + } if ((*outData = malloc(*outSize)) == NULL) { return NO_MEMORY; } - uint32_t* data = (uint32_t*)*outData; - *data++ = htodl(IDMAP_MAGIC); - *data++ = htodl(ResTable::IDMAP_CURRENT_VERSION); - *data++ = htodl(targetCrc); - *data++ = htodl(overlayCrc); - const char* paths[] = { targetPath, overlayPath }; - for (int j = 0; j < 2; ++j) { - char* p = (char*)data; - const char* path = paths[j]; - const size_t I = strlen(path); - if (I > 255) { - ALOGV("path exceeds expected 255 characters: %s\n", path); - return UNKNOWN_ERROR; - } - for (size_t i = 0; i < 256; ++i) { - *p++ = i < I ? path[i] : '\0'; - } - data += 256 / sizeof(uint32_t); + // write idmap header + uint32_t* data = reinterpret_cast<uint32_t*>(*outData); + *data++ = htodl(IDMAP_MAGIC); // write: magic + *data++ = htodl(ResTable::IDMAP_CURRENT_VERSION); // write: version + *data++ = htodl(targetCrc); // write: target crc + *data++ = htodl(overlayCrc); // write: overlay crc + + char* charData = reinterpret_cast<char*>(data); + size_t pathLen = strlen(targetPath); + for (size_t i = 0; i < 256; ++i) { + *charData++ = i < pathLen ? targetPath[i] : '\0'; // write: target path } - const size_t mapSize = map.size(); + pathLen = strlen(overlayPath); + for (size_t i = 0; i < 256; ++i) { + *charData++ = i < pathLen ? overlayPath[i] : '\0'; // write: overlay path + } + data += (2 * 256) / sizeof(uint32_t); + + // write idmap data header uint16_t* typeData = reinterpret_cast<uint16_t*>(data); - *typeData++ = htods(pg->id); - *typeData++ = htods(mapSize); - for (size_t i = 0; i < mapSize; ++i) { - uint8_t targetTypeId = map.keyAt(i); - const IdmapTypeMap& typeMap = map[i]; - *typeData++ = htods(targetTypeId + 1); - *typeData++ = htods(typeMap.overlayTypeId); - *typeData++ = htods(typeMap.entryMap.size()); - *typeData++ = htods(typeMap.entryOffset); - - const size_t entryCount = typeMap.entryMap.size(); - uint32_t* entries = reinterpret_cast<uint32_t*>(typeData); - for (size_t j = 0; j < entryCount; j++) { - entries[j] = htodl(typeMap.entryMap[j]); + *typeData++ = htods(targetPackageStruct->id); // write: target package id + *typeData++ = + htods(static_cast<uint16_t>(matchingResources.typeMappings.size())); // write: type count + + // write idmap data + for (auto ti = matchingResources.typeMappings.cbegin(); ti != typesEnd; ++ti) { + const size_t entryCount = matchingResources.numberOfEntriesIncludingPadding[ti->first]; + auto ei = ti->second.cbegin(); + *typeData++ = htods(Res_GETTYPE(ei->first) + 1); // write: target type id + *typeData++ = htods(Res_GETTYPE(ei->second) + 1); // write: overlay type id + *typeData++ = htods(entryCount); // write: entry count + *typeData++ = htods(Res_GETENTRY(ei->first)); // write: (target) entry offset + uint32_t *entryData = reinterpret_cast<uint32_t*>(typeData); + for (; ei != ti->second.cend(); ++ei) { + const size_t padding = matchingResources.entryPadding[ei->first]; + for (size_t i = 0; i < padding; ++i) { + *entryData++ = htodl(0xffffffff); // write: padding + } + *entryData++ = htodl(Res_GETENTRY(ei->second)); // write: (overlay) entry } typeData += entryCount * 2; } diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 59abad45edbb..ad33fcfa2429 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -1990,7 +1990,7 @@ public: // Return value: on success: NO_ERROR; caller is responsible for free-ing // outData (using free(3)). On failure, any status_t value other than // NO_ERROR; the caller should not free outData. - status_t createIdmap(const ResTable& overlay, + status_t createIdmap(const ResTable& targetResTable, uint32_t targetCrc, uint32_t overlayCrc, const char* targetPath, const char* overlayPath, void** outData, size_t* outSize) const; diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp index 26d28965d459..10b83a75304d 100644 --- a/libs/androidfw/tests/Idmap_test.cpp +++ b/libs/androidfw/tests/Idmap_test.cpp @@ -40,7 +40,7 @@ class IdmapTest : public ::testing::Test { ASSERT_EQ(NO_ERROR, overlay_table.add(overlay_data_.data(), overlay_data_.size())); char target_name[256] = "com.android.basic"; - ASSERT_EQ(NO_ERROR, target_table_.createIdmap(overlay_table, 0, 0, target_name, target_name, + ASSERT_EQ(NO_ERROR, overlay_table.createIdmap(target_table_, 0, 0, target_name, target_name, &data_, &data_size_)); } diff --git a/location/java/android/location/ILocationListener.aidl b/location/java/android/location/ILocationListener.aidl index 7627cf6920c1..180183e77668 100644 --- a/location/java/android/location/ILocationListener.aidl +++ b/location/java/android/location/ILocationListener.aidl @@ -26,7 +26,9 @@ import android.os.Bundle; oneway interface ILocationListener { void onLocationChanged(in Location location); - void onStatusChanged(String provider, int status, in Bundle extras); void onProviderEnabled(String provider); void onProviderDisabled(String provider); + + // --- deprecated --- + void onStatusChanged(String provider, int status, in Bundle extras); } diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index b5d835a90c7b..ff2fad443bcb 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -99,9 +99,10 @@ interface ILocationManager void clearTestProviderLocation(String provider, String opPackageName); void setTestProviderEnabled(String provider, boolean enabled, String opPackageName); void clearTestProviderEnabled(String provider, String opPackageName); + + // --- deprecated --- void setTestProviderStatus(String provider, int status, in Bundle extras, long updateTime, String opPackageName); - void clearTestProviderStatus(String provider, String opPackageName); boolean sendExtraCommand(String provider, String command, inout Bundle extras); diff --git a/location/java/android/location/LocationListener.java b/location/java/android/location/LocationListener.java index 88904c824912..aa9dddc03515 100644 --- a/location/java/android/location/LocationListener.java +++ b/location/java/android/location/LocationListener.java @@ -44,29 +44,12 @@ public interface LocationListener { void onLocationChanged(Location location); /** - * Called when the provider status changes. This method is called when - * a provider is unable to fetch a location or if the provider has recently - * become available after a period of unavailability. + * This callback will never be invoked and providers can be considers as always in the + * {@link LocationProvider#AVAILABLE} state. * - * @param provider the name of the location provider associated with this - * update. - * @param status {@link LocationProvider#OUT_OF_SERVICE} if the - * provider is out of service, and this is not expected to change in the - * near future; {@link LocationProvider#TEMPORARILY_UNAVAILABLE} if - * the provider is temporarily unavailable but is expected to be available - * shortly; and {@link LocationProvider#AVAILABLE} if the - * provider is currently available. - * @param extras an optional Bundle which will contain provider specific - * status variables. - * - * <p> A number of common key/value pairs for the extras Bundle are listed - * below. Providers that use any of the keys on this list must - * provide the corresponding value as described below. - * - * <ul> - * <li> satellites - the number of satellites used to derive the fix - * </ul> + * @deprecated This callback will never be invoked. */ + @Deprecated void onStatusChanged(String provider, int status, Bundle extras); /** diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 02680ab86062..b66ceef29ceb 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -146,9 +146,14 @@ public class LocationManager { public static final String KEY_PROXIMITY_ENTERING = "entering"; /** + * This key is no longer in use. + * * Key used for a Bundle extra holding an Integer status value * when a status change is broadcast using a PendingIntent. + * + * @deprecated Status changes are deprecated and no longer broadcast. */ + @Deprecated public static final String KEY_STATUS_CHANGED = "status"; /** @@ -1581,8 +1586,7 @@ public class LocationManager { } /** - * Sets mock status values for the given provider. These values will be used in place - * of any actual values from the provider. + * This method has no effect as provider status has been deprecated and is no longer supported. * * @param provider the provider name * @param status the mock status @@ -1593,7 +1597,10 @@ public class LocationManager { * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED * allowed} for your app. * @throws IllegalArgumentException if no provider with the given name exists + * + * @deprecated This method has no effect. */ + @Deprecated public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) { try { mService.setTestProviderStatus(provider, status, extras, updateTime, @@ -1604,21 +1611,19 @@ public class LocationManager { } /** - * Removes any mock status values associated with the given provider. + * This method has no effect as provider status has been deprecated and is no longer supported. * * @param provider the provider name - * * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED * allowed} for your app. * @throws IllegalArgumentException if no provider with the given name exists + * + * @deprecated This method has no effect. */ + @Deprecated public void clearTestProviderStatus(String provider) { - try { - mService.clearTestProviderStatus(provider, mContext.getOpPackageName()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + setTestProviderStatus(provider, LocationProvider.AVAILABLE, null, 0L); } // --- GPS-specific support --- diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java index c4fd0975a3bc..b69a9d79e5b8 100644 --- a/location/java/android/location/LocationProvider.java +++ b/location/java/android/location/LocationProvider.java @@ -34,8 +34,23 @@ import com.android.internal.location.ProviderProperties; * user-specified criteria. */ public class LocationProvider { + + /** + * @deprecated Location provider statuses are no longer supported. + */ + @Deprecated public static final int OUT_OF_SERVICE = 0; + + /** + * @deprecated Location provider statuses are no longer supported. + */ + @Deprecated public static final int TEMPORARILY_UNAVAILABLE = 1; + + /** + * @deprecated Location provider statuses are no longer supported. + */ + @Deprecated public static final int AVAILABLE = 2; /** diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt index 1e69f162fb07..d19559e8cccd 100644 --- a/location/lib/api/current.txt +++ b/location/lib/api/current.txt @@ -11,8 +11,8 @@ package com.android.location.provider { method public abstract void onDisable(); method public void onDump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); method public abstract void onEnable(); - method public abstract int onGetStatus(android.os.Bundle); - method public abstract long onGetStatusUpdateTime(); + method public deprecated int onGetStatus(android.os.Bundle); + method public deprecated long onGetStatusUpdateTime(); method public boolean onSendExtraCommand(java.lang.String, android.os.Bundle); method public abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource); method public final void reportLocation(android.location.Location); diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java index 30655f5bbf10..d45a4bac8f96 100644 --- a/location/lib/java/com/android/location/provider/LocationProviderBase.java +++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java @@ -16,14 +16,11 @@ package com.android.location.provider; -import java.io.FileDescriptor; -import java.io.FileOutputStream; -import java.io.PrintWriter; - import android.content.Context; import android.location.ILocationManager; import android.location.Location; import android.location.LocationManager; +import android.location.LocationProvider; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -36,6 +33,10 @@ import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; import com.android.internal.util.FastPrintWriter; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.PrintWriter; + /** * Base class for location providers implemented as unbundled services. * @@ -173,6 +174,8 @@ public abstract class LocationProviderBase { } /** + * This method will no longer be invoked. + * * Returns a information on the status of this provider. * <p>{@link android.location.LocationProvider#OUT_OF_SERVICE} is returned if the provider is * out of service, and this is not expected to change in the near @@ -183,10 +186,17 @@ public abstract class LocationProviderBase { * * <p>If extras is non-null, additional status information may be * added to it in the form of provider-specific key/value pairs. + * + * @deprecated This method will no longer be invoked. */ - public abstract int onGetStatus(Bundle extras); + @Deprecated + public int onGetStatus(Bundle extras) { + return LocationProvider.AVAILABLE; + } /** + * This method will no longer be invoked. + * * Returns the time at which the status was last updated. It is the * responsibility of the provider to appropriately set this value using * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}. @@ -195,8 +205,13 @@ public abstract class LocationProviderBase { * the same status again. * * @return time of last status update in millis since last reboot + * + * @deprecated This method will no longer be invoked. */ - public abstract long onGetStatusUpdateTime(); + @Deprecated + public long onGetStatusUpdateTime() { + return 0; + } /** * Implements addditional location provider specific additional commands. diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 976d3803096f..ff1bdd47f565 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1911,7 +1911,7 @@ public class AudioManager { * system failed to generate a new session, a condition in which audio playback or recording * will subsequently fail as well. */ - public static int generateAudioSessionId() { + public int generateAudioSessionId() { int session = AudioSystem.newAudioSessionId(); if (session > 0) { return session; diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 995ebb2897c5..dfe29e953a56 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -1135,6 +1135,10 @@ public final class MediaCodecInfo { maxChannels = 6; } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3)) { maxChannels = 16; + } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AC4)) { + sampleRates = new int[] { 44100, 48000, 96000, 192000 }; + bitRates = Range.create(16000, 2688000); + maxChannels = 24; } else { Log.w(TAG, "Unsupported mime " + mime); mParent.mError |= ERROR_UNSUPPORTED; diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java index c203fa9ca71b..7785900a7c6e 100644 --- a/media/java/android/media/MediaExtractor.java +++ b/media/java/android/media/MediaExtractor.java @@ -434,9 +434,12 @@ final public class MediaExtractor { */ @NonNull public List<AudioPresentation> getAudioPresentations(int trackIndex) { - return new ArrayList<AudioPresentation>(); + return native_getAudioPresentations(trackIndex); } + @NonNull + private native List<AudioPresentation> native_getAudioPresentations(int trackIndex); + /** * Get the PSSH info if present. * @return a map of uuid-to-bytes, with the uuid specifying diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index 5dee16e03542..284e422374aa 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -138,6 +138,7 @@ public final class MediaFormat { public static final String MIMETYPE_AUDIO_MSGSM = "audio/gsm"; public static final String MIMETYPE_AUDIO_AC3 = "audio/ac3"; public static final String MIMETYPE_AUDIO_EAC3 = "audio/eac3"; + public static final String MIMETYPE_AUDIO_AC4 = "audio/ac4"; public static final String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled"; /** diff --git a/media/jni/android_media_AudioPresentation.h b/media/jni/android_media_AudioPresentation.h index 5306de6f8580..a3adddd21fd6 100644 --- a/media/jni/android_media_AudioPresentation.h +++ b/media/jni/android_media_AudioPresentation.h @@ -14,173 +14,135 @@ * limitations under the License. */ -#ifndef _ANDROID_MEDIA_AUDIO_PRESENTATION_H_ -#define _ANDROID_MEDIA_AUDIO_PRESENTATION_H_ +#ifndef _ANDROID_MEDIA_AUDIOPRESENTATION_H_ +#define _ANDROID_MEDIA_AUDIOPRESENTATION_H_ #include "jni.h" -#include <media/AudioPresentationInfo.h> -#include <media/stagefright/foundation/ADebug.h> -#include <media/stagefright/foundation/AMessage.h> - +#include <media/stagefright/foundation/ADebug.h> // CHECK +#include <media/stagefright/foundation/AudioPresentationInfo.h> #include <nativehelper/ScopedLocalRef.h> namespace android { struct JAudioPresentationInfo { struct fields_t { - jclass clazz; + jclass clazz = NULL; jmethodID constructID; // list parameters - jclass listclazz; + jclass listClazz = NULL; jmethodID listConstructId; jmethodID listAddId; + // hashmap parameters + jclass hashMapClazz = NULL; + jmethodID hashMapConstructID; + jmethodID hashMapPutID; + + // ulocale parameters + jclass ulocaleClazz = NULL; + jmethodID ulocaleConstructID; + void init(JNIEnv *env) { jclass lclazz = env->FindClass("android/media/AudioPresentation"); - if (lclazz == NULL) { - return; - } - + CHECK(lclazz != NULL); clazz = (jclass)env->NewGlobalRef(lclazz); - if (clazz == NULL) { - return; - } - + CHECK(clazz != NULL); constructID = env->GetMethodID(clazz, "<init>", - "(IILandroid/icu/util/ULocale;IZZZLjava/util/Map;)V"); - env->DeleteLocalRef(lclazz); + "(IILandroid/icu/util/ULocale;IZZZLjava/util/Map;)V"); + CHECK(constructID != NULL); // list objects - jclass llistclazz = env->FindClass("java/util/ArrayList"); - CHECK(llistclazz != NULL); - listclazz = static_cast<jclass>(env->NewGlobalRef(llistclazz)); - CHECK(listclazz != NULL); - listConstructId = env->GetMethodID(listclazz, "<init>", "()V"); + jclass llistClazz = env->FindClass("java/util/ArrayList"); + CHECK(llistClazz != NULL); + listClazz = static_cast<jclass>(env->NewGlobalRef(llistClazz)); + CHECK(listClazz != NULL); + listConstructId = env->GetMethodID(listClazz, "<init>", "()V"); CHECK(listConstructId != NULL); - listAddId = env->GetMethodID(listclazz, "add", "(Ljava/lang/Object;)Z"); + listAddId = env->GetMethodID(listClazz, "add", "(Ljava/lang/Object;)Z"); CHECK(listAddId != NULL); - env->DeleteLocalRef(llistclazz); - } - void exit(JNIEnv *env) { - env->DeleteGlobalRef(clazz); - clazz = NULL; - env->DeleteGlobalRef(listclazz); - listclazz = NULL; - } - }; - - static status_t ConvertMessageToMap(JNIEnv *env, const sp<AMessage> &msg, jobject *map) { - ScopedLocalRef<jclass> hashMapClazz(env, env->FindClass("java/util/HashMap")); - - if (hashMapClazz.get() == NULL) { - return -EINVAL; - } - jmethodID hashMapConstructID = - env->GetMethodID(hashMapClazz.get(), "<init>", "()V"); - - if (hashMapConstructID == NULL) { - return -EINVAL; - } - jmethodID hashMapPutID = - env->GetMethodID( - hashMapClazz.get(), + // hashmap objects + jclass lhashMapClazz = env->FindClass("java/util/HashMap"); + CHECK(lhashMapClazz != NULL); + hashMapClazz = (jclass)env->NewGlobalRef(lhashMapClazz); + CHECK(hashMapClazz != NULL); + hashMapConstructID = env->GetMethodID(hashMapClazz, "<init>", "()V"); + CHECK(hashMapConstructID != NULL); + hashMapPutID = env->GetMethodID( + hashMapClazz, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); - - if (hashMapPutID == NULL) { - return -EINVAL; + CHECK(hashMapPutID != NULL); + + jclass lulocaleClazz = env->FindClass("android/icu/util/ULocale"); + CHECK(lulocaleClazz != NULL); + ulocaleClazz = (jclass)env->NewGlobalRef(lulocaleClazz); + CHECK(ulocaleClazz != NULL); + ulocaleConstructID = env->GetMethodID(ulocaleClazz, "<init>", "(Ljava/lang/String;)V"); + CHECK(ulocaleConstructID != NULL); } - jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID); + void exit(JNIEnv *env) { + env->DeleteGlobalRef(clazz); clazz = NULL; + env->DeleteGlobalRef(listClazz); listClazz = NULL; + env->DeleteGlobalRef(hashMapClazz); hashMapClazz = NULL; + env->DeleteGlobalRef(ulocaleClazz); ulocaleClazz = NULL; + } + }; - for (size_t i = 0; i < msg->countEntries(); ++i) { - AMessage::Type valueType; - const char *key = msg->getEntryNameAt(i, &valueType); + static jobject asJobject(JNIEnv *env, const fields_t& fields) { + return env->NewObject(fields.listClazz, fields.listConstructId); + } - if (!strncmp(key, "android._", 9)) { - // don't expose private keys (starting with android._) - continue; - } - jobject valueObj = NULL; - AString val; - CHECK(msg->findString(key, &val)); - valueObj = env->NewStringUTF(val.c_str()); - if (valueObj != NULL) { - ScopedLocalRef<jclass> localeClazz(env, env->FindClass("android/icu/util/ULocale")); - if (localeClazz.get() == NULL) { - return -EINVAL; - } - jmethodID localeConstructID = - env->GetMethodID(localeClazz.get(), "<init>", "(Ljava/lang/String;)V"); - if (localeConstructID == NULL) { - return -EINVAL; - } - jstring jLanguage = env->NewStringUTF(key); - jobject jLocale = env->NewObject(localeClazz.get(), localeConstructID, jLanguage); - env->CallObjectMethod(hashMap, hashMapPutID, jLocale, valueObj); - env->DeleteLocalRef(jLocale); jLocale = NULL; - env->DeleteLocalRef(valueObj); valueObj = NULL; - env->DeleteLocalRef(jLanguage); jLanguage = NULL; + static void addPresentations(JNIEnv *env, const fields_t& fields, + const AudioPresentationCollection& presentations, jobject presentationsJObj) { + for (const auto& ap : presentations) { + ScopedLocalRef<jobject> jLabelObject = convertLabelsToMap(env, fields, ap.mLabels); + if (jLabelObject == nullptr) return; + ScopedLocalRef<jstring> jLanguage(env, env->NewStringUTF(ap.mLanguage.c_str())); + if (jLanguage == nullptr) return; + ScopedLocalRef<jobject> jLocale(env, env->NewObject( + fields.ulocaleClazz, fields.ulocaleConstructID, jLanguage.get())); + ScopedLocalRef<jobject> jValueObj(env, env->NewObject(fields.clazz, fields.constructID, + static_cast<jint>(ap.mPresentationId), + static_cast<jint>(ap.mProgramId), + jLocale.get(), + static_cast<jint>(ap.mMasteringIndication), + static_cast<jboolean>((ap.mAudioDescriptionAvailable == 1) ? 1 : 0), + static_cast<jboolean>((ap.mSpokenSubtitlesAvailable == 1) ? 1 : 0), + static_cast<jboolean>((ap.mDialogueEnhancementAvailable == 1) ? 1 : 0), + jLabelObject.get())); + if (jValueObj != nullptr) { + env->CallBooleanMethod(presentationsJObj, fields.listAddId, jValueObj.get()); } } - - *map = hashMap; - - return OK; } - jobject asJobject(JNIEnv *env, const fields_t& fields, const AudioPresentationInfo &info) { - jobject list = env->NewObject(fields.listclazz, fields.listConstructId); - - for (size_t i = 0; i < info.countPresentations(); ++i) { - const sp<AudioPresentation> &ap = info.getPresentation(i); - jobject jLabelObject; - - sp<AMessage> labelMessage = new AMessage(); - for (size_t i = 0; i < ap->mLabels.size(); ++i) { - labelMessage->setString(ap->mLabels.keyAt(i).string(), - ap->mLabels.valueAt(i).string()); - } - if (ConvertMessageToMap(env, labelMessage, &jLabelObject) != OK) { - return NULL; - } - ScopedLocalRef<jclass> localeClazz(env, env->FindClass("android/icu/util/ULocale")); - if (localeClazz.get() == NULL) { - return NULL; - } - jmethodID localeConstructID = - env->GetMethodID(localeClazz.get(), "<init>", "(Ljava/lang/String;)V"); - if (localeConstructID == NULL) { - return NULL; - } - jstring jLanguage = env->NewStringUTF(ap->mLanguage.c_str()); - jobject jLocale = env->NewObject(localeClazz.get(), localeConstructID, jLanguage); - jobject jValueObj = env->NewObject(fields.clazz, fields.constructID, - static_cast<jint>(ap->mPresentationId), - static_cast<jint>(ap->mProgramId), - jLocale, - static_cast<jint>(ap->mMasteringIndication), - static_cast<jboolean>((ap->mAudioDescriptionAvailable == 1) ? - 1 : 0), - static_cast<jboolean>((ap->mSpokenSubtitlesAvailable == 1) ? - 1 : 0), - static_cast<jboolean>((ap->mDialogueEnhancementAvailable == 1) ? - 1 : 0), - jLabelObject); - if (jValueObj == NULL) { - env->DeleteLocalRef(jLanguage); jLanguage = NULL; - return NULL; - } + private: + static ScopedLocalRef<jobject> convertLabelsToMap( + JNIEnv *env, const fields_t& fields, const std::map<std::string, std::string> &labels) { + ScopedLocalRef<jobject> nullMap(env, nullptr); + ScopedLocalRef<jobject> hashMap(env, env->NewObject( + fields.hashMapClazz, fields.hashMapConstructID)); + if (hashMap == nullptr) { + return nullMap; + } - env->CallBooleanMethod(list, fields.listAddId, jValueObj); - env->DeleteLocalRef(jLocale); jLocale = NULL; - env->DeleteLocalRef(jValueObj); jValueObj = NULL; - env->DeleteLocalRef(jLanguage); jLanguage = NULL; + for (const auto& label : labels) { + ScopedLocalRef<jstring> jLanguage(env, env->NewStringUTF(label.first.c_str())); + if (jLanguage == nullptr) return nullMap; + ScopedLocalRef<jobject> jLocale(env, env->NewObject( + fields.ulocaleClazz, + fields.ulocaleConstructID, + jLanguage.get())); + if (jLocale == nullptr) return nullMap; + ScopedLocalRef<jobject> jValue(env, env->NewStringUTF(label.second.c_str())); + if (jValue == nullptr) return nullMap; + env->CallObjectMethod(hashMap.get(), fields.hashMapPutID, jLocale.get(), jValue.get()); } - return list; + return hashMap; } }; } // namespace android diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp index 15957c6a9c5e..29238d3b8ea4 100644 --- a/media/jni/android_media_MediaExtractor.cpp +++ b/media/jni/android_media_MediaExtractor.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "MediaExtractor-JNI" #include <utils/Log.h> +#include "android_media_AudioPresentation.h" #include "android_media_MediaDataSource.h" #include "android_media_MediaExtractor.h" #include "android_media_MediaMetricsJNI.h" @@ -56,6 +57,7 @@ struct fields_t { }; static fields_t gFields; +static JAudioPresentationInfo::fields_t gAudioPresentationFields; JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz) : mClass(NULL), @@ -289,6 +291,10 @@ bool JMediaExtractor::getCachedDuration(int64_t *durationUs, bool *eos) const { return mImpl->getCachedDuration(durationUs, eos); } +status_t JMediaExtractor::getAudioPresentations(size_t trackIdx, + AudioPresentationCollection *presentations) const { + return mImpl->getAudioPresentations(trackIdx, presentations); +} } // namespace android //////////////////////////////////////////////////////////////////////////////// @@ -668,6 +674,28 @@ static jboolean android_media_MediaExtractor_getSampleCryptoInfo( return JNI_TRUE; } +static jobject android_media_MediaExtractor_getAudioPresentations( + JNIEnv *env, jobject thiz, jint trackIdx) { + sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); + jobject presentationsJObj = JAudioPresentationInfo::asJobject(env, gAudioPresentationFields); + if (extractor == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return presentationsJObj; + } + AudioPresentationCollection presentations; + status_t err = extractor->getAudioPresentations(trackIdx, &presentations); + if (err == ERROR_END_OF_STREAM || err == ERROR_UNSUPPORTED) { + return presentationsJObj; + } else if (err != OK) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return presentationsJObj; + } + + JAudioPresentationInfo::addPresentations( + env, gAudioPresentationFields, presentations, presentationsJObj); + return presentationsJObj; +} + static void android_media_MediaExtractor_native_init(JNIEnv *env) { jclass clazz = env->FindClass("android/media/MediaExtractor"); CHECK(clazz != NULL); @@ -683,6 +711,8 @@ static void android_media_MediaExtractor_native_init(JNIEnv *env) { gFields.cryptoInfoSetPatternID = env->GetMethodID(clazz, "setPattern", "(II)V"); + + gAudioPresentationFields.init(env); } static void android_media_MediaExtractor_native_setup( @@ -963,6 +993,9 @@ static const JNINativeMethod gMethods[] = { {"native_getMetrics", "()Landroid/os/PersistableBundle;", (void *)android_media_MediaExtractor_native_getMetrics}, + + { "native_getAudioPresentations", "(I)Ljava/util/List;", + (void *)android_media_MediaExtractor_getAudioPresentations }, }; int register_android_media_MediaExtractor(JNIEnv *env) { diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h index aaa8421df48e..baa779c24b09 100644 --- a/media/jni/android_media_MediaExtractor.h +++ b/media/jni/android_media_MediaExtractor.h @@ -18,6 +18,7 @@ #define _ANDROID_MEDIA_MEDIAEXTRACTOR_H_ #include <media/stagefright/foundation/ABase.h> +#include <media/stagefright/foundation/AudioPresentationInfo.h> #include <media/MediaSource.h> #include <media/DataSource.h> #include <utils/Errors.h> @@ -66,6 +67,8 @@ struct JMediaExtractor : public RefBase { status_t getMetrics(Parcel *reply) const; bool getCachedDuration(int64_t *durationUs, bool *eos) const; + status_t getAudioPresentations(size_t trackIdx, + AudioPresentationCollection *presentations) const; protected: virtual ~JMediaExtractor(); diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp index 8f13497b2673..f244f9f88684 100644 --- a/packages/CarSystemUI/Android.bp +++ b/packages/CarSystemUI/Android.bp @@ -45,6 +45,7 @@ android_app { "androidx.slice_slice-builders", "androidx.arch.core_core-runtime", "androidx.lifecycle_lifecycle-extensions", + "car-theme-lib-bp", "SystemUI-tags", "SystemUI-proto", ], diff --git a/packages/CarSystemUI/res/layout/car_volume_dialog.xml b/packages/CarSystemUI/res/layout/car_volume_dialog.xml index c98740e42701..709797d41060 100644 --- a/packages/CarSystemUI/res/layout/car_volume_dialog.xml +++ b/packages/CarSystemUI/res/layout/car_volume_dialog.xml @@ -20,11 +20,9 @@ android:id="@+id/volume_list" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@android:color/black" android:minWidth="@dimen/volume_dialog_panel_width" - android:theme="@style/Theme.Car.DialogListView" - app:dividerEndMargin="@dimen/car_keyline_1" - app:dividerStartMargin="@dimen/car_keyline_1" + android:theme="@style/PagedListViewTheme" app:gutter="none" app:scrollBarEnabled="false" + app:listDividerColor="@color/list_divider_color" app:showPagedListViewDivider="true"/> diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml index df8f8dba06b9..c510ab6d1046 100644 --- a/packages/CarSystemUI/res/values/colors.xml +++ b/packages/CarSystemUI/res/values/colors.xml @@ -51,4 +51,6 @@ <color name="car_grey_900">#ff212121</color> <color name="keyguard_button_text_color">@android:color/black</color> + + <color name="list_divider_color">@*android:color/car_list_divider_light</color> </resources> diff --git a/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml index 7f4544a38479..0d95d308f48b 100644 --- a/packages/CarSystemUI/res/values/styles.xml +++ b/packages/CarSystemUI/res/values/styles.xml @@ -41,15 +41,16 @@ <item name="android:colorControlHighlight">@color/nav_bar_ripple_background_color</item> </style> - <style name="Theme.Car.DialogListView" parent="@style/Theme.Car.NoActionBar"> - <item name="android:colorControlActivated">@color/car_accent</item> - <item name="listItemBackgroundColor">@android:color/black</item> - </style> - <style name="NavigationBarButton"> <item name="android:layout_height">96dp</item> <item name="android:layout_width">96dp</item> <item name="android:background">@drawable/nav_button_background</item> </style> -</resources> + <style name="PagedListViewTheme" parent="@style/Theme.CarSupportWrapper.NoActionBar"> + <item name="android:background">@*android:color/car_background</item> + <item name="listItemBackgroundColor">@*android:color/car_background</item> + <item name="dividerEndMargin">@dimen/car_keyline_1</item> + <item name="dividerStartMargin">@dimen/car_keyline_1</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java index 56feb4726dd6..87d6e4a137ac 100644 --- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java +++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java @@ -16,27 +16,24 @@ package com.android.location.fused; - -import java.io.FileDescriptor; -import java.io.PrintWriter; - -import com.android.location.provider.LocationProviderBase; -import com.android.location.provider.ProviderPropertiesUnbundled; -import com.android.location.provider.ProviderRequestUnbundled; - import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.location.Criteria; -import android.location.LocationProvider; -import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.UserHandle; import android.os.WorkSource; +import com.android.location.provider.LocationProviderBase; +import com.android.location.provider.ProviderPropertiesUnbundled; +import com.android.location.provider.ProviderRequestUnbundled; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + public class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback { private static final String TAG = "FusedLocationProvider"; @@ -48,7 +45,6 @@ public class FusedLocationProvider extends LocationProviderBase implements Fusio private static final int MSG_DISABLE = 2; private static final int MSG_SET_REQUEST = 3; - private final Context mContext; private final FusionEngine mEngine; private static class RequestWrapper { @@ -62,13 +58,12 @@ public class FusedLocationProvider extends LocationProviderBase implements Fusio public FusedLocationProvider(Context context) { super(TAG, PROPERTIES); - mContext = context; mEngine = new FusionEngine(context, Looper.myLooper()); // listen for user change IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_USER_SWITCHED); - mContext.registerReceiverAsUser(new BroadcastReceiver() { + context.registerReceiverAsUser(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); @@ -122,14 +117,4 @@ public class FusedLocationProvider extends LocationProviderBase implements Fusio // perform synchronously mEngine.dump(fd, pw, args); } - - @Override - public int onGetStatus(Bundle extras) { - return LocationProvider.AVAILABLE; - } - - @Override - public long onGetStatusUpdateTime() { - return 0; - } } diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index d1f140f4deec..444e72459510 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -16,6 +16,7 @@ android_library { "SettingsLibAppPreference", "SettingsLibSearchWidget", "SettingsLibSettingsSpinner", + "SettingsLayoutPreference", ], // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES diff --git a/packages/SettingsLib/SettingsLayoutPreference/Android.bp b/packages/SettingsLib/SettingsLayoutPreference/Android.bp new file mode 100644 index 000000000000..489d3606ae15 --- /dev/null +++ b/packages/SettingsLib/SettingsLayoutPreference/Android.bp @@ -0,0 +1,13 @@ +android_library { + name: "SettingsLayoutPreference", + + srcs: ["src/**/*.java"], + resource_dirs: ["res"], + + static_libs: [ + "androidx.preference_preference", + ], + + sdk_version: "system_current", + min_sdk_version: "21", +} diff --git a/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml b/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml new file mode 100644 index 000000000000..4b9f1ab8d6cc --- /dev/null +++ b/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.settingslib.widget"> + + <uses-sdk android:minSdkVersion="21" /> + +</manifest> diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml new file mode 100644 index 000000000000..ee4ce499396f --- /dev/null +++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. + --> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml new file mode 100644 index 000000000000..06782633a4de --- /dev/null +++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. + --> + +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/entity_header" + style="@style/EntityHeader" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <LinearLayout + android:id="@+id/entity_header_content" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:gravity="center_horizontal" + android:orientation="vertical"> + + <ImageView + android:id="@+id/entity_header_icon" + android:layout_width="48dp" + android:layout_height="48dp" + android:scaleType="fitXY" + android:antialias="true"/> + + <TextView + android:id="@+id/entity_header_title" + style="@style/TextAppearance.EntityHeaderTitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="false" + android:ellipsize="marquee" + android:textDirection="locale" + android:layout_marginTop="8dp"/> + + <TextView + android:id="@+id/install_type" + style="@style/TextAppearance.EntityHeaderSummary" + android:visibility="gone" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="2dp"/> + + <TextView + android:id="@+id/entity_header_summary" + style="@style/TextAppearance.EntityHeaderSummary" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="2dp"/> + + <TextView + android:id="@+id/entity_header_second_summary" + style="@style/TextAppearance.EntityHeaderSummary" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + </LinearLayout> + + <LinearLayout + android:id="@+id/entity_header_links" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_centerVertical="true" + android:layout_alignParentEnd="true" + android:orientation="vertical"> + + <ImageButton + android:id="@android:id/button1" + style="?android:attr/actionOverflowButtonStyle" + android:layout_width="wrap_content" + android:layout_weight="1" + android:layout_height="0dp" + android:minWidth="24dp" + android:src="@null" + android:tint="?android:attr/colorAccent"/> + + <ImageButton + android:id="@android:id/button2" + style="?android:attr/actionOverflowButtonStyle" + android:layout_width="wrap_content" + android:layout_weight="1" + android:layout_height="0dp" + android:minWidth="24dp" + android:src="@null" + android:tint="?android:attr/colorAccent"/> + + </LinearLayout> + +</RelativeLayout> diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml b/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml new file mode 100644 index 000000000000..805744baef10 --- /dev/null +++ b/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. + --> + +<resources> + <style name="EntityHeader"> + <item name="android:background">?android:attr/colorPrimaryDark</item> + <item name="android:paddingTop">24dp</item> + <item name="android:paddingBottom">16dp</item> + <item name="android:paddingEnd">16dp</item> + </style> + + <style name="TextAppearance.EntityHeaderTitle" + parent="@android:style/TextAppearance.Material.Subhead"> + <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> + <item name="android:textSize">20sp</item> + </style> + + <style name="TextAppearance.EntityHeaderSummary" + parent="@android:style/TextAppearance.Material.Body1"> + <item name="android:textAlignment">viewStart</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> + <item name="android:gravity">start</item> + <item name="android:singleLine">true</item> + <item name="android:ellipsize">marquee</item> + <item name="android:textSize">14sp</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java b/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java new file mode 100644 index 000000000000..2a635b0996e6 --- /dev/null +++ b/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java @@ -0,0 +1,174 @@ +/* + * 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.settingslib.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import androidx.core.content.res.TypedArrayUtils; +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + +/** + * A preference can be simply customized a view by adding layout attribute in xml. + * User also can decide whether or not LayoutPreference allows above divider or below divider. + * + * For instances, + * + * <com.android.settingslib.widget.LayoutPreference + * ... + * android:layout="@layout/settings_entity_header" + * xxxxxxx:allowDividerAbove="true" + * xxxxxxx:allowDividerBelow="true" + * + */ +public class LayoutPreference extends Preference { + + private final View.OnClickListener mClickListener = v -> performClick(v); + private boolean mAllowDividerAbove; + private boolean mAllowDividerBelow; + private View mRootView; + + /** + * Constructs a new LayoutPreference with the given context's theme and the supplied + * attribute set. + * + * @param context The Context the view is running in, through which it can + * access the current theme, resources, etc. + * @param attrs The attributes of the XML tag that is inflating the view. + */ + public LayoutPreference(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs, 0 /* defStyleAttr */); + } + + /** + * Constructs a new LayoutPreference with the given context's theme, the supplied + * attribute set, and default style attribute. + * + * @param context The Context the view is running in, through which it can + * access the current theme, resources, etc. + * @param attrs The attributes of the XML tag that is inflating the view. + * @param defStyleAttr An attribute in the current theme that contains a + * reference to a style resource that supplies default + * values for the view. Can be 0 to not look for + * defaults. + */ + public LayoutPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs, defStyleAttr); + } + + /** + * Constructs a new LayoutPreference with the given context's theme and a customized view id. + * + * @param context The Context the view is running in, through which it can + * access the current theme, resources, etc. + * @param resource The view id which you expected to be inflated and show in preference. + */ + public LayoutPreference(Context context, int resource) { + this(context, LayoutInflater.from(context).inflate(resource, null, false)); + } + + /** + * Constructs a new LayoutPreference with the given context's theme and a customized view. + * + * @param context The Context the view is running in, through which it can + * access the current theme, resources, etc. + * @param view The view which you expected show in preference. + */ + public LayoutPreference(Context context, View view) { + super(context); + setView(view); + } + + private void init(Context context, AttributeSet attrs, int defStyleAttr) { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Preference); + mAllowDividerAbove = TypedArrayUtils.getBoolean(a, R.styleable.Preference_allowDividerAbove, + R.styleable.Preference_allowDividerAbove, false); + mAllowDividerBelow = TypedArrayUtils.getBoolean(a, R.styleable.Preference_allowDividerBelow, + R.styleable.Preference_allowDividerBelow, false); + a.recycle(); + + a = context.obtainStyledAttributes( + attrs, R.styleable.Preference, defStyleAttr, 0); + int layoutResource = a.getResourceId(R.styleable.Preference_android_layout, 0); + if (layoutResource == 0) { + throw new IllegalArgumentException("LayoutPreference requires a layout to be defined"); + } + a.recycle(); + + // Need to create view now so that findViewById can be called immediately. + final View view = LayoutInflater.from(getContext()) + .inflate(layoutResource, null, false); + setView(view); + } + + private void setView(View view) { + setLayoutResource(R.layout.layout_preference_frame); + mRootView = view; + setShouldDisableView(false); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + holder.itemView.setOnClickListener(mClickListener); + + final boolean selectable = isSelectable(); + holder.itemView.setFocusable(selectable); + holder.itemView.setClickable(selectable); + holder.setDividerAllowedAbove(mAllowDividerAbove); + holder.setDividerAllowedBelow(mAllowDividerBelow); + + FrameLayout layout = (FrameLayout) holder.itemView; + layout.removeAllViews(); + ViewGroup parent = (ViewGroup) mRootView.getParent(); + if (parent != null) { + parent.removeView(mRootView); + } + layout.addView(mRootView); + } + + /** + * Finds the view with the given ID. + * + * @param id the ID to search for + * @return a view with given ID if found, or {@code null} otherwise + */ + public <T extends View> T findViewById(int id) { + return mRootView.findViewById(id); + } + + /** + * LayoutPreference whether or not allows to set a below divider. + */ + public void setAllowDividerBelow(boolean allowed) { + mAllowDividerBelow = allowed; + } + + /** + * Return a value whether or not LayoutPreference allows to set a below divider. + */ + public boolean isAllowDividerBelow() { + return mAllowDividerBelow; + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 86928fc07cc2..4f81dafe7305 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -154,8 +154,10 @@ public class AccessPoint implements Comparable<AccessPoint> { static final String KEY_CARRIER_NAME = "key_carrier_name"; static final AtomicInteger sLastId = new AtomicInteger(0); - /** - * These values are matched in string arrays -- changes must be kept in sync + /* + * NOTE: These constants for security and PSK types are saved to the bundle in saveWifiState, + * and sent across IPC. The numeric values should remain stable, otherwise the changes will need + * to be synced with other unbundled users of this library. */ public static final int SECURITY_NONE = 0; public static final int SECURITY_WEP = 1; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java index ede248b85083..8757eed8b746 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java @@ -55,6 +55,8 @@ public class SettingsLibRobolectricTestRunner extends RobolectricTestRunner { paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/AppPreference/res")); paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/HelpUtils/res")); paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/RestrictedLockUtils/res")); + paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/" + + "SettingsLayoutPreference/res")); paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/res")); paths.add(resourcePath("file:frameworks/base/core/res/res")); paths.add(resourcePath("file:out/soong/.intermediates/prebuilts/sdk/current/androidx/androidx.appcompat_appcompat-nodeps/android_common/aar/res/")); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java new file mode 100644 index 000000000000..427a611d61da --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java @@ -0,0 +1,91 @@ +/* + * 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.settingslib.widget; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.view.LayoutInflater; + +import androidx.preference.Preference.OnPreferenceClickListener; +import androidx.preference.PreferenceViewHolder; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; + +@RunWith(SettingsLibRobolectricTestRunner.class) +public class LayoutPreferenceTest { + + private LayoutPreference mPreference; + private PreferenceViewHolder mHolder; + + @Before + public void setUp() { + final Context mContext = RuntimeEnvironment.application; + mPreference = new LayoutPreference(mContext, R.layout.settings_entity_header); + mHolder = PreferenceViewHolder.createInstanceForTests(LayoutInflater.from(mContext) + .inflate(R.layout.layout_preference_frame, null, false)); + } + + @Test + public void setOnPreferenceClickListener_layoutPreferenceShouldListenClickEvent() { + final OnPreferenceClickListener listener = mock(OnPreferenceClickListener.class); + + mPreference.setOnPreferenceClickListener(listener); + mPreference.onBindViewHolder(mHolder); + + mHolder.itemView.callOnClick(); + + verify(listener).onPreferenceClick(mPreference); + assertThat(mHolder.itemView.isFocusable()).isTrue(); + assertThat(mHolder.itemView.isClickable()).isTrue(); + } + + @Test + public void setNonSelectable_viewShouldNotBeSelectable() { + mPreference.setSelectable(false); + mPreference.onBindViewHolder(mHolder); + + assertThat(mHolder.itemView.isFocusable()).isFalse(); + assertThat(mHolder.itemView.isClickable()).isFalse(); + } + + @Test + public void disableSomeView_shouldMaintainStateAfterBind() { + mPreference.findViewById(android.R.id.button1).setEnabled(false); + mPreference.findViewById(android.R.id.button2).setEnabled(true); + + mPreference.onBindViewHolder(mHolder); + + assertThat(mPreference.findViewById(android.R.id.button1).isEnabled()).isFalse(); + assertThat(mPreference.findViewById(android.R.id.button2).isEnabled()).isTrue(); + } + + @Test + public void allowDividerBelow_shouldSaveCorrectDividerStatus() { + mPreference.setAllowDividerBelow(true); + + assertThat(mPreference.isAllowDividerBelow()).isTrue(); + } +} diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 2227642f1d28..00ea45c4e024 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -186,6 +186,7 @@ public class SettingsProvider extends ContentProvider { Settings.NameValueTable.VALUE, null); public static final String RESULT_ROWS_DELETED = "result_rows_deleted"; + public static final String RESULT_SETTINGS_LIST = "result_settings_list"; // Overlay specified settings whitelisted for Instant Apps private static final Set<String> OVERLAY_ALLOWED_GLOBAL_INSTANT_APP_SETTINGS = new ArraySet<>(); @@ -483,6 +484,27 @@ public class SettingsProvider extends ContentProvider { return result; } + case Settings.CALL_METHOD_LIST_SYSTEM: { + Bundle result = new Bundle(); + result.putStringArrayList(RESULT_SETTINGS_LIST, + buildSettingsList(getAllSystemSettings(requestingUserId, null))); + return result; + } + + case Settings.CALL_METHOD_LIST_SECURE: { + Bundle result = new Bundle(); + result.putStringArrayList(RESULT_SETTINGS_LIST, + buildSettingsList(getAllSecureSettings(requestingUserId, null))); + return result; + } + + case Settings.CALL_METHOD_LIST_GLOBAL: { + Bundle result = new Bundle(); + result.putStringArrayList(RESULT_SETTINGS_LIST, + buildSettingsList(getAllGlobalSettings(null))); + return result; + } + default: { Slog.w(LOG_TAG, "call() with invalid method: " + method); } break; @@ -552,6 +574,20 @@ public class SettingsProvider extends ContentProvider { } } + private ArrayList<String> buildSettingsList(Cursor cursor) { + final ArrayList<String> lines = new ArrayList<String>(); + try { + while (cursor != null && cursor.moveToNext()) { + lines.add(cursor.getString(1) + "=" + cursor.getString(2)); + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + return lines; + } + @Override public Uri insert(Uri uri, ContentValues values) { if (DEBUG) { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java index f8445fd2201f..13537c466eed 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java @@ -19,8 +19,6 @@ package com.android.providers.settings; import android.app.ActivityManager; import android.content.IContentProvider; import android.content.pm.PackageManager; -import android.database.Cursor; -import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.Process; @@ -265,9 +263,6 @@ final public class SettingsService extends Binder { } if (mUser < 0) { mUser = UserHandle.USER_SYSTEM; - } else if (mVerb == CommandVerb.LIST) { - perr.println("--user not supported for list."); - return -1; } UserManager userManager = UserManager.get(mProvider.getContext()); if (userManager.getUserInfo(mUser) == null) { @@ -304,27 +299,22 @@ final public class SettingsService extends Binder { return 0; } - private List<String> listForUser(IContentProvider provider, int userHandle, String table) { - final Uri uri = "system".equals(table) ? Settings.System.CONTENT_URI - : "secure".equals(table) ? Settings.Secure.CONTENT_URI - : "global".equals(table) ? Settings.Global.CONTENT_URI - : null; - final ArrayList<String> lines = new ArrayList<String>(); - if (uri == null) { - return lines; + List<String> listForUser(IContentProvider provider, int userHandle, String table) { + final String callListCommand; + if ("system".equals(table)) callListCommand = Settings.CALL_METHOD_LIST_SYSTEM; + else if ("secure".equals(table)) callListCommand = Settings.CALL_METHOD_LIST_SECURE; + else if ("global".equals(table)) callListCommand = Settings.CALL_METHOD_LIST_GLOBAL; + else { + getErrPrintWriter().println("Invalid table; no list performed"); + throw new IllegalArgumentException("Invalid table " + table); } + final ArrayList<String> lines = new ArrayList<String>(); try { - final Cursor cursor = provider.query(resolveCallingPackage(), uri, null, null, - null); - try { - while (cursor != null && cursor.moveToNext()) { - lines.add(cursor.getString(1) + "=" + cursor.getString(2)); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } + Bundle arg = new Bundle(); + arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle); + Bundle result = + provider.call(resolveCallingPackage(), callListCommand, null, arg); + lines.addAll(result.getStringArrayList(SettingsProvider.RESULT_SETTINGS_LIST)); Collections.sort(lines); } catch (RemoteException e) { throw new RuntimeException("Failed in IPC", e); @@ -483,7 +473,7 @@ final public class SettingsService extends Binder { pw.println(" reset [--user <USER_ID> | current] NAMESPACE {PACKAGE_NAME | RESET_MODE}"); pw.println(" Reset the global/secure table for a package with mode."); pw.println(" RESET_MODE is one of {untrusted_defaults, untrusted_clear, trusted_defaults}, case-insensitive"); - pw.println(" list NAMESPACE"); + pw.println(" list [--user <USER_ID> | current] NAMESPACE"); pw.println(" Print all defined keys."); pw.println(" NAMESPACE is one of {system, secure, global}, case-insensitive"); } diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index c86ebe70db5c..eb3f70adc48b 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -51,7 +51,7 @@ android:layout_centerVertical="true" android:layout_toEndOf="@id/pkgicon" /> <TextView - android:id="@+id/pkg_group_divider" + android:id="@+id/pkg_divider" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info" @@ -61,7 +61,7 @@ android:layout_centerVertical="true" android:layout_toEndOf="@id/pkgname" /> <TextView - android:id="@+id/group_name" + android:id="@+id/delegate_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info" @@ -70,7 +70,7 @@ android:ellipsize="end" android:maxLines="1" android:layout_centerVertical="true" - android:layout_toEndOf="@id/pkg_group_divider" /> + android:layout_toEndOf="@id/pkg_divider" /> <!-- 24 dp icon with 16 dp padding all around to mirror notification content margins --> <ImageButton android:id="@+id/info" @@ -101,13 +101,39 @@ android:layout_marginStart="@*android:dimen/notification_content_margin_start" android:layout_marginEnd="@*android:dimen/notification_content_margin_start" android:orientation="vertical"> - <!-- Channel Name --> - <TextView - android:id="@+id/channel_name" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="1" - style="@android:style/TextAppearance.Material.Notification.Title" /> + <RelativeLayout + android:id="@+id/names" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView + android:id="@+id/group_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title" + android:layout_marginStart="2dp" + android:layout_marginEnd="2dp" + android:ellipsize="end" + android:maxLines="1" + android:layout_centerVertical="true" /> + <TextView + android:id="@+id/pkg_group_divider" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title" + android:layout_marginStart="2dp" + android:layout_marginEnd="2dp" + android:text="@*android:string/notification_header_divider_symbol" + android:layout_centerVertical="true" + android:layout_toEndOf="@id/group_name" /> + <!-- Channel Name --> + <TextView + android:id="@+id/channel_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + style="@android:style/TextAppearance.Material.Notification.Title" + android:layout_toEndOf="@id/pkg_group_divider"/> + </RelativeLayout> <!-- Question prompt --> <TextView android:id="@+id/block_prompt" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 4a0bc9b81378..31dd46cd1af0 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1574,6 +1574,9 @@ <!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. --> <string name="notification_unblockable_desc">These notifications can\'t be turned off</string> + <!-- Notification: Control panel: Label for the app that posted this notification, if it's not the package that the notification was posted for --> + <string name="notification_delegate_header">via <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string> + <!-- Notification Inline controls: describes what the app is doing in the background [CHAR_LIMIT=NONE] --> <string name="appops_camera">This app is using the camera.</string> <!-- Notification Inline controls: describes what the app is doing in the background [CHAR_LIMIT=NONE] --> 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 3191d14c5a83..42e60aa7908b 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 @@ -17,6 +17,9 @@ package com.android.systemui.shared.system; import static android.view.Display.DEFAULT_DISPLAY; +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 android.app.WindowConfiguration; import android.graphics.Rect; @@ -26,10 +29,6 @@ import android.util.Log; import android.view.WindowManager; import android.view.WindowManagerGlobal; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; - import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture; import com.android.systemui.shared.recents.view.RecentsTransition; @@ -179,6 +178,7 @@ public class WindowManagerWrapper { */ public int getNavBarPosition() { try { + // TODO: Use WindowManagerService.getNavBarPosition(int displayId) return WindowManagerGlobal.getWindowManagerService().getNavBarPosition(); } catch (RemoteException e) { Log.w(TAG, "Failed to get nav bar position"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index 0818513faf41..450d34d4007e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -108,13 +108,13 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. VisualStabilityManager.Callback, BubbleController.BubbleDismissListener { private static final String TAG = "NotificationEntryMgr"; protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - protected static final boolean ENABLE_HEADS_UP = true; - protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up"; + private static final boolean ENABLE_HEADS_UP = true; + private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up"; - protected final NotificationMessagingUtil mMessagingUtil; + private final NotificationMessagingUtil mMessagingUtil; protected final Context mContext; protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>(); - protected final NotificationClicker mNotificationClicker = new NotificationClicker(); + private final NotificationClicker mNotificationClicker = new NotificationClicker(); private final NotificationGroupManager mGroupManager = Dependency.get(NotificationGroupManager.class); @@ -145,14 +145,15 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. private NotificationPresenter mPresenter; private Callback mCallback; protected PowerManager mPowerManager; - protected NotificationListenerService.RankingMap mLatestRankingMap; + private NotificationListenerService.RankingMap mLatestRankingMap; protected HeadsUpManager mHeadsUpManager; protected NotificationData mNotificationData; - protected ContentObserver mHeadsUpObserver; + private ContentObserver mHeadsUpObserver; protected boolean mUseHeadsUp = false; - protected boolean mDisableNotificationAlerts; + private boolean mDisableNotificationAlerts; protected NotificationListContainer mListContainer; - protected final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders + @VisibleForTesting + final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders = new ArrayList<>(); private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener; private NotificationViewHierarchyManager.StatusBarStateListener mStatusBarStateListener; @@ -485,22 +486,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. updateNotifications(); } - /** - * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService - * about the failure. - * - * WARNING: this will call back into us. Don't hold any locks. - */ - void handleNotificationError(StatusBarNotification n, String message) { - removeNotificationInternal(n.getKey(), null, true /* forceRemove */); - try { - mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(), - n.getInitialPid(), message, n.getUserId()); - } catch (RemoteException ex) { - // The end is nigh. - } - } - private void abortExistingInflation(String key) { if (mPendingNotifications.containsKey(key)) { NotificationData.Entry entry = mPendingNotifications.get(key); @@ -513,13 +498,31 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. } } + /** + * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService + * about the failure. + * + * WARNING: this will call back into us. Don't hold any locks. + */ @Override - public void handleInflationException(StatusBarNotification notification, Exception e) { - handleNotificationError(notification, e.getMessage()); + public void handleInflationException(StatusBarNotification n, Exception e) { + removeNotificationInternal(n.getKey(), null, true /* forceRemove */); + try { + mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(), + n.getInitialPid(), e.getMessage(), n.getUserId()); + } catch (RemoteException ex) { + // The end is nigh. + } } private void addEntry(NotificationData.Entry shadeEntry) { - addNotificationViews(shadeEntry); + if (shadeEntry == null) { + return; + } + // Add the expanded view and icon. + mNotificationData.add(shadeEntry); + tagForeground(shadeEntry.notification); + updateNotifications(); mCallback.onNotificationAdded(shadeEntry); } @@ -755,17 +758,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. row.inflateViews(); } - protected void addNotificationViews(NotificationData.Entry entry) { - if (entry == null) { - return; - } - // Add the expanded view and icon. - mNotificationData.add(entry); - tagForeground(entry.notification); - updateNotifications(); - } - - protected NotificationData.Entry createNotificationViews( + private NotificationData.Entry createNotificationViews( StatusBarNotification sbn, NotificationListenerService.Ranking ranking) throws InflationException { if (DEBUG) { @@ -841,7 +834,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. } @VisibleForTesting - protected void tagForeground(StatusBarNotification notification) { + void tagForeground(StatusBarNotification notification) { ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps( notification.getUserId(), notification.getPackageName()); if (activeOps != null) { @@ -1098,7 +1091,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. * @param entry the entry to check * @return true if the entry should ambient pulse, false otherwise */ - protected boolean shouldPulse(NotificationData.Entry entry) { + private boolean shouldPulse(NotificationData.Entry entry) { StatusBarNotification sbn = entry.notification; if (!getShadeController().isDozing()) { @@ -1173,7 +1166,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. return true; } - protected void setNotificationShown(StatusBarNotification n) { + private void setNotificationShown(StatusBarNotification n) { setNotificationsShown(new String[]{n.getKey()}); } @@ -1185,7 +1178,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. } } - protected boolean isSnoozedPackage(StatusBarNotification sbn) { + private boolean isSnoozedPackage(StatusBarNotification sbn) { return mHeadsUpManager.isSnoozed(sbn.getPackageName()); } 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 b6d99b245466..daec9c9cca33 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 @@ -3145,8 +3145,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView pw.print(", alpha: " + getAlpha()); pw.print(", translation: " + getTranslation()); pw.print(", removed: " + isRemoved()); - pw.print(", privateShowing: " + (getShowingLayout() == mPrivateLayout)); + NotificationContentView showingLayout = getShowingLayout(); + pw.print(", privateShowing: " + (showingLayout == mPrivateLayout)); pw.println(); + showingLayout.dump(fd, pw, args); pw.print(" "); if (mNotificationViewState != null) { mNotificationViewState.dump(fd, pw, args); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index bb9a3412e703..88edc0d8f17d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -55,6 +55,9 @@ import com.android.systemui.statusbar.policy.RemoteInputView; import com.android.systemui.statusbar.policy.SmartReplyConstants; import com.android.systemui.statusbar.policy.SmartReplyView; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Collections; import java.util.List; /** @@ -1322,6 +1325,10 @@ public class NotificationContentView extends FrameLayout { List<Notification.Action> appGeneratedSmartActions = notification.getContextualActions(); boolean appGeneratedSmartActionsExist = !appGeneratedSmartActions.isEmpty(); + List<Notification.Action> sysGeneratedSmartActions = + notification.getAllowSystemGeneratedContextualActions() + ? entry.systemGeneratedSmartActions : Collections.emptyList(); + if (appGeneratedSmartRepliesExist) { return new SmartRepliesAndActions(remoteInputActionPair.first, remoteInputActionPair.second.actionIntent, @@ -1338,11 +1345,11 @@ public class NotificationContentView extends FrameLayout { return new SmartRepliesAndActions(freeformRemoteInputActionPair.first, freeformRemoteInputActionPair.second.actionIntent, entry.smartReplies, - entry.systemGeneratedSmartActions, + sysGeneratedSmartActions, freeformRemoteInputActionPair); } // App didn't generate anything, and there are no NAS-generated smart replies. - return new SmartRepliesAndActions(null, null, null, entry.systemGeneratedSmartActions, + return new SmartRepliesAndActions(null, null, null, sysGeneratedSmartActions, freeformRemoteInputActionPair); } @@ -1928,4 +1935,23 @@ public class NotificationContentView extends FrameLayout { mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount); } } + + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.print(" "); + pw.print("contentView visibility: " + getVisibility()); + pw.print(", alpha: " + getAlpha()); + pw.print(", clipBounds: " + getClipBounds()); + pw.print(", contentHeight: " + mContentHeight); + pw.print(", visibleType: " + mVisibleType); + View view = getViewForVisibleType(mVisibleType); + pw.print(", visibleView "); + if (view != null) { + pw.print(" visibility: " + view.getVisibility()); + pw.print(", alpha: " + view.getAlpha()); + pw.print(", clipBounds: " + view.getClipBounds()); + } else { + pw.print("null"); + } + pw.println(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index 3a7091bb843a..0d36d2c2f77c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -92,6 +92,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private String mPackageName; private String mAppName; private int mAppUid; + private String mDelegatePkg; private int mNumUniqueChannelsInRow; private NotificationChannel mSingleNotificationChannel; private int mStartingChannelImportance; @@ -235,6 +236,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G (mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0; mIsForBlockingHelper = isForBlockingHelper; mAppUid = mSbn.getUid(); + mDelegatePkg = mSbn.getOpPkg(); mIsDeviceProvisioned = isDeviceProvisioned; mIsNoisy = isNoisy; @@ -281,26 +283,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon); ((TextView) findViewById(R.id.pkgname)).setText(mAppName); - // Set group information if this channel has an associated group. - CharSequence groupName = null; - if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) { - final NotificationChannelGroup notificationChannelGroup = - mINotificationManager.getNotificationChannelGroupForPackage( - mSingleNotificationChannel.getGroup(), mPackageName, mAppUid); - if (notificationChannelGroup != null) { - groupName = notificationChannelGroup.getName(); - } - } - TextView groupNameView = findViewById(R.id.group_name); - TextView groupDividerView = findViewById(R.id.pkg_group_divider); - if (groupName != null) { - groupNameView.setText(groupName); - groupNameView.setVisibility(View.VISIBLE); - groupDividerView.setVisibility(View.VISIBLE); - } else { - groupNameView.setVisibility(View.GONE); - groupDividerView.setVisibility(View.GONE); - } + // Delegate + bindDelegate(); // Settings button. final View settingsButton = findViewById(R.id.info); @@ -320,9 +304,10 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } } - private void bindPrompt() { + private void bindPrompt() throws RemoteException { final TextView blockPrompt = findViewById(R.id.block_prompt); bindName(); + bindGroup(); if (mIsNonblockable) { blockPrompt.setText(R.string.notification_unblockable_desc); } else { @@ -345,6 +330,60 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } } + private void bindDelegate() { + TextView delegateView = findViewById(R.id.delegate_name); + TextView dividerView = findViewById(R.id.pkg_divider); + + CharSequence delegatePkg = null; + if (!TextUtils.equals(mPackageName, mDelegatePkg)) { + // this notification was posted by a delegate! + ApplicationInfo info; + try { + info = mPm.getApplicationInfo( + mDelegatePkg, + PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DISABLED_COMPONENTS + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_DIRECT_BOOT_AWARE); + if (info != null) { + delegatePkg = String.valueOf(mPm.getApplicationLabel(info)); + } + } catch (PackageManager.NameNotFoundException e) { } + } + if (delegatePkg != null) { + delegateView.setText(mContext.getResources().getString( + R.string.notification_delegate_header, delegatePkg)); + delegateView.setVisibility(View.VISIBLE); + dividerView.setVisibility(View.VISIBLE); + } else { + delegateView.setVisibility(View.GONE); + dividerView.setVisibility(View.GONE); + } + } + + private void bindGroup() throws RemoteException { + // Set group information if this channel has an associated group. + CharSequence groupName = null; + if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) { + final NotificationChannelGroup notificationChannelGroup = + mINotificationManager.getNotificationChannelGroupForPackage( + mSingleNotificationChannel.getGroup(), mPackageName, mAppUid); + if (notificationChannelGroup != null) { + groupName = notificationChannelGroup.getName(); + } + } + TextView groupNameView = findViewById(R.id.group_name); + TextView groupDividerView = findViewById(R.id.pkg_group_divider); + if (groupName != null) { + groupNameView.setText(groupName); + groupNameView.setVisibility(View.VISIBLE); + groupDividerView.setVisibility(View.VISIBLE); + } else { + groupNameView.setVisibility(View.GONE); + groupDividerView.setVisibility(View.GONE); + } + } + @VisibleForTesting void logBlockingHelperCounter(String counterTag) { if (mIsForBlockingHelper) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index c0d1818d8da9..7d1367971ed6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -227,6 +227,7 @@ public class KeyguardBouncer { mShowingSoon = false; if (mExpansion == EXPANSION_VISIBLE) { mKeyguardView.onResume(); + mKeyguardView.resetSecurityContainer(); } StatsLog.write(StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED, StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 0cf1b3ddc213..8657003891be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -56,7 +56,6 @@ import android.telecom.TelecomManager; import android.text.TextUtils; import android.util.Log; import android.view.Display; -import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -863,16 +862,9 @@ public class NavigationBarFragment extends Fragment implements Callbacks { public static View create(Context context, FragmentListener listener) { final int displayId = context.getDisplay().getDisplayId(); - final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); - final int height = isDefaultDisplay - ? LayoutParams.MATCH_PARENT - : context.getResources().getDimensionPixelSize(R.dimen.navigation_bar_height); WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - LayoutParams.MATCH_PARENT, height, - // TODO(b/117478341): Resolve one status bar/ navigation bar assumption - isDefaultDisplay - ? WindowManager.LayoutParams.TYPE_NAVIGATION_BAR - : WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL @@ -884,10 +876,6 @@ public class NavigationBarFragment extends Fragment implements Callbacks { lp.setTitle("NavigationBar" + displayId); lp.accessibilityTitle = context.getString(R.string.nav_bar); lp.windowAnimations = 0; - if (!isDefaultDisplay) { - lp.flags |= LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; - lp.gravity = Gravity.BOTTOM; - } View navigationBarView = LayoutInflater.from(context).inflate( R.layout.navigation_bar_window, null); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 2f58ca1c984b..5db43eae8443 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -904,6 +904,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL); int navBarPos = 0; try { + // TODO: Use WindowManagerService.getNavBarPosition(int displayId) navBarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition(); } catch (RemoteException e) { Slog.e(TAG, "Failed to get nav bar position.", e); 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 45e924fa4321..bdddf5b9794f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -2251,7 +2251,8 @@ public class StatusBar extends SystemUI implements DemoMode, private void notifyUiVisibilityChanged(int vis) { try { if (mLastDispatchedSystemUiVisibility != vis) { - mWindowManagerService.statusBarVisibilityChanged(vis); + // TODO (b/117478341): Resolve one status bar/ navigation bar assumption + mWindowManagerService.statusBarVisibilityChanged(Display.DEFAULT_DISPLAY, vis); mLastDispatchedSystemUiVisibility = vis; } } catch (RemoteException ex) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java index a6725b81f358..5e137a7de65a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java @@ -106,7 +106,8 @@ public class NotificationContentViewTest extends SysuiTestCase { mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight()); - // Smart replies + // Smart replies and actions + when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(true); when(mStatusBarNotification.getNotification()).thenReturn(mNotification); mEntry = new NotificationData.Entry(mStatusBarNotification); when(mSmartReplyConstants.isEnabled()).thenReturn(true); @@ -299,6 +300,23 @@ public class NotificationContentViewTest extends SysuiTestCase { assertThat(repliesAndActions.smartActions, equalTo(appGenSmartActions)); } + @Test + public void chooseSmartRepliesAndActions_disallowSysGenSmartActions() { + // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart + // actions. + setupAppGeneratedReplies(null); + + when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(false); + + mEntry.systemGeneratedSmartActions = + createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"}); + NotificationContentView.SmartRepliesAndActions repliesAndActions = + NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); + + assertThat(repliesAndActions.smartReplies, equalTo(null)); + assertThat(repliesAndActions.smartActions, is(empty())); + } + private Notification.Action.Builder createActionBuilder(String actionTitle) { PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(TEST_ACTION), 0); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index 1cc1c637983b..985827a9cd54 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java @@ -134,7 +134,7 @@ public class NotificationInfoTest extends SysuiTestCase { .thenReturn(packageInfo); final ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.uid = TEST_UID; // non-zero - when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn( + when(mMockPackageManager.getApplicationInfo(eq(TEST_PACKAGE_NAME), anyInt())).thenReturn( applicationInfo); final PackageInfo systemPackageInfo = new PackageInfo(); systemPackageInfo.packageName = TEST_SYSTEM_PACKAGE_NAME; @@ -206,6 +206,37 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test + public void testBindNotification_noDelegate() throws Exception { + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); + assertEquals(GONE, nameView.getVisibility()); + final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider); + assertEquals(GONE, dividerView.getVisibility()); + } + + @Test + public void testBindNotification_delegate() throws Exception { + mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, "other", 0, null, TEST_UID, 0, + new Notification(), UserHandle.CURRENT, null, 0); + final ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.uid = 7; // non-zero + when(mMockPackageManager.getApplicationInfo(eq("other"), anyInt())).thenReturn( + applicationInfo); + when(mMockPackageManager.getApplicationLabel(any())).thenReturn("Other"); + + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); + assertEquals(VISIBLE, nameView.getVisibility()); + assertTrue(nameView.getText().toString().contains("Other")); + final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider); + assertEquals(VISIBLE, dividerView.getVisibility()); + } + + @Test public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 17d8ea7eb77c..c56f31efd953 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -18,11 +18,13 @@ package com.android.server.autofill; import static android.Manifest.permission.MANAGE_AUTO_FILL; import static android.content.Context.AUTOFILL_MANAGER_SERVICE; +import static android.util.DebugUtils.flagsToString; import static com.android.server.autofill.Helper.sDebug; import static com.android.server.autofill.Helper.sFullScreenMode; import static com.android.server.autofill.Helper.sVerbose; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -72,9 +74,12 @@ import com.android.server.AbstractMasterSystemService; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.autofill.ui.AutoFillUI; +import com.android.server.intelligence.IntelligenceManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -95,6 +100,27 @@ public final class AutofillManagerService private static final Object sLock = AutofillManagerService.class; + + /** + * IME supports Smart Suggestions. + */ + // NOTE: must be public because of flagsToString() + public static final int FLAG_SMART_SUGGESTION_IME = 0x1; + + /** + * System supports Smarts Suggestions (as a popup-window similar to standard Autofill). + */ + // NOTE: must be public because of flagsToString() + public static final int FLAG_SMART_SUGGESTION_SYSTEM = 0x2; + + /** @hide */ + @IntDef(flag = true, prefix = { "FLAG_SMART_SUGGESTION_" }, value = { + FLAG_SMART_SUGGESTION_IME, + FLAG_SMART_SUGGESTION_SYSTEM + }) + @Retention(RetentionPolicy.SOURCE) + @interface SmartSuggestionMode {} + static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions"; private static final char COMPAT_PACKAGE_DELIMITER = ':'; @@ -102,7 +128,6 @@ public final class AutofillManagerService private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_BEGIN = '['; private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_END = ']'; - /** * Maximum number of partitions that can be allowed in a session. * @@ -130,6 +155,7 @@ public final class AutofillManagerService private final AutofillCompatState mAutofillCompatState = new AutofillCompatState(); private final LocalService mLocalService = new LocalService(); + final IntelligenceManagerInternal mIntelligenceManagerInternal; private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -153,13 +179,21 @@ public final class AutofillManagerService @GuardedBy("mLock") private boolean mAllowInstantService; + /** + * Supported modes for Augmented Autofill Smart Suggestions. + */ + @GuardedBy("mLock") + private int mSupportedSmartSuggestionModes; + public AutofillManagerService(Context context) { super(context, UserManager.DISALLOW_AUTOFILL); mUi = new AutoFillUI(ActivityThread.currentActivityThread().getSystemUiContext()); + mIntelligenceManagerInternal = LocalServices.getService(IntelligenceManagerInternal.class); setLogLevelFromSettings(); setMaxPartitionsFromSettings(); setMaxVisibleDatasetsFromSettings(); + setSmartSuggestionEmulationFromSettings(); final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); @@ -186,6 +220,9 @@ public final class AutofillManagerService resolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS), false, observer, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS), false, observer, + UserHandle.USER_ALL); } @Override // from AbstractMasterSystemService @@ -200,6 +237,9 @@ public final class AutofillManagerService case Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS: setMaxVisibleDatasetsFromSettings(); break; + case Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS: + setSmartSuggestionEmulationFromSettings(); + break; default: Slog.w(TAG, "Unexpected property (" + property + "); updating cache instead"); // fall through @@ -243,6 +283,10 @@ public final class AutofillManagerService mUi.hideAll(null); } + @SmartSuggestionMode int getSupportedSmartSuggestionModesLocked() { + return mSupportedSmartSuggestionModes; + } + // Called by Shell command. void destroySessions(@UserIdInt int userId, IResultReceiver receiver) { Slog.i(TAG, "destroySessions() for userId " + userId); @@ -420,6 +464,19 @@ public final class AutofillManagerService } } + private void setSmartSuggestionEmulationFromSettings() { + final int flags = Settings.Global.getInt(getContext().getContentResolver(), + Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS, 0); + if (sDebug) { + Slog.d(TAG, "setSmartSuggestionEmulationFromSettings(): " + + smartSuggestionFlagsToString(flags)); + } + + synchronized (mLock) { + mSupportedSmartSuggestionModes = flags; + } + } + // Called by Shell command. void getScore(@Nullable String algorithmName, @NonNull String value1, @NonNull String value2, @NonNull RemoteCallback callback) { @@ -610,6 +667,10 @@ public final class AutofillManagerService } } + static String smartSuggestionFlagsToString(int flags) { + return flagsToString(AutofillManagerService.class, "FLAG_SMART_SUGGESTION_", flags); + } + private final class LocalService extends AutofillManagerInternal { @Override public void onBackKeyPressed() { @@ -1158,6 +1219,10 @@ public final class AutofillManagerService pw.print("from settings: "); pw.println(getWhitelistedCompatModePackagesFromSettings()); pw.print("Allow instant service: "); pw.println(mAllowInstantService); + if (mSupportedSmartSuggestionModes != 0) { + pw.print("Smart Suggestion modes: "); + pw.println(smartSuggestionFlagsToString(mSupportedSmartSuggestionModes)); + } if (showHistory) { pw.println(); pw.println("Requests history:"); pw.println(); mRequestsHistory.reverseDump(fd, pw, args); diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 67ccc9b18543..0df99d4b6642 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -73,6 +73,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.server.AbstractPerUserSystemService; import com.android.server.LocalServices; import com.android.server.autofill.AutofillManagerService.AutofillCompatState; +import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode; import com.android.server.autofill.ui.AutoFillUI; import java.io.PrintWriter; @@ -268,8 +269,8 @@ final class AutofillManagerServiceImpl pruneAbandonedSessionsLocked(); final Session newSession = createSessionByTokenLocked(activityToken, taskId, uid, - appCallbackToken, hasCallback, componentName, compatMode, bindInstantServiceAllowed, - flags); + appCallbackToken, hasCallback, componentName, compatMode, + bindInstantServiceAllowed, flags); if (newSession == null) { return NO_SESSION; } @@ -823,6 +824,12 @@ final class AutofillManagerServiceImpl return true; } + @GuardedBy("mLock") + @SmartSuggestionMode int getSupportedSmartSuggestionModesLocked() { + // TODO(b/111330312): once we support IME, we need to set it per-user (OR'ed with master) + return mMaster.getSupportedSmartSuggestionModesLocked(); + } + @Override @GuardedBy("mLock") protected void dumpLocked(String prefix, PrintWriter pw) { @@ -962,6 +969,9 @@ final class AutofillManagerServiceImpl if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id); session.forceRemoveSelfLocked(); } + else { + session.destroyAugmentedAutofillWindowsLocked(); + } } } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 1ff1acdb3b57..8676f7f5bea0 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -25,6 +25,9 @@ import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED; import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; +import static com.android.server.autofill.AutofillManagerService.FLAG_SMART_SUGGESTION_IME; +import static com.android.server.autofill.AutofillManagerService.FLAG_SMART_SUGGESTION_SYSTEM; +import static com.android.server.autofill.AutofillManagerService.smartSuggestionFlagsToString; import static com.android.server.autofill.Helper.getNumericValue; import static com.android.server.autofill.Helper.sDebug; import static com.android.server.autofill.Helper.sVerbose; @@ -93,8 +96,11 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.ArrayUtils; import com.android.server.AbstractRemoteService; +import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode; import com.android.server.autofill.ui.AutoFillUI; import com.android.server.autofill.ui.PendingUi; +import com.android.server.intelligence.IntelligenceManagerInternal; +import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback; import java.io.PrintWriter; import java.util.ArrayList; @@ -242,6 +248,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private final SparseArray<LogMaker> mRequestLogs = new SparseArray<>(1); + @GuardedBy("mLock") + @Nullable + private AugmentedAutofillCallback mAugmentedAutofillCallback; + /** * Receiver of assist data from the app's {@link Activity}. */ @@ -2497,15 +2507,83 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState processResponseLocked(newResponse, newClientState, 0); } + @GuardedBy("mLock") private void processNullResponseLocked(int flags) { - if (sVerbose) Slog.v(TAG, "canceling session " + id + " when server returned null"); if ((flags & FLAG_MANUAL_REQUEST) != 0) { getUiForShowing().showError(R.string.autofill_error_cannot_autofill, this); } mService.resetLastResponse(); - // Nothing to be done, but need to notify client. - notifyUnavailableToClient(AutofillManager.STATE_FINISHED); - removeSelf(); + + // The default autofill service cannot fullfill the request, let's check if the intelligence + // service can. + mAugmentedAutofillCallback = triggerAugmentedAutofillLocked(); + if (mAugmentedAutofillCallback == null) { + if (sVerbose) { + Slog.v(TAG, "canceling session " + id + " when server returned null and there is no" + + " AugmentedAutofill for user"); + } + // Nothing to be done, but need to notify client. + notifyUnavailableToClient(AutofillManager.STATE_FINISHED); + removeSelf(); + } else { + // TODO(b/111330312, b/119638958): must set internal state so when user focus other + // fields it does not generate a new call to the standard autofill service (right now + // it does). Must also add CTS tests to exercise this scenario. + if (sVerbose) { + Slog.v(TAG, "keeping session " + id + " when server returned null but " + + "there is an AugmentedAutofill for user"); + } + } + } + + /** + * Tries to trigger Augmented Autofill when the standard service could not fulfill a request. + * + * @return callback to the Augmented Autofill service, or {@code null} if not supported. + */ + // TODO(b/111330312): might need to call it in other places, like when the service returns a + // non-null response but without datasets (for example, just SaveInfo) + @GuardedBy("mLock") + private AugmentedAutofillCallback triggerAugmentedAutofillLocked() { + // Check if Smart Suggestions is supported... + final @SmartSuggestionMode int supportedModes = mService + .getSupportedSmartSuggestionModesLocked(); + if (supportedModes == 0) return null; + + // ...then if the service is set for the user + final IntelligenceManagerInternal intelligenceManagerInternal = mService + .getMaster().mIntelligenceManagerInternal; + if (intelligenceManagerInternal == null) return null; + + // Define which mode will be used + final int mode; + if ((supportedModes & FLAG_SMART_SUGGESTION_IME) != 0) { + // TODO(b/111330312): support it :-) + Slog.w(TAG, "Smart Suggestions on IME not supported yet"); + return null; + } else if ((supportedModes & FLAG_SMART_SUGGESTION_SYSTEM) != 0) { + mode = FLAG_SMART_SUGGESTION_SYSTEM; + } else { + Slog.w(TAG, "Unsupported Smart Suggestion Mode: " + supportedModes); + return null; + } + + if (mCurrentViewId == null) { + Slog.w(TAG, "triggerAugmentedAutofillLocked(): no view currently focused"); + return null; + } + + if (sVerbose) { + Slog.v(TAG, "calling IntelligenseService on view " + mCurrentViewId + + " using suggestion mode " + smartSuggestionFlagsToString(mode) + + " when server returned null for session " + this.id); + } + + // TODO(b/111330312): we might need to add a new state in the AutofillManager to optimize + // furgher AFM -> AFMS calls. + // TODO(b/119638958): add CTS tests + return intelligenceManagerInternal.requestAutofill(mService.getUserId(), mClient, + mActivityToken, this.id, mCurrentViewId); } @GuardedBy("mLock") @@ -2786,6 +2864,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println( mSaveOnAllViewsInvisible); pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds); + if (mAugmentedAutofillCallback != null) { + pw.print(prefix); pw.println("has AugmentedAutofillCallback"); + } mRemoteFillService.dump(prefix, pw); } @@ -2957,6 +3038,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.e(TAG, "Error notifying client to finish session", e); } } + destroyAugmentedAutofillWindowsLocked(); + } + + @GuardedBy("mLock") + void destroyAugmentedAutofillWindowsLocked() { + if (mAugmentedAutofillCallback != null) { + mAugmentedAutofillCallback.destroy(); + } } /** diff --git a/services/core/java/com/android/server/AbstractPerUserSystemService.java b/services/core/java/com/android/server/AbstractPerUserSystemService.java index b37888f8ef2e..71d261c7f5b5 100644 --- a/services/core/java/com/android/server/AbstractPerUserSystemService.java +++ b/services/core/java/com/android/server/AbstractPerUserSystemService.java @@ -163,6 +163,20 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst } /** + * Gets the user associated with this service. + */ + public final @UserIdInt int getUserId() { + return mUserId; + } + + /** + * Gets the master service. + */ + public final M getMaster() { + return mMaster; + } + + /** * Gets this UID of the remote service this service binds to, or {@code -1} if the service is * disabled. */ diff --git a/services/core/java/com/android/server/AbstractRemoteService.java b/services/core/java/com/android/server/AbstractRemoteService.java index 181d7fde1fb8..73a34d6e0f3c 100644 --- a/services/core/java/com/android/server/AbstractRemoteService.java +++ b/services/core/java/com/android/server/AbstractRemoteService.java @@ -205,6 +205,9 @@ public abstract class AbstractRemoteService implements DeathRecipient { protected void scheduleUnbind() { cancelScheduledUnbind(); + // TODO(b/111276913): implement "permanent binding" + // TODO(b/117779333): make sure it's unbound if the service settings changing (right now + // it's not) mHandler.sendMessageDelayed(obtainMessage(AbstractRemoteService::handleUnbind, this) .setWhat(MSG_UNBIND), getTimeoutIdleBindMillis()); } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 74c80237d721..564d35a0936d 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -894,10 +894,18 @@ public class ConnectivityService extends IConnectivityManager.Stub intentFilter.addAction(Intent.ACTION_USER_REMOVED); intentFilter.addAction(Intent.ACTION_USER_UNLOCKED); mContext.registerReceiverAsUser( - mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null); + mIntentReceiver, UserHandle.ALL, intentFilter, null, null); mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM, new IntentFilter(Intent.ACTION_USER_PRESENT), null, null); + // Listen to package add and removal events for all users. + intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); + intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); + intentFilter.addDataScheme("package"); + mContext.registerReceiverAsUser( + mIntentReceiver, UserHandle.ALL, intentFilter, null, null); + try { mNMS.registerObserver(mTethering); mNMS.registerObserver(mDataActivityObserver); @@ -4155,6 +4163,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void onUserAdded(int userId) { + mPermissionMonitor.onUserAdded(userId); synchronized (mVpns) { final int vpnsSize = mVpns.size(); for (int i = 0; i < vpnsSize; i++) { @@ -4165,6 +4174,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void onUserRemoved(int userId) { + mPermissionMonitor.onUserRemoved(userId); synchronized (mVpns) { final int vpnsSize = mVpns.size(); for (int i = 0; i < vpnsSize; i++) { @@ -4174,6 +4184,22 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private void onPackageAdded(String packageName, int uid) { + if (TextUtils.isEmpty(packageName) || uid < 0) { + Slog.wtf(TAG, "Invalid package in onPackageAdded: " + packageName + " | " + uid); + return; + } + mPermissionMonitor.onPackageAdded(packageName, uid); + } + + private void onPackageRemoved(String packageName, int uid) { + if (TextUtils.isEmpty(packageName) || uid < 0) { + Slog.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid); + return; + } + mPermissionMonitor.onPackageRemoved(uid); + } + private void onUserUnlocked(int userId) { synchronized (mVpns) { // User present may be sent because of an unlock, which might mean an unlocked keystore. @@ -4185,11 +4211,15 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() { + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); + final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); + final Uri packageData = intent.getData(); + final String packageName = + packageData != null ? packageData.getSchemeSpecificPart() : null; if (userId == UserHandle.USER_NULL) return; if (Intent.ACTION_USER_STARTED.equals(action)) { @@ -4202,6 +4232,10 @@ public class ConnectivityService extends IConnectivityManager.Stub onUserRemoved(userId); } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) { onUserUnlocked(userId); + } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { + onPackageAdded(packageName, uid); + } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + onPackageRemoved(packageName, uid); } } }; diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 8c25917c7436..7ee3d3b3bdc7 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -24,6 +24,7 @@ import static android.system.OsConstants.AF_UNSPEC; import static android.system.OsConstants.EINVAL; import static android.system.OsConstants.IPPROTO_UDP; import static android.system.OsConstants.SOCK_DGRAM; + import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.NonNull; @@ -62,6 +63,8 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; +import libcore.io.IoUtils; + import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; @@ -73,8 +76,6 @@ import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; -import libcore.io.IoUtils; - /** * A service to manage multiple clients that want to access the IpSec API. The service is * responsible for maintaining a list of clients and managing the resources (and related quotas) @@ -621,7 +622,8 @@ public class IpSecService extends IIpSecService.Stub { mConfig.getDestinationAddress(), spi, mConfig.getMarkValue(), - mConfig.getMarkMask()); + mConfig.getMarkMask(), + mConfig.getXfrmInterfaceId()); } catch (RemoteException | ServiceSpecificException e) { Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e); } @@ -683,7 +685,8 @@ public class IpSecService extends IIpSecService.Stub { mSrvConfig .getNetdInstance() .ipSecDeleteSecurityAssociation( - uid, mSourceAddress, mDestinationAddress, mSpi, 0, 0); + uid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */, + 0 /* mask */, 0 /* if_id */); } } catch (ServiceSpecificException | RemoteException e) { Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e); @@ -795,6 +798,8 @@ public class IpSecService extends IIpSecService.Stub { private final int mIkey; private final int mOkey; + private final int mIfId; + TunnelInterfaceRecord( int resourceId, String interfaceName, @@ -802,7 +807,8 @@ public class IpSecService extends IIpSecService.Stub { String localAddr, String remoteAddr, int ikey, - int okey) { + int okey, + int intfId) { super(resourceId); mInterfaceName = interfaceName; @@ -811,6 +817,7 @@ public class IpSecService extends IIpSecService.Stub { mRemoteAddress = remoteAddr; mIkey = ikey; mOkey = okey; + mIfId = intfId; } /** always guarded by IpSecService#this */ @@ -821,7 +828,7 @@ public class IpSecService extends IIpSecService.Stub { // Delete global policies try { final INetd netd = mSrvConfig.getNetdInstance(); - netd.removeVirtualTunnelInterface(mInterfaceName); + netd.ipSecRemoveTunnelInterface(mInterfaceName); for (int selAddrFamily : ADDRESS_FAMILIES) { netd.ipSecDeleteSecurityPolicy( @@ -829,13 +836,15 @@ public class IpSecService extends IIpSecService.Stub { selAddrFamily, IpSecManager.DIRECTION_OUT, mOkey, - 0xffffffff); + 0xffffffff, + mIfId); netd.ipSecDeleteSecurityPolicy( uid, selAddrFamily, IpSecManager.DIRECTION_IN, mIkey, - 0xffffffff); + 0xffffffff, + mIfId); } } catch (ServiceSpecificException | RemoteException e) { Log.e( @@ -877,6 +886,10 @@ public class IpSecService extends IIpSecService.Stub { return mOkey; } + public int getIfId() { + return mIfId; + } + @Override protected ResourceTracker getResourceTracker() { return getUserRecord().mTunnelQuotaTracker; @@ -1286,7 +1299,7 @@ public class IpSecService extends IIpSecService.Stub { // Add inbound/outbound global policies // (use reqid = 0) final INetd netd = mSrvConfig.getNetdInstance(); - netd.addVirtualTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey); + netd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId); for (int selAddrFamily : ADDRESS_FAMILIES) { // Always send down correct local/remote addresses for template. @@ -1298,7 +1311,8 @@ public class IpSecService extends IIpSecService.Stub { remoteAddr, 0, okey, - 0xffffffff); + 0xffffffff, + resourceId); netd.ipSecAddSecurityPolicy( callerUid, selAddrFamily, @@ -1307,7 +1321,8 @@ public class IpSecService extends IIpSecService.Stub { localAddr, 0, ikey, - 0xffffffff); + 0xffffffff, + resourceId); } userRecord.mTunnelInterfaceRecords.put( @@ -1320,7 +1335,8 @@ public class IpSecService extends IIpSecService.Stub { localAddr, remoteAddr, ikey, - okey), + okey, + resourceId), binder)); return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName); } catch (RemoteException e) { @@ -1523,6 +1539,9 @@ public class IpSecService extends IIpSecService.Stub { throw new IllegalArgumentException( "Invalid IpSecTransform.mode: " + config.getMode()); } + + config.setMarkValue(0); + config.setMarkMask(0); } private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS; @@ -1584,7 +1603,8 @@ public class IpSecService extends IIpSecService.Stub { (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0, encapType, encapLocalPort, - encapRemotePort); + encapRemotePort, + c.getXfrmInterfaceId()); } /** @@ -1740,27 +1760,48 @@ public class IpSecService extends IIpSecService.Stub { : tunnelInterfaceInfo.getIkey(); try { - c.setMarkValue(mark); - c.setMarkMask(0xffffffff); + // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip + // SPI matching as part of the template resolution. + int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX; + c.setXfrmInterfaceId(tunnelInterfaceInfo.getIfId()); + + // TODO: enable this when UPDSA supports updating marks. Adding kernel support upstream + // (and backporting) would allow us to narrow the mark space, and ensure that the SA + // and SPs have matching marks (as VTI are meant to be built). + // Currently update does nothing with marks. Leave empty (defaulting to 0) to ensure the + // config matches the actual allocated resources in the kernel. + // All SAs will have zero marks (from creation time), and any policy that matches the + // same src/dst could match these SAs. Non-IpSecService governed processes that + // establish floating policies with the same src/dst may result in undefined + // behavior. This is generally limited to vendor code due to the permissions + // (CAP_NET_ADMIN) required. + // + // c.setMarkValue(mark); + // c.setMarkMask(0xffffffff); if (direction == IpSecManager.DIRECTION_OUT) { // Set output mark via underlying network (output only) c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork()); - // If outbound, also add SPI to the policy. - for (int selAddrFamily : ADDRESS_FAMILIES) { - mSrvConfig - .getNetdInstance() - .ipSecUpdateSecurityPolicy( - callingUid, - selAddrFamily, - direction, - tunnelInterfaceInfo.getLocalAddress(), - tunnelInterfaceInfo.getRemoteAddress(), - transformInfo.getSpiRecord().getSpi(), - mark, - 0xffffffff); - } + // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys, + // but want to guarantee outbound packets are sent over the new SA. + spi = transformInfo.getSpiRecord().getSpi(); + } + + // Always update the policy with the relevant XFRM_IF_ID + for (int selAddrFamily : ADDRESS_FAMILIES) { + mSrvConfig + .getNetdInstance() + .ipSecUpdateSecurityPolicy( + callingUid, + selAddrFamily, + direction, + transformInfo.getConfig().getSourceAddress(), + transformInfo.getConfig().getDestinationAddress(), + spi, // If outbound, also add SPI to the policy. + mark, // Must always set policy mark; ikey/okey for VTIs + 0xffffffff, + c.getXfrmInterfaceId()); } // Update SA with tunnel mark (ikey or okey based on direction) diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 046442a0b3ad..e5275e50e484 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -17,6 +17,7 @@ package com.android.server; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.provider.Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS; import android.Manifest; import android.annotation.NonNull; @@ -2990,7 +2991,7 @@ public class LocationManagerService extends ILocationManager.Stub { ArrayList<Receiver> deadReceivers = null; ArrayList<UpdateRecord> deadUpdateRecords = null; - // Broadcast location or status to all listeners + // Broadcast location to all listeners for (UpdateRecord r : records) { Receiver receiver = r.mReceiver; boolean receiverDead = false; @@ -3049,14 +3050,19 @@ public class LocationManagerService extends ILocationManager.Stub { } } - long prevStatusUpdateTime = r.mLastStatusBroadcast; - if ((newStatusUpdateTime > prevStatusUpdateTime) && - (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) { + // TODO: location provider status callbacks have been disabled and deprecated, and are + // guarded behind this setting now. should be removed completely post-Q + if (Settings.Global.getInt(mContext.getContentResolver(), + LOCATION_DISABLE_STATUS_CALLBACKS, 1) == 0) { + long prevStatusUpdateTime = r.mLastStatusBroadcast; + if ((newStatusUpdateTime > prevStatusUpdateTime) + && (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) { - r.mLastStatusBroadcast = newStatusUpdateTime; - if (!receiver.callStatusChangedLocked(provider, status, extras)) { - receiverDead = true; - Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver); + r.mLastStatusBroadcast = newStatusUpdateTime; + if (!receiver.callStatusChangedLocked(provider, status, extras)) { + receiverDead = true; + Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver); + } } } @@ -3276,7 +3282,6 @@ public class LocationManagerService extends ILocationManager.Stub { // we don't leave anything dangling. clearTestProviderEnabled(provider, opPackageName); clearTestProviderLocation(provider, opPackageName); - clearTestProviderStatus(provider, opPackageName); MockProvider mockProvider = mMockProviders.remove(provider); if (mockProvider == null) { @@ -3409,21 +3414,6 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public void clearTestProviderStatus(String provider, String opPackageName) { - if (!canCallerAccessMockLocation(opPackageName)) { - return; - } - - synchronized (mLock) { - MockProvider mockProvider = mMockProviders.get(provider); - if (mockProvider == null) { - throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); - } - mockProvider.clearStatus(); - } - } - - @Override public PendingIntent createManageLocationPermissionIntent(String packageName, String permission) { Preconditions.checkNotNull(packageName); diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java index 2dee3a0b5c21..c563ad224da6 100644 --- a/services/core/java/com/android/server/LooperStatsService.java +++ b/services/core/java/com/android/server/LooperStatsService.java @@ -141,6 +141,7 @@ public class LooperStatsService extends Binder { if (mEnabled != enabled) { mEnabled = enabled; mStats.reset(); + mStats.setAddDebugEntries(enabled); Looper.setObserver(enabled ? mStats : null); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7e9e83cf17d0..5f9e3493fb23 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -497,6 +497,18 @@ public class ActivityManagerService extends IActivityManager.Stub private static final int MINIMUM_MEMORY_GROWTH_THRESHOLD = 10 * 1000; // 10 MB /** + * The number of binder proxies we need to have before we start warning and + * dumping debug info. + */ + private static final int BINDER_PROXY_HIGH_WATERMARK = 6000; + + /** + * Low watermark that needs to be met before we consider dumping info again, + * after already hitting the high watermark. + */ + private static final int BINDER_PROXY_LOW_WATERMARK = 5500; + + /** * State indicating that there is no need for any blocking for network. */ @VisibleForTesting @@ -8477,7 +8489,8 @@ public class ActivityManagerService extends IActivityManager.Stub mAtmInternal.resumeTopActivities(false /* scheduleIdle */); mUserController.sendUserSwitchBroadcasts(-1, currentUserId); - BinderInternal.nSetBinderProxyCountWatermarks(6000,5500); + BinderInternal.nSetBinderProxyCountWatermarks(BINDER_PROXY_HIGH_WATERMARK, + BINDER_PROXY_LOW_WATERMARK); BinderInternal.nSetBinderProxyCountEnabled(true); BinderInternal.setBinderProxyCountCallback( new BinderInternal.BinderProxyLimitListener() { @@ -9217,11 +9230,6 @@ public class ActivityManagerService extends IActivityManager.Stub if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - dumpBinderProxies(pw); - pw.println(); - if (dumpAll) { - pw.println("-------------------------------------------------------------------------------"); - } dumpLmkLocked(pw); } pw.println(); @@ -9235,6 +9243,19 @@ public class ActivityManagerService extends IActivityManager.Stub } dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId); } + if (dumpPackage == null) { + // Intentionally dropping the lock for this, because dumpBinderProxies() will make many + // outgoing binder calls to retrieve interface descriptors; while that is system code, + // there is nothing preventing an app from overriding this implementation by talking to + // the binder driver directly, and hang up system_server in the process. So, dump + // without locks held, and even then only when there is an unreasonably large number of + // proxies in the first place. + pw.println(); + if (dumpAll) { + pw.println("-------------------------------------------------------------------------------"); + } + dumpBinderProxies(pw, BINDER_PROXY_HIGH_WATERMARK /* minToDump */); + } } /** @@ -9373,7 +9394,7 @@ public class ActivityManagerService extends IActivityManager.Stub cmd, fd, pw, args, opti, true /* dumpAll */, dumpClient, dumpPackage); } else if ("binder-proxies".equals(cmd)) { if (opti >= args.length) { - dumpBinderProxies(pw); + dumpBinderProxies(pw, 0 /* minToDump */); } else { String uid = args[opti]; opti++; @@ -9714,10 +9735,17 @@ public class ActivityManagerService extends IActivityManager.Stub return false; } - void dumpBinderProxies(PrintWriter pw) { + void dumpBinderProxies(PrintWriter pw, int minCountToDumpInterfaces) { pw.println("ACTIVITY MANAGER BINDER PROXY STATE (dumpsys activity binder-proxies)"); - dumpBinderProxyInterfaceCounts(pw, - " Top proxy interface names held by SYSTEM"); + final int proxyCount = BinderProxy.getProxyCount(); + if (proxyCount >= minCountToDumpInterfaces) { + dumpBinderProxyInterfaceCounts(pw, + "Top proxy interface names held by SYSTEM"); + } else { + pw.print("Not dumping proxy interface counts because size (" + + Integer.toString(proxyCount) + ") looks reasonable"); + pw.println(); + } dumpBinderProxiesCounts(pw, " Counts of Binder Proxies held by SYSTEM"); } diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java index 4fe7d03c3362..09df7e20a3e9 100644 --- a/services/core/java/com/android/server/am/AssistDataRequester.java +++ b/services/core/java/com/android/server/am/AssistDataRequester.java @@ -222,11 +222,11 @@ public class AssistDataRequester extends IAssistDataReceiver.Stub { receiverExtras.putInt(KEY_RECEIVER_EXTRA_INDEX, i); receiverExtras.putInt(KEY_RECEIVER_EXTRA_COUNT, numActivities); boolean result = requestAutofillData - ? ActivityTaskManager.getService().requestAssistContextExtras( + ? ActivityTaskManager.getService().requestAutofillData(this, + receiverExtras, topActivity, 0 /* flags */) + : ActivityTaskManager.getService().requestAssistContextExtras( ASSIST_CONTEXT_FULL, this, receiverExtras, topActivity, - /* focused= */ i == 0, /* newSessionId= */ i == 0) - : ActivityTaskManager.getService().requestAutofillData(this, - receiverExtras, topActivity, 0 /* flags */); + /* focused= */ i == 0, /* newSessionId= */ i == 0); if (result) { mPendingDataCount++; } else if (i == 0) { diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java index 94c94a514dec..420b23e6a39f 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java @@ -27,10 +27,7 @@ import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; import android.annotation.NonNull; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -42,7 +39,6 @@ import android.os.INetworkManagementService; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; -import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -64,15 +60,14 @@ import java.util.Set; public class PermissionMonitor { private static final String TAG = "PermissionMonitor"; private static final boolean DBG = true; - private static final Boolean SYSTEM = Boolean.TRUE; - private static final Boolean NETWORK = Boolean.FALSE; + protected static final Boolean SYSTEM = Boolean.TRUE; + protected static final Boolean NETWORK = Boolean.FALSE; private static final int VERSION_Q = Build.VERSION_CODES.Q; private final Context mContext; private final PackageManager mPackageManager; private final UserManager mUserManager; private final INetworkManagementService mNetd; - private final BroadcastReceiver mIntentReceiver; // Values are User IDs. private final Set<Integer> mUsers = new HashSet<>(); @@ -85,26 +80,6 @@ public class PermissionMonitor { mPackageManager = context.getPackageManager(); mUserManager = UserManager.get(context); mNetd = netd; - mIntentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); - int appUid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID); - Uri appData = intent.getData(); - String appName = appData != null ? appData.getSchemeSpecificPart() : null; - - if (Intent.ACTION_USER_ADDED.equals(action)) { - onUserAdded(user); - } else if (Intent.ACTION_USER_REMOVED.equals(action)) { - onUserRemoved(user); - } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { - onAppAdded(appName, appUid); - } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { - onAppRemoved(appUid); - } - } - }; } // Intended to be called only once at startup, after the system is ready. Installs a broadcast @@ -112,17 +87,6 @@ public class PermissionMonitor { public synchronized void startMonitoring() { log("Monitoring"); - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(Intent.ACTION_USER_ADDED); - intentFilter.addAction(Intent.ACTION_USER_REMOVED); - mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null); - - intentFilter = new IntentFilter(); - intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); - intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); - intentFilter.addDataScheme("package"); - mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null); - List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS); if (apps == null) { loge("No apps"); @@ -260,7 +224,14 @@ public class PermissionMonitor { } } - private synchronized void onUserAdded(int user) { + /** + * Called when a user is added. See {link #ACTION_USER_ADDED}. + * + * @param user The integer userHandle of the added user. See {@link #EXTRA_USER_HANDLE}. + * + * @hide + */ + public synchronized void onUserAdded(int user) { if (user < 0) { loge("Invalid user in onUserAdded: " + user); return; @@ -272,7 +243,14 @@ public class PermissionMonitor { update(users, mApps, true); } - private synchronized void onUserRemoved(int user) { + /** + * Called when an user is removed. See {link #ACTION_USER_REMOVED}. + * + * @param user The integer userHandle of the removed user. See {@link #EXTRA_USER_HANDLE}. + * + * @hide + */ + public synchronized void onUserRemoved(int user) { if (user < 0) { loge("Invalid user in onUserRemoved: " + user); return; @@ -284,8 +262,8 @@ public class PermissionMonitor { update(users, mApps, false); } - - private Boolean highestPermissionForUid(Boolean currentPermission, String name) { + @VisibleForTesting + protected Boolean highestPermissionForUid(Boolean currentPermission, String name) { if (currentPermission == SYSTEM) { return currentPermission; } @@ -303,33 +281,39 @@ public class PermissionMonitor { return currentPermission; } - private synchronized void onAppAdded(String appName, int appUid) { - if (TextUtils.isEmpty(appName) || appUid < 0) { - loge("Invalid app in onAppAdded: " + appName + " | " + appUid); - return; - } - + /** + * Called when a package is added. See {link #ACTION_PACKAGE_ADDED}. + * + * @param packageName The name of the new package. + * @param uid The uid of the new package. + * + * @hide + */ + public synchronized void onPackageAdded(String packageName, int uid) { // If multiple packages share a UID (cf: android:sharedUserId) and ask for different // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is). - final Boolean permission = highestPermissionForUid(mApps.get(appUid), appName); - if (permission != mApps.get(appUid)) { - mApps.put(appUid, permission); + final Boolean permission = highestPermissionForUid(mApps.get(uid), packageName); + if (permission != mApps.get(uid)) { + mApps.put(uid, permission); Map<Integer, Boolean> apps = new HashMap<>(); - apps.put(appUid, permission); + apps.put(uid, permission); update(mUsers, apps, true); } } - private synchronized void onAppRemoved(int appUid) { - if (appUid < 0) { - loge("Invalid app in onAppRemoved: " + appUid); - return; - } + /** + * Called when a package is removed. See {link #ACTION_PACKAGE_REMOVED}. + * + * @param uid containing the integer uid previously assigned to the package. + * + * @hide + */ + public synchronized void onPackageRemoved(int uid) { Map<Integer, Boolean> apps = new HashMap<>(); Boolean permission = null; - String[] packages = mPackageManager.getPackagesForUid(appUid); + String[] packages = mPackageManager.getPackagesForUid(uid); if (packages != null && packages.length > 0) { for (String name : packages) { permission = highestPermissionForUid(permission, name); @@ -341,16 +325,16 @@ public class PermissionMonitor { } } } - if (permission == mApps.get(appUid)) { + if (permission == mApps.get(uid)) { // The permissions of this UID have not changed. Nothing to do. return; } else if (permission != null) { - mApps.put(appUid, permission); - apps.put(appUid, permission); + mApps.put(uid, permission); + apps.put(uid, permission); update(mUsers, apps, true); } else { - mApps.remove(appUid); - apps.put(appUid, NETWORK); // doesn't matter which permission we pick here + mApps.remove(uid); + apps.put(uid, NETWORK); // doesn't matter which permission we pick here update(mUsers, apps, false); } } diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index a8f7259050c1..3c14393ca740 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -23,24 +23,18 @@ import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY; import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER; -import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE; import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER; import static android.net.ConnectivityManager.EXTRA_ERRORED_TETHER; import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO; -import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK; -import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE; -import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION; -import static android.net.ConnectivityManager.EXTRA_SET_ALARM; -import static android.net.ConnectivityManager.TETHER_ERROR_MASTER_ERROR; -import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; -import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL; -import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE; -import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE; import static android.net.ConnectivityManager.TETHERING_BLUETOOTH; import static android.net.ConnectivityManager.TETHERING_INVALID; import static android.net.ConnectivityManager.TETHERING_USB; import static android.net.ConnectivityManager.TETHERING_WIFI; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.ConnectivityManager.TETHER_ERROR_MASTER_ERROR; +import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; +import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL; +import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE; +import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; @@ -50,6 +44,7 @@ import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; import static android.net.wifi.WifiManager.IFACE_IP_MODE_UNSPECIFIED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; + import static com.android.server.ConnectivityService.SHORT_ARG; import android.app.Notification; @@ -60,7 +55,6 @@ import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile.ServiceListener; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -68,7 +62,6 @@ import android.content.res.Resources; import android.hardware.usb.UsbManager; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; -import android.net.ip.IpServer; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; @@ -76,7 +69,7 @@ import android.net.Network; import android.net.NetworkInfo; import android.net.NetworkState; import android.net.NetworkUtils; -import android.net.RouteInfo; +import android.net.ip.IpServer; import android.net.util.InterfaceSet; import android.net.util.PrefixUtils; import android.net.util.SharedLog; @@ -89,15 +82,12 @@ import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; import android.os.Parcel; -import android.os.PersistableBundle; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.os.UserManagerInternal.UserRestrictionsListener; -import android.provider.Settings; -import android.telephony.CarrierConfigManager; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; @@ -113,6 +103,7 @@ import com.android.internal.util.Protocol; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.server.LocalServices; +import com.android.server.connectivity.tethering.EntitlementManager; import com.android.server.connectivity.tethering.IPv6TetheringCoordinator; import com.android.server.connectivity.tethering.OffloadController; import com.android.server.connectivity.tethering.TetheringConfiguration; @@ -123,8 +114,6 @@ import com.android.server.net.BaseNetworkObserver; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.net.Inet4Address; -import java.net.Inet6Address; import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; @@ -145,18 +134,12 @@ public class Tethering extends BaseNetworkObserver { private final static boolean DBG = false; private final static boolean VDBG = false; - protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning"; - private static final Class[] messageClasses = { Tethering.class, TetherMasterSM.class, IpServer.class }; private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(messageClasses); - // {@link ComponentName} of the Service used to run tether provisioning. - private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources - .getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable)); - private static class TetherState { public final IpServer ipServer; public int lastState; @@ -191,7 +174,6 @@ public class Tethering extends BaseNetworkObserver { private final INetworkStatsService mStatsService; private final INetworkPolicyManager mPolicyManager; private final Looper mLooper; - private final MockableSystemProperties mSystemProperties; private final StateMachine mTetherMasterSM; private final OffloadController mOffloadController; private final UpstreamNetworkMonitor mUpstreamNetworkMonitor; @@ -200,6 +182,7 @@ public class Tethering extends BaseNetworkObserver { private final HashSet<IpServer> mForwardedDownstreams; private final VersionedBroadcastListener mCarrierConfigChange; private final TetheringDependencies mDeps; + private final EntitlementManager mEntitlementMgr; private volatile TetheringConfiguration mConfig; private InterfaceSet mCurrentUpstreamIfaceSet; @@ -220,7 +203,6 @@ public class Tethering extends BaseNetworkObserver { mStatsService = statsService; mPolicyManager = policyManager; mLooper = looper; - mSystemProperties = systemProperties; mDeps = deps; mPublicSync = new Object(); @@ -241,12 +223,13 @@ public class Tethering extends BaseNetworkObserver { IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_CARRIER_CONFIG_CHANGED); + mEntitlementMgr = mDeps.getEntitlementManager(mContext, mLog, systemProperties); mCarrierConfigChange = new VersionedBroadcastListener( "CarrierConfigChangeListener", mContext, smHandler, filter, (Intent ignored) -> { mLog.log("OBSERVED carrier config change"); updateConfiguration(); - reevaluateSimCardProvisioning(); + mEntitlementMgr.reevaluateSimCardProvisioning(); }); mStateReceiver = new StateReceiver(); @@ -289,6 +272,7 @@ public class Tethering extends BaseNetworkObserver { private void updateConfiguration() { mConfig = new TetheringConfiguration(mContext, mLog); mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired); + mEntitlementMgr.updateConfiguration(mConfig); } private void maybeUpdateConfiguration() { @@ -354,61 +338,32 @@ public class Tethering extends BaseNetworkObserver { } public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) { - if (!isTetherProvisioningRequired()) { + mEntitlementMgr.startTethering(type); + if (!mEntitlementMgr.isTetherProvisioningRequired()) { enableTetheringInternal(type, true, receiver); return; } + final ResultReceiver proxyReceiver = getProxyReceiver(type, receiver); if (showProvisioningUi) { - runUiTetherProvisioningAndEnable(type, receiver); + mEntitlementMgr.runUiTetherProvisioningAndEnable(type, proxyReceiver); } else { - runSilentTetherProvisioningAndEnable(type, receiver); + mEntitlementMgr.runSilentTetherProvisioningAndEnable(type, proxyReceiver); } } public void stopTethering(int type) { enableTetheringInternal(type, false, null); - if (isTetherProvisioningRequired()) { - cancelTetherProvisioningRechecks(type); - } - } - - /** - * Check if the device requires a provisioning check in order to enable tethering. - * - * @return a boolean - {@code true} indicating tether provisioning is required by the carrier. - */ - @VisibleForTesting - protected boolean isTetherProvisioningRequired() { - final TetheringConfiguration cfg = mConfig; - if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false) - || cfg.provisioningApp.length == 0) { - return false; - } - if (carrierConfigAffirmsEntitlementCheckNotRequired()) { - return false; + mEntitlementMgr.stopTethering(type); + if (mEntitlementMgr.isTetherProvisioningRequired()) { + // There are lurking bugs where the notion of "provisioning required" or + // "tethering supported" may change without notifying tethering properly, then + // tethering can't shutdown correctly. + // TODO: cancel re-check all the time + if (mDeps.isTetheringSupported()) { + mEntitlementMgr.cancelTetherProvisioningRechecks(type); + } } - return (cfg.provisioningApp.length == 2); - } - - // The logic here is aimed solely at confirming that a CarrierConfig exists - // and affirms that entitlement checks are not required. - // - // TODO: find a better way to express this, or alter the checking process - // entirely so that this is more intuitive. - private boolean carrierConfigAffirmsEntitlementCheckNotRequired() { - // Check carrier config for entitlement checks - final CarrierConfigManager configManager = (CarrierConfigManager) mContext - .getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (configManager == null) return false; - - final PersistableBundle carrierConfig = configManager.getConfig(); - if (carrierConfig == null) return false; - - // A CarrierConfigManager was found and it has a config. - final boolean isEntitlementCheckRequired = carrierConfig.getBoolean( - CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL); - return !isEntitlementCheckRequired; } /** @@ -417,20 +372,20 @@ public class Tethering extends BaseNetworkObserver { * for the specified interface. */ private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) { - boolean isProvisioningRequired = enable && isTetherProvisioningRequired(); + boolean isProvisioningRequired = enable && mEntitlementMgr.isTetherProvisioningRequired(); int result; switch (type) { case TETHERING_WIFI: result = setWifiTethering(enable); if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) { - scheduleProvisioningRechecks(type); + mEntitlementMgr.scheduleProvisioningRechecks(type); } sendTetherResult(receiver, result); break; case TETHERING_USB: result = setUsbTethering(enable); if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) { - scheduleProvisioningRechecks(type); + mEntitlementMgr.scheduleProvisioningRechecks(type); } sendTetherResult(receiver, result); break; @@ -489,32 +444,14 @@ public class Tethering extends BaseNetworkObserver { ? TETHER_ERROR_NO_ERROR : TETHER_ERROR_MASTER_ERROR; sendTetherResult(receiver, result); - if (enable && isTetherProvisioningRequired()) { - scheduleProvisioningRechecks(TETHERING_BLUETOOTH); + if (enable && mEntitlementMgr.isTetherProvisioningRequired()) { + mEntitlementMgr.scheduleProvisioningRechecks(TETHERING_BLUETOOTH); } adapter.closeProfileProxy(BluetoothProfile.PAN, proxy); } }, BluetoothProfile.PAN); } - private void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) { - ResultReceiver proxyReceiver = getProxyReceiver(type, receiver); - sendUiTetherProvisionIntent(type, proxyReceiver); - } - - private void sendUiTetherProvisionIntent(int type, ResultReceiver receiver) { - Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING); - intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); - intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - final long ident = Binder.clearCallingIdentity(); - try { - mContext.startActivityAsUser(intent, UserHandle.CURRENT); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - /** * Creates a proxy {@link ResultReceiver} which enables tethering if the provisioning result * is successful before firing back up to the wrapped receiver. @@ -546,62 +483,6 @@ public class Tethering extends BaseNetworkObserver { return receiverForSending; } - private void scheduleProvisioningRechecks(int type) { - Intent intent = new Intent(); - intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); - intent.putExtra(EXTRA_SET_ALARM, true); - intent.setComponent(TETHER_SERVICE); - final long ident = Binder.clearCallingIdentity(); - try { - mContext.startServiceAsUser(intent, UserHandle.CURRENT); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - private void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) { - ResultReceiver proxyReceiver = getProxyReceiver(type, receiver); - sendSilentTetherProvisionIntent(type, proxyReceiver); - } - - private void sendSilentTetherProvisionIntent(int type, ResultReceiver receiver) { - Intent intent = new Intent(); - intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); - intent.putExtra(EXTRA_RUN_PROVISION, true); - intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); - intent.setComponent(TETHER_SERVICE); - final long ident = Binder.clearCallingIdentity(); - try { - mContext.startServiceAsUser(intent, UserHandle.CURRENT); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - private void cancelTetherProvisioningRechecks(int type) { - if (mDeps.isTetheringSupported()) { - Intent intent = new Intent(); - intent.putExtra(EXTRA_REM_TETHER_TYPE, type); - intent.setComponent(TETHER_SERVICE); - final long ident = Binder.clearCallingIdentity(); - try { - mContext.startServiceAsUser(intent, UserHandle.CURRENT); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - // Used by the SIM card change observation code. - // TODO: De-duplicate with above code, where possible. - private void startProvisionIntent(int tetherType) { - final Intent startProvIntent = new Intent(); - startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType); - startProvIntent.putExtra(EXTRA_RUN_PROVISION, true); - startProvIntent.setComponent(TETHER_SERVICE); - mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT); - } - public int tether(String iface) { return tether(iface, IpServer.STATE_TETHERED); } @@ -1166,30 +1047,6 @@ public class Tethering extends BaseNetworkObserver { return false; } - private void reevaluateSimCardProvisioning() { - if (!mConfig.hasMobileHotspotProvisionApp()) return; - if (carrierConfigAffirmsEntitlementCheckNotRequired()) return; - - ArrayList<Integer> tethered = new ArrayList<>(); - synchronized (mPublicSync) { - for (int i = 0; i < mTetherStates.size(); i++) { - TetherState tetherState = mTetherStates.valueAt(i); - if (tetherState.lastState != IpServer.STATE_TETHERED) { - continue; // Skip interfaces that aren't tethered. - } - String iface = mTetherStates.keyAt(i); - int interfaceType = ifaceNameToType(iface); - if (interfaceType != TETHERING_INVALID) { - tethered.add(interfaceType); - } - } - } - - for (int tetherType : tethered) { - startProvisionIntent(tetherType); - } - } - class TetherMasterSM extends StateMachine { private static final int BASE_MASTER = Protocol.BASE_TETHERING; // an interface SM has requested Tethering/Local Hotspot diff --git a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java new file mode 100644 index 000000000000..a4e3e1d85bcb --- /dev/null +++ b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java @@ -0,0 +1,224 @@ +/* + * 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.connectivity.tethering; + +import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE; +import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK; +import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE; +import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION; +import static android.net.ConnectivityManager.EXTRA_SET_ALARM; + +import static com.android.internal.R.string.config_wifi_tether_enable; + +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.net.util.SharedLog; +import android.os.Binder; +import android.os.PersistableBundle; +import android.os.ResultReceiver; +import android.os.UserHandle; +import android.provider.Settings; +import android.telephony.CarrierConfigManager; +import android.util.ArraySet; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.connectivity.MockableSystemProperties; + +/** + * This class encapsulates entitlement/provisioning mechanics + * provisioning check only applies to the use of the mobile network as an upstream + * + * @hide + */ +public class EntitlementManager { + private static final String TAG = EntitlementManager.class.getSimpleName(); + + // {@link ComponentName} of the Service used to run tether provisioning. + private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString( + Resources.getSystem().getString(config_wifi_tether_enable)); + protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning"; + + // The ArraySet contains enabled downstream types, ex: + // {@link ConnectivityManager.TETHERING_WIFI} + // {@link ConnectivityManager.TETHERING_USB} + // {@link ConnectivityManager.TETHERING_BLUETOOTH} + @GuardedBy("mCurrentTethers") + private final ArraySet<Integer> mCurrentTethers; + private final Context mContext; + private final MockableSystemProperties mSystemProperties; + private final SharedLog mLog; + @Nullable + private TetheringConfiguration mConfig; + + public EntitlementManager(Context ctx, SharedLog log, + MockableSystemProperties systemProperties) { + mContext = ctx; + mLog = log; + mCurrentTethers = new ArraySet<Integer>(); + mSystemProperties = systemProperties; + } + + /** + * Pass a new TetheringConfiguration instance each time when + * Tethering#updateConfiguration() is called. + */ + public void updateConfiguration(TetheringConfiguration conf) { + mConfig = conf; + } + + /** + * Tell EntitlementManager that a given type of tethering has been enabled + * + * @param type Tethering type + */ + public void startTethering(int type) { + synchronized (mCurrentTethers) { + mCurrentTethers.add(type); + } + } + + /** + * Tell EntitlementManager that a given type of tethering has been disabled + * + * @param type Tethering type + */ + public void stopTethering(int type) { + synchronized (mCurrentTethers) { + mCurrentTethers.remove(type); + } + } + + /** + * Check if the device requires a provisioning check in order to enable tethering. + * + * @return a boolean - {@code true} indicating tether provisioning is required by the carrier. + */ + @VisibleForTesting + public boolean isTetherProvisioningRequired() { + if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false) + || mConfig.provisioningApp.length == 0) { + return false; + } + if (carrierConfigAffirmsEntitlementCheckNotRequired()) { + return false; + } + return (mConfig.provisioningApp.length == 2); + } + + /** + * Re-check tethering provisioning for enabled downstream tether types. + * Reference ConnectivityManager.TETHERING_{@code *} for each tether type. + */ + public void reevaluateSimCardProvisioning() { + if (!mConfig.hasMobileHotspotProvisionApp()) return; + if (carrierConfigAffirmsEntitlementCheckNotRequired()) return; + + final ArraySet<Integer> reevaluateType; + synchronized (mCurrentTethers) { + reevaluateType = new ArraySet<Integer>(mCurrentTethers); + } + for (Integer type : reevaluateType) { + startProvisionIntent(type); + } + } + + // The logic here is aimed solely at confirming that a CarrierConfig exists + // and affirms that entitlement checks are not required. + // + // TODO: find a better way to express this, or alter the checking process + // entirely so that this is more intuitive. + private boolean carrierConfigAffirmsEntitlementCheckNotRequired() { + // Check carrier config for entitlement checks + final CarrierConfigManager configManager = (CarrierConfigManager) mContext + .getSystemService(Context.CARRIER_CONFIG_SERVICE); + if (configManager == null) return false; + + final PersistableBundle carrierConfig = configManager.getConfig(); + if (carrierConfig == null) return false; + + // A CarrierConfigManager was found and it has a config. + final boolean isEntitlementCheckRequired = carrierConfig.getBoolean( + CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL); + return !isEntitlementCheckRequired; + } + + public void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) { + Intent intent = new Intent(); + intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); + intent.putExtra(EXTRA_RUN_PROVISION, true); + intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); + intent.setComponent(TETHER_SERVICE); + final long ident = Binder.clearCallingIdentity(); + try { + mContext.startServiceAsUser(intent, UserHandle.CURRENT); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + public void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) { + Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING); + intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); + intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + final long ident = Binder.clearCallingIdentity(); + try { + mContext.startActivityAsUser(intent, UserHandle.CURRENT); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + // Used by the SIM card change observation code. + // TODO: De-duplicate with above code, where possible. + private void startProvisionIntent(int tetherType) { + final Intent startProvIntent = new Intent(); + startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType); + startProvIntent.putExtra(EXTRA_RUN_PROVISION, true); + startProvIntent.setComponent(TETHER_SERVICE); + mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT); + } + + public void scheduleProvisioningRechecks(int type) { + Intent intent = new Intent(); + intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); + intent.putExtra(EXTRA_SET_ALARM, true); + intent.setComponent(TETHER_SERVICE); + final long ident = Binder.clearCallingIdentity(); + try { + mContext.startServiceAsUser(intent, UserHandle.CURRENT); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + public void cancelTetherProvisioningRechecks(int type) { + Intent intent = new Intent(); + intent.putExtra(EXTRA_REM_TETHER_TYPE, type); + intent.setComponent(TETHER_SERVICE); + final long ident = Binder.clearCallingIdentity(); + try { + mContext.startServiceAsUser(intent, UserHandle.CURRENT); + } finally { + Binder.restoreCallingIdentity(ident); + } + } +} diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java index 8b4006916736..d56b167b9a75 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java @@ -17,19 +17,13 @@ package com.android.server.connectivity.tethering; import android.content.Context; -import android.net.INetd; import android.net.NetworkRequest; -import android.net.dhcp.DhcpServer; -import android.net.dhcp.DhcpServingParams; import android.net.ip.IpServer; -import android.net.ip.RouterAdvertisementDaemon; -import android.net.util.InterfaceParams; -import android.net.util.NetdService; -import android.os.Handler; import android.net.util.SharedLog; -import android.os.Looper; +import android.os.Handler; import com.android.internal.util.StateMachine; +import com.android.server.connectivity.MockableSystemProperties; import java.util.ArrayList; @@ -65,4 +59,9 @@ public class TetheringDependencies { public NetworkRequest getDefaultNetworkRequest() { return null; } + + public EntitlementManager getEntitlementManager(Context ctx, SharedLog log, + MockableSystemProperties systemProperties) { + return new EntitlementManager(ctx, log, systemProperties); + } } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index e70460aef3e0..d04fa237a599 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -247,9 +247,6 @@ public final class DisplayManagerService extends SystemService { // device). private Point mStableDisplaySize = new Point(); - // Whether the system has finished booting or not. - private boolean mSystemReady; - // The top inset of the default display. // This gets persisted so that the boot animation knows how to transition from the display's // full size to the size configured by the user. Right now we only persist and animate the top @@ -322,8 +319,6 @@ public final class DisplayManagerService extends SystemService { PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting(); mCurrentUserId = UserHandle.USER_SYSTEM; - - mSystemReady = false; } public void setupSchedulerPolicies() { @@ -413,10 +408,6 @@ public final class DisplayManagerService extends SystemService { synchronized (mSyncRoot) { mSafeMode = safeMode; mOnlyCore = onlyCore; - mSystemReady = true; - // Just in case the top inset changed before the system was ready. At this point, any - // relevant configuration should be in place. - recordTopInsetLocked(mLogicalDisplays.get(Display.DEFAULT_DISPLAY)); } mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS); @@ -1065,10 +1056,7 @@ public final class DisplayManagerService extends SystemService { } private void recordTopInsetLocked(@Nullable LogicalDisplay d) { - // We must only persist the inset after boot has completed, otherwise we will end up - // overwriting the persisted value before the masking flag has been loaded from the - // resource overlay. - if (!mSystemReady || d == null) { + if (d == null) { return; } int topInset = d.getInsets().top; diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 6f726e605d5e..95665989cc34 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -57,8 +57,6 @@ import java.util.Objects; * </p> */ final class LogicalDisplay { - private static final String PROP_MASKING_INSET_TOP = "persist.sys.displayinset.top"; - private final DisplayInfo mBaseDisplayInfo = new DisplayInfo(); // The layer stack we use when the display has been blanked to prevent any diff --git a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java index aac83b62b0a5..6fe632459eaa 100644 --- a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java +++ b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java @@ -19,6 +19,8 @@ import android.annotation.NonNull; import android.annotation.UserIdInt; import android.os.Bundle; import android.os.IBinder; +import android.view.autofill.AutofillId; +import android.view.autofill.IAutoFillManagerClient; /** * Intelligence Manager local system service interface. @@ -41,4 +43,37 @@ public abstract class IntelligenceManagerInternal { */ public abstract boolean sendActivityAssistData(@UserIdInt int userId, @NonNull IBinder activityToken, @NonNull Bundle data); + + /** + * Asks the intelligence service to provide Augmented Autofill for a given activity. + * + * @param userId user handle + * @param client binder used to communicate with the activity that originated this request. + * @param activityToken activity that originated this request. + * @param autofillSessionId autofill session id (must be used on {@code client} calls. + * @param focusedId id of the the field that triggered this request. + * + * @return {@code false} if the service cannot handle this request, {@code true} otherwise. + * <b>NOTE: </b> it must return right away; typically it will return {@code false} if the + * service is disabled (or the activity blacklisted). + */ + public abstract AugmentedAutofillCallback requestAutofill(@UserIdInt int userId, + @NonNull IAutoFillManagerClient client, @NonNull IBinder activityToken, + int autofillSessionId, @NonNull AutofillId focusedId); + + /** + * Callback used by the Autofill Session to communicate with the Augmented Autofill service. + */ + public interface AugmentedAutofillCallback { + // TODO(b/111330312): this method is calling when the Autofill session is destroyed, the + // main reason being the cases where user tap HOME. + // Right now it's completely destroying the UI, but we need to decide whether / how to + // properly recover it later (for example, if the user switches back to the activity, + // should it be restored? Right not it kind of is, because Autofill's Session trigger a + // new FillRequest, which in turn triggers the Augmented Autofill request again) + /** + * Destroys the Autofill UI. + */ + void destroy(); + } } diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 9e6e3812df6b..d5e4681a0d90 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -97,8 +97,8 @@ import java.util.Properties; * * {@hide} */ -public class GnssLocationProvider implements LocationProviderInterface, InjectNtpTimeCallback, - GnssSatelliteBlacklistCallback { +public class GnssLocationProvider extends LocationProviderInterface + implements InjectNtpTimeCallback, GnssSatelliteBlacklistCallback { private static final String TAG = "GnssLocationProvider"; diff --git a/services/core/java/com/android/server/location/LocationProviderInterface.java b/services/core/java/com/android/server/location/LocationProviderInterface.java index 6f0923233e19..678596445fe3 100644 --- a/services/core/java/com/android/server/location/LocationProviderInterface.java +++ b/services/core/java/com/android/server/location/LocationProviderInterface.java @@ -16,33 +16,63 @@ package com.android.server.location; -import java.io.FileDescriptor; -import java.io.PrintWriter; +import android.location.LocationProvider; +import android.os.Bundle; +import android.os.WorkSource; import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; - -import android.os.Bundle; -import android.os.WorkSource; +import java.io.FileDescriptor; +import java.io.PrintWriter; /** * Location Manager's interface for location providers. * @hide */ -public interface LocationProviderInterface { - public String getName(); +public abstract class LocationProviderInterface { + + /** Get name. */ + public abstract String getName(); + + /** Enable. */ + public abstract void enable(); + + /** Disable. */ + public abstract void disable(); + + /** Is enabled. */ + public abstract boolean isEnabled(); + + /** Set request. */ + public abstract void setRequest(ProviderRequest request, WorkSource source); + + /** dump. */ + public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args); + + /** Get properties. */ + public abstract ProviderProperties getProperties(); - public void enable(); - public void disable(); - public boolean isEnabled(); - public void setRequest(ProviderRequest request, WorkSource source); + /** + * Get status. + * + * @deprecated Will be removed in a future release. + */ + @Deprecated + public int getStatus(Bundle extras) { + return LocationProvider.AVAILABLE; + } - public void dump(FileDescriptor fd, PrintWriter pw, String[] args); + /** + * Get status update time. + * + * @deprecated Will be removed in a future release. + */ + @Deprecated + public long getStatusUpdateTime() { + return 0; + } - // --- deprecated (but still supported) --- - public ProviderProperties getProperties(); - public int getStatus(Bundle extras); - public long getStatusUpdateTime(); - public boolean sendExtraCommand(String command, Bundle extras); + /** Send extra command. */ + public abstract boolean sendExtraCommand(String command, Bundle extras); } diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java index bb86b4852fd5..b40841467946 100644 --- a/services/core/java/com/android/server/location/LocationProviderProxy.java +++ b/services/core/java/com/android/server/location/LocationProviderProxy.java @@ -41,7 +41,7 @@ import java.io.PrintWriter; /** * Proxy for ILocationProvider implementations. */ -public class LocationProviderProxy implements LocationProviderInterface { +public class LocationProviderProxy extends LocationProviderInterface { private static final String TAG = "LocationProviderProxy"; private static final boolean D = LocationManagerService.D; diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java index 857876189c55..145aee3a9e6a 100644 --- a/services/core/java/com/android/server/location/MockProvider.java +++ b/services/core/java/com/android/server/location/MockProvider.java @@ -25,32 +25,32 @@ import android.os.WorkSource; import android.util.Log; import android.util.PrintWriterPrinter; +import com.android.internal.location.ProviderProperties; +import com.android.internal.location.ProviderRequest; import java.io.FileDescriptor; import java.io.PrintWriter; -import com.android.internal.location.ProviderProperties; -import com.android.internal.location.ProviderRequest; - /** * A mock location provider used by LocationManagerService to implement test providers. * * {@hide} */ -public class MockProvider implements LocationProviderInterface { +public class MockProvider extends LocationProviderInterface { private final String mName; private final ProviderProperties mProperties; private final ILocationManager mLocationManager; private final Location mLocation; - private final Bundle mExtras = new Bundle(); - private int mStatus; - private long mStatusUpdateTime; private boolean mHasLocation; - private boolean mHasStatus; private boolean mEnabled; + + private int mStatus; + private long mStatusUpdateTime; + private Bundle mExtras; + private static final String TAG = "MockProvider"; public MockProvider(String name, ILocationManager locationManager, @@ -61,6 +61,10 @@ public class MockProvider implements LocationProviderInterface { mLocationManager = locationManager; mProperties = properties; mLocation = new Location(name); + + mStatus = LocationProvider.AVAILABLE; + mStatusUpdateTime = 0L; + mExtras = null; } @Override @@ -90,13 +94,12 @@ public class MockProvider implements LocationProviderInterface { @Override public int getStatus(Bundle extras) { - if (mHasStatus) { + if (mExtras != null) { extras.clear(); extras.putAll(mExtras); - return mStatus; - } else { - return LocationProvider.AVAILABLE; } + + return mStatus; } @Override @@ -120,19 +123,14 @@ public class MockProvider implements LocationProviderInterface { mHasLocation = false; } + /** + * @deprecated Will be removed in a future release. + */ + @Deprecated public void setStatus(int status, Bundle extras, long updateTime) { mStatus = status; mStatusUpdateTime = updateTime; - mExtras.clear(); - if (extras != null) { - mExtras.putAll(extras); - } - mHasStatus = true; - } - - public void clearStatus() { - mHasStatus = false; - mStatusUpdateTime = 0; + mExtras = extras; } @Override @@ -145,9 +143,6 @@ public class MockProvider implements LocationProviderInterface { pw.println(prefix + "mHasLocation=" + mHasLocation); pw.println(prefix + "mLocation:"); mLocation.dump(new PrintWriterPrinter(pw), prefix + " "); - pw.println(prefix + "mHasStatus=" + mHasStatus); - pw.println(prefix + "mStatus=" + mStatus); - pw.println(prefix + "mStatusUpdateTime=" + mStatusUpdateTime); pw.println(prefix + "mExtras=" + mExtras); } diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java index 71bae07680a3..99c92149fa1b 100644 --- a/services/core/java/com/android/server/location/PassiveProvider.java +++ b/services/core/java/com/android/server/location/PassiveProvider.java @@ -16,22 +16,20 @@ package com.android.server.location; -import java.io.FileDescriptor; -import java.io.PrintWriter; - -import com.android.internal.location.ProviderProperties; -import com.android.internal.location.ProviderRequest; - import android.location.Criteria; import android.location.ILocationManager; import android.location.Location; import android.location.LocationManager; -import android.location.LocationProvider; import android.os.Bundle; import android.os.RemoteException; import android.os.WorkSource; import android.util.Log; +import com.android.internal.location.ProviderProperties; +import com.android.internal.location.ProviderRequest; + +import java.io.FileDescriptor; +import java.io.PrintWriter; /** * A passive location provider reports locations received from other providers @@ -40,7 +38,7 @@ import android.util.Log; * * {@hide} */ -public class PassiveProvider implements LocationProviderInterface { +public class PassiveProvider extends LocationProviderInterface { private static final String TAG = "PassiveProvider"; private static final ProviderProperties PROPERTIES = new ProviderProperties( @@ -78,20 +76,6 @@ public class PassiveProvider implements LocationProviderInterface { } @Override - public int getStatus(Bundle extras) { - if (mReportLocation) { - return LocationProvider.AVAILABLE; - } else { - return LocationProvider.TEMPORARILY_UNAVAILABLE; - } - } - - @Override - public long getStatusUpdateTime() { - return -1; - } - - @Override public void setRequest(ProviderRequest request, WorkSource source) { mReportLocation = request.reportLocation; } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 042f74a09b13..2048d5fc9890 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4401,19 +4401,20 @@ public class NotificationManagerService extends SystemService { * * Has side effects. */ - private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag, + private boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag, NotificationRecord r, boolean isAutogroup) { final String pkg = r.sbn.getPackageName(); final boolean isSystemNotification = - isUidSystemOrPhone(callingUid) || ("android".equals(pkg)); + isUidSystemOrPhone(uid) || ("android".equals(pkg)); final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg); // Limit the number of notifications that any given package except the android // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks. if (!isSystemNotification && !isNotificationFromListener) { synchronized (mNotificationLock) { + final int callingUid = Binder.getCallingUid(); if (mNotificationsByKey.get(r.sbn.getKey()) == null - && isCallerInstantApp(pkg, Binder.getCallingUid(), userId)) { + && isCallerInstantApp(callingUid, userId)) { // Ephemeral apps have some special constraints for notifications. // They are not allowed to create new notifications however they are allowed to // update notifications created by the system (e.g. a foreground service @@ -6524,24 +6525,28 @@ public class NotificationManagerService extends SystemService { } @VisibleForTesting - boolean isCallerInstantApp(String pkg, int callingUid, int userId) { + boolean isCallerInstantApp(int callingUid, int userId) { // System is always allowed to act for ephemeral apps. if (isUidSystemOrPhone(callingUid)) { return false; } - mAppOps.checkPackage(callingUid, pkg); - try { + final String[] pkgs = mPackageManager.getPackagesForUid(callingUid); + if (pkgs == null) { + throw new SecurityException("Unknown uid " + callingUid); + } + final String pkg = pkgs[0]; + mAppOps.checkPackage(callingUid, pkg); + ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0, userId); if (ai == null) { throw new SecurityException("Unknown package " + pkg); } return ai.isInstantApp(); } catch (RemoteException re) { - throw new SecurityException("Unknown package " + pkg, re); + throw new SecurityException("Unknown uid " + callingUid, re); } - } private void checkCallerIsSameApp(String pkg) { diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index f1b03d1fc9d6..d4719049e426 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -22,14 +22,11 @@ import static android.content.Intent.ACTION_PACKAGE_CHANGED; import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static android.content.Intent.ACTION_USER_ADDED; import static android.content.Intent.ACTION_USER_REMOVED; -import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES; -import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.content.pm.PackageManager.SIGNATURE_MATCH; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; -import android.app.ActivityThread; import android.app.IActivityManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -37,7 +34,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.om.IOverlayManager; import android.content.om.OverlayInfo; -import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManagerInternal; @@ -59,11 +55,9 @@ import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; -import com.android.internal.util.ConcurrentUtils; import com.android.server.FgThread; import com.android.server.IoThread; import com.android.server.LocalServices; -import com.android.server.SystemServerInitThreadPool; import com.android.server.SystemService; import com.android.server.pm.Installer; import com.android.server.pm.UserManagerService; @@ -84,8 +78,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -228,8 +220,6 @@ public final class OverlayManagerService extends SystemService { private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false); - private Future<?> mInitCompleteSignal; - public OverlayManagerService(@NonNull final Context context, @NonNull final Installer installer) { super(context); @@ -241,29 +231,28 @@ public final class OverlayManagerService extends SystemService { mSettings = new OverlayManagerSettings(); mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings, getDefaultOverlayPackages(), new OverlayChangeListener()); - mInitCompleteSignal = SystemServerInitThreadPool.get().submit(() -> { - final IntentFilter packageFilter = new IntentFilter(); - packageFilter.addAction(ACTION_PACKAGE_ADDED); - packageFilter.addAction(ACTION_PACKAGE_CHANGED); - packageFilter.addAction(ACTION_PACKAGE_REMOVED); - packageFilter.addDataScheme("package"); - getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, - packageFilter, null, null); - - final IntentFilter userFilter = new IntentFilter(); - userFilter.addAction(ACTION_USER_ADDED); - userFilter.addAction(ACTION_USER_REMOVED); - getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL, - userFilter, null, null); - - restoreSettings(); - - initIfNeeded(); - onSwitchUser(UserHandle.USER_SYSTEM); - - publishBinderService(Context.OVERLAY_SERVICE, mService); - publishLocalService(OverlayManagerService.class, this); - }, "Init OverlayManagerService"); + + final IntentFilter packageFilter = new IntentFilter(); + packageFilter.addAction(ACTION_PACKAGE_ADDED); + packageFilter.addAction(ACTION_PACKAGE_CHANGED); + packageFilter.addAction(ACTION_PACKAGE_REMOVED); + packageFilter.addDataScheme("package"); + getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, + packageFilter, null, null); + + final IntentFilter userFilter = new IntentFilter(); + userFilter.addAction(ACTION_USER_ADDED); + userFilter.addAction(ACTION_USER_REMOVED); + getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL, + userFilter, null, null); + + restoreSettings(); + + initIfNeeded(); + onSwitchUser(UserHandle.USER_SYSTEM); + + publishBinderService(Context.OVERLAY_SERVICE, mService); + publishLocalService(OverlayManagerService.class, this); } @Override @@ -271,32 +260,6 @@ public final class OverlayManagerService extends SystemService { // Intentionally left empty. } - @Override - public void onBootPhase(int phase) { - if (phase == PHASE_SYSTEM_SERVICES_READY && mInitCompleteSignal != null) { - ConcurrentUtils.waitForFutureNoInterrupt(mInitCompleteSignal, - "Wait for OverlayManagerService init"); - mInitCompleteSignal = null; - } - } - - public void updateSystemUiContext() { - if (mInitCompleteSignal != null) { - ConcurrentUtils.waitForFutureNoInterrupt(mInitCompleteSignal, - "Wait for OverlayManagerService init"); - mInitCompleteSignal = null; - } - - final ApplicationInfo ai; - try { - ai = mPackageManager.mPackageManager.getApplicationInfo("android", - GET_SHARED_LIBRARY_FILES, UserHandle.USER_SYSTEM); - } catch (RemoteException e) { - throw e.rethrowAsRuntimeException(); - } - ActivityThread.currentActivityThread().handleSystemApplicationInfoChanged(ai); - } - private void initIfNeeded() { final UserManager um = getContext().getSystemService(UserManager.class); final List<UserInfo> users = um.getUsers(true /*excludeDying*/); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 59c6d0a78e1c..3ba1155d2316 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -20,13 +20,7 @@ import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.Manifest.permission.SYSTEM_ALERT_WINDOW; import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; import static android.app.AppOpsManager.OP_TOAST_WINDOW; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_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.Context.CONTEXT_RESTRICTED; import static android.content.Context.DISPLAY_SERVICE; import static android.content.Context.WINDOW_SERVICE; @@ -35,61 +29,28 @@ import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; import static android.content.pm.PackageManager.FEATURE_WATCH; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.res.Configuration.EMPTY; -import static android.content.res.Configuration.UI_MODE_TYPE_CAR; -import static android.content.res.Configuration.UI_MODE_TYPE_MASK; import static android.os.Build.VERSION_CODES.M; import static android.os.Build.VERSION_CODES.O; import static android.provider.Settings.Secure.VOLUME_HUSH_OFF; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.STATE_OFF; -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.INPUT_CONSUMER_NAVIGATION; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW; -import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON; -import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; -import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN; -import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; -import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; -import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; -import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; -import static android.view.WindowManager.LayoutParams.MATCH_PARENT; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR; -import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; -import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT; -import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; -import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; -import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; -import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; -import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; -import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; @@ -106,19 +67,13 @@ import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE; import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION; import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG; -import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT; import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR; -import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; -import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; -import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; -import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; -import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType; @@ -132,25 +87,15 @@ import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.C import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN; -import static com.android.server.wm.WindowManagerPolicyProto.FOCUSED_APP_TOKEN; -import static com.android.server.wm.WindowManagerPolicyProto.FOCUSED_WINDOW; -import static com.android.server.wm.WindowManagerPolicyProto.FORCE_STATUS_BAR; -import static com.android.server.wm.WindowManagerPolicyProto.FORCE_STATUS_BAR_FROM_KEYGUARD; import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DELEGATE; import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DRAW_COMPLETE; import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_OCCLUDED; import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_OCCLUDED_CHANGED; import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_OCCLUDED_PENDING; -import static com.android.server.wm.WindowManagerPolicyProto.LAST_SYSTEM_UI_FLAGS; -import static com.android.server.wm.WindowManagerPolicyProto.NAVIGATION_BAR; import static com.android.server.wm.WindowManagerPolicyProto.ORIENTATION; -import static com.android.server.wm.WindowManagerPolicyProto.ORIENTATION_LISTENER; import static com.android.server.wm.WindowManagerPolicyProto.ROTATION; import static com.android.server.wm.WindowManagerPolicyProto.ROTATION_MODE; import static com.android.server.wm.WindowManagerPolicyProto.SCREEN_ON_FULLY; -import static com.android.server.wm.WindowManagerPolicyProto.STATUS_BAR; -import static com.android.server.wm.WindowManagerPolicyProto.TOP_FULLSCREEN_OPAQUE_OR_DIMMING_WINDOW; -import static com.android.server.wm.WindowManagerPolicyProto.TOP_FULLSCREEN_OPAQUE_WINDOW; import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW_COMPLETE; import android.annotation.Nullable; @@ -158,12 +103,10 @@ import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityOptions; import android.app.ActivityTaskManager; -import android.app.ActivityThread; import android.app.AppOpsManager; import android.app.IUiModeManager; import android.app.ProgressDialog; import android.app.SearchManager; -import android.app.StatusBarManager; import android.app.UiModeManager; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; @@ -181,15 +124,12 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.database.ContentObserver; import android.graphics.PixelFormat; -import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManager; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPlaybackClient; import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback; -import android.hardware.input.InputManager; import android.hardware.input.InputManagerInternal; -import android.hardware.power.V1_0.PowerHint; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.AudioManagerInternal; @@ -202,7 +142,6 @@ import android.os.FactoryTest; import android.os.Handler; import android.os.IBinder; import android.os.IDeviceIdleController; -import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.PowerManagerInternal; @@ -224,7 +163,6 @@ import android.service.dreams.IDreamManager; import android.service.vr.IPersistentVrStateCallbacks; import android.speech.RecognizerIntent; import android.telecom.TelecomManager; -import android.util.ArraySet; import android.util.EventLog; import android.util.Log; import android.util.LongSparseArray; @@ -234,21 +172,13 @@ import android.util.Slog; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import android.view.Display; -import android.view.DisplayCutout; -import android.view.Gravity; import android.view.HapticFeedbackConstants; -import android.view.IApplicationToken; import android.view.IWindowManager; -import android.view.InputChannel; import android.view.InputDevice; -import android.view.InputEvent; -import android.view.InputEventReceiver; import android.view.KeyCharacterMap; import android.view.KeyCharacterMap.FallbackAction; import android.view.KeyEvent; import android.view.MotionEvent; -import android.view.PointerIcon; -import android.view.Surface; import android.view.View; import android.view.ViewConfiguration; import android.view.WindowManager; @@ -263,8 +193,6 @@ import android.view.autofill.AutofillManagerInternal; import com.android.internal.R; import com.android.internal.accessibility.AccessibilityShortcutController; -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.policy.IKeyguardDismissCallback; @@ -272,7 +200,6 @@ import com.android.internal.policy.IShortcutService; import com.android.internal.policy.PhoneWindow; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.ArrayUtils; -import com.android.internal.util.ScreenShapeHelper; import com.android.internal.util.ScreenshotHelper; import com.android.internal.widget.PointerLocationView; import com.android.server.ExtconStateObserver; @@ -289,19 +216,17 @@ import com.android.server.vr.VrManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal.SleepToken; import com.android.server.wm.AppTransition; -import com.android.server.wm.DisplayFrames; import com.android.server.wm.DisplayPolicy; import com.android.server.wm.DisplayRotation; -import com.android.server.wm.WindowFrames; import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal.AppTransitionListener; -import com.android.server.wm.utils.InsetUtils; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; +import java.util.HashSet; import java.util.List; /** @@ -313,11 +238,9 @@ import java.util.List; */ public class PhoneWindowManager implements WindowManagerPolicy { static final String TAG = "WindowManager"; - static final boolean DEBUG = false; static final boolean localLOGV = false; static final boolean DEBUG_INPUT = false; static final boolean DEBUG_KEYGUARD = false; - static final boolean DEBUG_LAYOUT = false; static final boolean DEBUG_SPLASH_SCREEN = false; static final boolean DEBUG_WAKEUP = false; static final boolean SHOW_SPLASH_SCREENS = true; @@ -329,8 +252,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Whether to allow devices placed in vr headset viewers to have an alternative Home intent. static final boolean ENABLE_VR_HEADSET_HOME_CAPTURE = true; - static final boolean ALTERNATE_CAR_MODE_NAV_SIZE = false; - static final int SHORT_PRESS_POWER_NOTHING = 0; static final int SHORT_PRESS_POWER_GO_TO_SLEEP = 1; static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP = 2; @@ -372,13 +293,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final int PENDING_KEY_NULL = -1; - // Controls navigation bar opacity depending on which workspace stacks are currently - // visible. - // Nav bar is always opaque when either the freeform stack or docked stack is visible. - static final int NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED = 0; - // Nav bar is always translucent when the freeform stack is visible, otherwise always opaque. - static final int NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE = 1; - static public final String SYSTEM_DIALOG_REASON_KEY = "reason"; static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions"; static public final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"; @@ -386,33 +300,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { static public final String SYSTEM_DIALOG_REASON_ASSIST = "assist"; static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot"; - /** - * These are the system UI flags that, when changing, can cause the layout - * of the screen to change. - */ - static final int SYSTEM_UI_CHANGING_LAYOUT = - View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_FULLSCREEN - | View.STATUS_BAR_TRANSLUCENT - | View.NAVIGATION_BAR_TRANSLUCENT - | View.STATUS_BAR_TRANSPARENT - | View.NAVIGATION_BAR_TRANSPARENT; - private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) .build(); - // The panic gesture may become active only after the keyguard is dismissed and the immersive - // app shows again. If that doesn't happen for 30s we drop the gesture. - private static final long PANIC_GESTURE_EXPIRATION = 30000; - - private static final String SYSUI_PACKAGE = "com.android.systemui"; - private static final String SYSUI_SCREENSHOT_SERVICE = - "com.android.systemui.screenshot.TakeScreenshotService"; - private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER = - "com.android.systemui.screenshot.ScreenshotServiceErrorReceiver"; - /** * Keyguard stuff */ @@ -501,16 +393,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private AccessibilityShortcutController mAccessibilityShortcutController; boolean mSafeMode; - private final ArraySet<WindowState> mScreenDecorWindows = new ArraySet<>(); - WindowState mStatusBar = null; - private final int[] mStatusBarHeightForRotation = new int[4]; - WindowState mNavigationBar = null; - @NavigationBarPosition - int mNavigationBarPosition = NAV_BAR_BOTTOM; - int[] mNavigationBarHeightForRotationDefault = new int[4]; - int[] mNavigationBarWidthForRotationDefault = new int[4]; - int[] mNavigationBarHeightForRotationInCarMode = new int[4]; - int[] mNavigationBarWidthForRotationInCarMode = new int[4]; + private WindowState mKeyguardCandidate = null; private LongSparseArray<IShortcutService> mShortcutKeyServices = new LongSparseArray<>(); @@ -591,7 +474,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mShortPressOnSleepBehavior; int mShortPressOnWindowBehavior; boolean mHasSoftInput = false; - boolean mTranslucentDecorEnabled = true; boolean mUseTvRouting; int mVeryLongPressTimeout; boolean mAllowStartActivityForLongPressOnPowerDuringSetup; @@ -600,74 +482,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { private boolean mHandleVolumeKeysInWM; int mPointerLocationMode = 0; // guarded by mLock - - // The windows we were told about in focusChanged. - WindowState mFocusedWindow; - WindowState mLastFocusedWindow; - - IApplicationToken mFocusedApp; - PointerLocationView mPointerLocationView; - // During layout, the layer at which the doc window is placed. - int mDockLayer; - // During layout, this is the layer of the status bar. - int mStatusBarLayer; - int mLastSystemUiFlags; - // Bits that we are in the process of clearing, so we want to prevent - // them from being set by applications until everything has been updated - // to have them clear. - int mResettingSystemUiFlags = 0; - // Bits that we are currently always keeping cleared. - int mForceClearedSystemUiFlags = 0; - int mLastFullscreenStackSysUiFlags; - int mLastDockedStackSysUiFlags; - final Rect mNonDockedStackBounds = new Rect(); - final Rect mDockedStackBounds = new Rect(); - final Rect mLastNonDockedStackBounds = new Rect(); - final Rect mLastDockedStackBounds = new Rect(); - - // What we last reported to system UI about whether the compatibility - // menu needs to be displayed. - boolean mLastFocusNeedsMenu = false; - // If nonzero, a panic gesture was performed at that time in uptime millis and is still pending. - private long mPendingPanicGestureUptime; - - InputConsumer mInputConsumer = null; - - private static final Rect sTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect(); - private static final Rect sTmpRect = new Rect(); - private static final Rect sTmpDockedFrame = new Rect(); - private static final Rect sTmpNavFrame = new Rect(); - private static final Rect sTmpLastParentFrame = new Rect(); - - WindowState mTopFullscreenOpaqueWindowState; - WindowState mTopFullscreenOpaqueOrDimmingWindowState; - WindowState mTopDockedOpaqueWindowState; - WindowState mTopDockedOpaqueOrDimmingWindowState; - boolean mTopIsFullscreen; - boolean mForceStatusBar; - boolean mForceStatusBarFromKeyguard; - private boolean mForceStatusBarTransparent; - int mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED; - boolean mForcingShowNavBar; - int mForcingShowNavBarLayer; - private boolean mPendingKeyguardOccluded; private boolean mKeyguardOccludedChanged; private boolean mNotifyUserActivity; - boolean mShowingDream; - private boolean mLastShowingDream; - boolean mDreamingLockscreen; - boolean mDreamingSleepTokenNeeded; - private boolean mWindowSleepTokenNeeded; - private boolean mLastWindowSleepTokenNeeded; - - @GuardedBy("mHandler") - private SleepToken mWindowSleepToken; - - SleepToken mDreamingSleepToken; SleepToken mScreenOffSleepToken; volatile boolean mKeyguardOccluded; Intent mHomeIntent; @@ -680,10 +500,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mPendingCapsLockToggle; int mMetaState; int mInitialMetaState; - boolean mForceShowSystemBars; // support for activating the lock screen while the screen is on - boolean mAllowLockscreenWhenOn; + private HashSet<Integer> mAllowLockscreenWhenOnDisplays = new HashSet<>(); int mLockScreenTimeout; boolean mLockScreenTimerActive; @@ -704,7 +523,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { Display mDefaultDisplay; DisplayRotation mDefaultDisplayRotation; DisplayPolicy mDefaultDisplayPolicy; - WindowOrientationListener mDefaultOrientationListener; // What we do when the user long presses on home private int mLongPressOnHomeBehavior; @@ -781,10 +599,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { private boolean mAodShowing; - // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed - private NavigationBarExperiments mExperiments = new NavigationBarExperiments(); - // EXPERIMENT END - private static final int MSG_ENABLE_POINTER_LOCATION = 1; private static final int MSG_DISABLE_POINTER_LOCATION = 2; private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3; @@ -798,25 +612,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK = 12; private static final int MSG_POWER_DELAYED_PRESS = 13; private static final int MSG_POWER_LONG_PRESS = 14; - private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 15; - private static final int MSG_REQUEST_TRANSIENT_BARS = 16; - private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 17; - private static final int MSG_BACK_LONG_PRESS = 18; - private static final int MSG_DISPOSE_INPUT_CONSUMER = 19; - private static final int MSG_ACCESSIBILITY_SHORTCUT = 20; - private static final int MSG_BUGREPORT_TV = 21; - private static final int MSG_ACCESSIBILITY_TV = 22; - private static final int MSG_DISPATCH_BACK_KEY_TO_AUTOFILL = 23; - private static final int MSG_SYSTEM_KEY_PRESS = 24; - private static final int MSG_HANDLE_ALL_APPS = 25; - private static final int MSG_LAUNCH_ASSIST = 26; - private static final int MSG_LAUNCH_ASSIST_LONG_PRESS = 27; - private static final int MSG_POWER_VERY_LONG_PRESS = 28; - private static final int MSG_NOTIFY_USER_ACTIVITY = 29; - private static final int MSG_RINGER_TOGGLE_CHORD = 30; - - private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0; - private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1; + private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 15; + private static final int MSG_BACK_LONG_PRESS = 16; + private static final int MSG_ACCESSIBILITY_SHORTCUT = 17; + private static final int MSG_BUGREPORT_TV = 18; + private static final int MSG_ACCESSIBILITY_TV = 19; + private static final int MSG_DISPATCH_BACK_KEY_TO_AUTOFILL = 20; + private static final int MSG_SYSTEM_KEY_PRESS = 21; + private static final int MSG_HANDLE_ALL_APPS = 22; + private static final int MSG_LAUNCH_ASSIST = 23; + private static final int MSG_LAUNCH_ASSIST_LONG_PRESS = 24; + private static final int MSG_POWER_VERY_LONG_PRESS = 25; + private static final int MSG_NOTIFY_USER_ACTIVITY = 26; + private static final int MSG_RINGER_TOGGLE_CHORD = 27; private class PolicyHandler extends Handler { @Override @@ -876,25 +684,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MSG_POWER_VERY_LONG_PRESS: powerVeryLongPress(); break; - case MSG_UPDATE_DREAMING_SLEEP_TOKEN: - updateDreamingSleepToken(msg.arg1 != 0); - break; - case MSG_REQUEST_TRANSIENT_BARS: - WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS) ? - mStatusBar : mNavigationBar; - if (targetBar != null) { - requestTransientBars(targetBar); - } - break; case MSG_SHOW_PICTURE_IN_PICTURE_MENU: showPictureInPictureMenuInternal(); break; case MSG_BACK_LONG_PRESS: backLongPress(); break; - case MSG_DISPOSE_INPUT_CONSUMER: - disposeInputConsumer((InputConsumer) msg.obj); - break; case MSG_ACCESSIBILITY_SHORTCUT: accessibilityShortcutActivated(); break; @@ -966,14 +761,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { Settings.Secure.DEFAULT_INPUT_METHOD), false, this, UserHandle.USER_ALL); resolver.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS), false, this, - UserHandle.USER_ALL); - resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.VOLUME_HUSH_GESTURE), false, this, UserHandle.USER_ALL); - resolver.registerContentObserver(Settings.Global.getUriFor( - Settings.Global.POLICY_CONTROL), false, this, - UserHandle.USER_ALL); resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED), false, this, UserHandle.USER_ALL); @@ -1012,45 +801,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }; - private final StatusBarController mStatusBarController = new StatusBarController(); - - private final BarController mNavigationBarController = new BarController("NavigationBar", - View.NAVIGATION_BAR_TRANSIENT, - View.NAVIGATION_BAR_UNHIDE, - View.NAVIGATION_BAR_TRANSLUCENT, - StatusBarManager.WINDOW_NAVIGATION_BAR, - FLAG_TRANSLUCENT_NAVIGATION, - View.NAVIGATION_BAR_TRANSPARENT); - - private final BarController.OnBarVisibilityChangedListener mNavBarVisibilityListener = - new BarController.OnBarVisibilityChangedListener() { - @Override - public void onBarVisibilityChanged(boolean visible) { - mAccessibilityManager.notifyAccessibilityButtonVisibilityChanged(visible); - } - }; - - private final Runnable mAcquireSleepTokenRunnable = () -> { - if (mWindowSleepToken != null) { - return; - } - mWindowSleepToken = mActivityTaskManagerInternal.acquireSleepToken("WindowSleepToken", - DEFAULT_DISPLAY); - }; - - private final Runnable mReleaseSleepTokenRunnable = () -> { - if (mWindowSleepToken == null) { - return; - } - mWindowSleepToken.release(); - mWindowSleepToken = null; - }; - - private ImmersiveModeConfirmation mImmersiveModeConfirmation; - - @VisibleForTesting - SystemGesturesPointerEventListener mSystemGestures; - private void handleRingerChordGesture() { if (mRingerToggleChord == VOLUME_HUSH_OFF) { return; @@ -1150,14 +900,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mHandler.removeMessages(MSG_POWER_DELAYED_PRESS); } - // Detect user pressing the power button in panic when an application has - // taken over the whole screen. - boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(interactive, - SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags), - isNavBarEmpty(mLastSystemUiFlags)); - if (panic) { - mHandler.post(mHiddenNavPanic); - } + mWindowManagerFuncs.onPowerKeyDown(interactive); // Abort possibly stuck animations. mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe); @@ -1500,12 +1243,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { mAccessibilityShortcutController.performAccessibilityShortcut(); } - private void disposeInputConsumer(InputConsumer inputConsumer) { - if (inputConsumer != null) { - inputConsumer.dismiss(); - } - } - private void sleepPress() { if (mShortPressOnSleepBehavior == SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME) { launchHomeFromHotKey(DEFAULT_DISPLAY, false /* awakenDreams */, @@ -1643,9 +1380,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void run() { - mScreenshotHelper.takeScreenshot(mScreenshotType, - mStatusBar != null && mStatusBar.isVisibleLw(), - mNavigationBar != null && mNavigationBar.isVisibleLw(), mHandler); + mDefaultDisplayPolicy.takeScreenshot(mScreenshotType); } } @@ -1673,7 +1408,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0; } - boolean isUserSetupComplete() { + @Override + public boolean isUserSetupComplete() { boolean isSetupComplete = Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0; if (mHasFeatureLeanback) { @@ -1931,7 +1667,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { mDefaultDisplay = displayContentInfo.getDisplay(); mDefaultDisplayRotation = displayContentInfo.getDisplayRotation(); mDefaultDisplayPolicy = mDefaultDisplayRotation.getDisplayPolicy(); - mDefaultOrientationListener = mDefaultDisplayRotation.getOrientationListener(); } /** {@inheritDoc} */ @@ -2028,8 +1763,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.bool.config_lidControlsScreenLock); mLidControlsSleep = mContext.getResources().getBoolean( com.android.internal.R.bool.config_lidControlsSleep); - mTranslucentDecorEnabled = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_enableTranslucentDecor); mAllowTheaterModeWakeFromKey = mContext.getResources().getBoolean( com.android.internal.R.bool.config_allowTheaterModeWakeFromKey); @@ -2107,76 +1840,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { filter = new IntentFilter(Intent.ACTION_USER_SWITCHED); context.registerReceiver(mMultiuserReceiver, filter); - // monitor for system gestures - // TODO(multi-display): Needs to be display specific. - mSystemGestures = new SystemGesturesPointerEventListener(context, - new SystemGesturesPointerEventListener.Callbacks() { - @Override - public void onSwipeFromTop() { - if (mStatusBar != null) { - requestTransientBars(mStatusBar); - } - } - @Override - public void onSwipeFromBottom() { - if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_BOTTOM) { - requestTransientBars(mNavigationBar); - } - } - @Override - public void onSwipeFromRight() { - if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_RIGHT) { - requestTransientBars(mNavigationBar); - } - } - @Override - public void onSwipeFromLeft() { - if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_LEFT) { - requestTransientBars(mNavigationBar); - } - } - @Override - public void onFling(int duration) { - if (mPowerManagerInternal != null) { - mPowerManagerInternal.powerHint( - PowerHint.INTERACTION, duration); - } - } - @Override - public void onDebug() { - // no-op - } - @Override - public void onDown() { - mDefaultOrientationListener.onTouchStart(); - } - @Override - public void onUpOrCancel() { - mDefaultOrientationListener.onTouchEnd(); - } - @Override - public void onMouseHoverAtTop() { - mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS); - Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS); - msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS; - mHandler.sendMessageDelayed(msg, 500); - } - @Override - public void onMouseHoverAtBottom() { - mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS); - Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS); - msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION; - mHandler.sendMessageDelayed(msg, 500); - } - @Override - public void onMouseLeaveFromEdge() { - mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS); - } - }); - mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext); - //TODO (b/111365687) : make system context per display. - mWindowManagerFuncs.registerPointerEventListener(mSystemGestures, DEFAULT_DISPLAY); - mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE); mLongPressVibePattern = getLongIntArray(mContext.getResources(), com.android.internal.R.array.config_longPressVibePattern); @@ -2199,8 +1862,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { finishedGoingToSleep(WindowManagerPolicy.OFF_BECAUSE_OF_USER); } - mWindowManagerInternal.registerAppTransitionListener( - mStatusBarController.getAppTransitionListener()); mWindowManagerInternal.registerAppTransitionListener(new AppTransitionListener() { @Override public int onAppTransitionStartingLocked(int transit, IBinder openToken, @@ -2255,16 +1916,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE; } - - mNavBarOpacityMode = res.getInteger( - com.android.internal.R.integer.config_navBarOpacityMode); - } - - /** - * @return whether the navigation bar can be hidden, e.g. the device has a navigation bar - */ - private boolean canHideNavigationBar() { - return mDefaultDisplayPolicy.hasNavigationBar(); } public void updateSettings() { @@ -2322,12 +1973,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { mHasSoftInput = hasSoftInput; updateRotation = true; } - if (mImmersiveModeConfirmation != null) { - mImmersiveModeConfirmation.loadSetting(mCurrentUserId); - } - } - synchronized (mWindowManagerFuncs.getWindowManagerLock()) { - PolicyControl.reloadFromSetting(mContext); } if (updateRotation) { updateRotation(true); @@ -2536,84 +2181,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { return mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW) != PERMISSION_GRANTED; } - @Override - public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs, - boolean hasStatusBarServicePermission) { - - final boolean isScreenDecor = (attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0; - if (mScreenDecorWindows.contains(win)) { - if (!isScreenDecor) { - // No longer has the flag set, so remove from the set. - mScreenDecorWindows.remove(win); - } - } else if (isScreenDecor && hasStatusBarServicePermission) { - mScreenDecorWindows.add(win); - } - - switch (attrs.type) { - case TYPE_SYSTEM_OVERLAY: - case TYPE_SECURE_SYSTEM_OVERLAY: - // These types of windows can't receive input events. - attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; - attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; - break; - case TYPE_DREAM: - case TYPE_WALLPAPER: - // Dreams and wallpapers don't have an app window token and can thus not be - // letterboxed. Hence always let them extend under the cutout. - attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - break; - case TYPE_STATUS_BAR: - - // If the Keyguard is in a hidden state (occluded by another window), we force to - // remove the wallpaper and keyguard flag so that any change in-flight after setting - // the keyguard as occluded wouldn't set these flags again. - // See {@link #processKeyguardSetHiddenResultLw}. - if (mKeyguardOccluded) { - attrs.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; - attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; - } - break; - - case TYPE_SCREENSHOT: - attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; - break; - - case TYPE_TOAST: - // While apps should use the dedicated toast APIs to add such windows - // it possible legacy apps to add the window directly. Therefore, we - // make windows added directly by the app behave as a toast as much - // as possible in terms of timeout and animation. - if (attrs.hideTimeoutMilliseconds < 0 - || attrs.hideTimeoutMilliseconds > TOAST_WINDOW_TIMEOUT) { - attrs.hideTimeoutMilliseconds = TOAST_WINDOW_TIMEOUT; - } - attrs.windowAnimations = com.android.internal.R.style.Animation_Toast; - break; - } - - if (attrs.type != TYPE_STATUS_BAR) { - // The status bar is the only window allowed to exhibit keyguard behavior. - attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; - } - } - - private int getImpliedSysUiFlagsForLayout(LayoutParams attrs) { - int impliedFlags = 0; - if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) { - impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; - } - final boolean forceWindowDrawsStatusBarBackground = - (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0; - if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 - || forceWindowDrawsStatusBarBackground - && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT) { - impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; - } - return impliedFlags; - } - void readLidState() { mDefaultDisplayPolicy.setLidState(mWindowManagerFuncs.getLidState()); } @@ -2660,161 +2227,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - public void onOverlayChangedLw(DisplayContentInfo displayContentInfo) { - onConfigurationChanged(displayContentInfo); - } - - @Override - public void onConfigurationChanged(DisplayContentInfo displayContentInfo) { - final DisplayRotation displayRotation = displayContentInfo.getDisplayRotation(); - // TODO(multi-display): Define policy for secondary displays. - if (!displayRotation.isDefaultDisplay) { - return; - } - - final Context uiContext = getSystemUiContext(); - final Resources res = uiContext.getResources(); - final int portraitRotation = displayRotation.getPortraitRotation(); - final int upsideDownRotation = displayRotation.getUpsideDownRotation(); - final int landscapeRotation = displayRotation.getLandscapeRotation(); - final int seascapeRotation = displayRotation.getSeascapeRotation(); - - mStatusBarHeightForRotation[portraitRotation] = - mStatusBarHeightForRotation[upsideDownRotation] = res.getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height_portrait); - mStatusBarHeightForRotation[landscapeRotation] = - mStatusBarHeightForRotation[seascapeRotation] = res.getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height_landscape); - - // Height of the navigation bar when presented horizontally at bottom - mNavigationBarHeightForRotationDefault[portraitRotation] = - mNavigationBarHeightForRotationDefault[upsideDownRotation] = - res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height); - mNavigationBarHeightForRotationDefault[landscapeRotation] = - mNavigationBarHeightForRotationDefault[seascapeRotation] = res.getDimensionPixelSize( - com.android.internal.R.dimen.navigation_bar_height_landscape); - - // Width of the navigation bar when presented vertically along one side - mNavigationBarWidthForRotationDefault[portraitRotation] = - mNavigationBarWidthForRotationDefault[upsideDownRotation] = - mNavigationBarWidthForRotationDefault[landscapeRotation] = - mNavigationBarWidthForRotationDefault[seascapeRotation] = - res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width); - - if (ALTERNATE_CAR_MODE_NAV_SIZE) { - // Height of the navigation bar when presented horizontally at bottom - mNavigationBarHeightForRotationInCarMode[portraitRotation] = - mNavigationBarHeightForRotationInCarMode[upsideDownRotation] = - res.getDimensionPixelSize( - com.android.internal.R.dimen.navigation_bar_height_car_mode); - mNavigationBarHeightForRotationInCarMode[landscapeRotation] = - mNavigationBarHeightForRotationInCarMode[seascapeRotation] = res.getDimensionPixelSize( - com.android.internal.R.dimen.navigation_bar_height_landscape_car_mode); - - // Width of the navigation bar when presented vertically along one side - mNavigationBarWidthForRotationInCarMode[portraitRotation] = - mNavigationBarWidthForRotationInCarMode[upsideDownRotation] = - mNavigationBarWidthForRotationInCarMode[landscapeRotation] = - mNavigationBarWidthForRotationInCarMode[seascapeRotation] = - res.getDimensionPixelSize( - com.android.internal.R.dimen.navigation_bar_width_car_mode); - } - - // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed - mExperiments.onConfigurationChanged(uiContext); - // EXPERIMENT END - } - - @VisibleForTesting - Context getSystemUiContext() { - return ActivityThread.currentActivityThread().getSystemUiContext(); - } - - @Override public int getMaxWallpaperLayer() { return getWindowLayerFromTypeLw(TYPE_STATUS_BAR); } - private int getNavigationBarWidth(int rotation, int uiMode) { - if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { - return mNavigationBarWidthForRotationInCarMode[rotation]; - } else { - return mNavigationBarWidthForRotationDefault[rotation]; - } - } - - @Override - public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode, - int displayId, DisplayCutout displayCutout) { - int width = fullWidth; - // TODO(multi-display): Support navigation bar on secondary displays. - if (displayId == DEFAULT_DISPLAY && mDefaultDisplayPolicy.hasNavigationBar()) { - // For a basic navigation bar, when we are in landscape mode we place - // the navigation bar to the side. - if (mDefaultDisplayPolicy.navigationBarCanMove() && fullWidth > fullHeight) { - width -= getNavigationBarWidth(rotation, uiMode); - } - } - if (displayCutout != null) { - width -= displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight(); - } - return width; - } - - private int getNavigationBarHeight(int rotation, int uiMode) { - if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { - return mNavigationBarHeightForRotationInCarMode[rotation]; - } else { - return mNavigationBarHeightForRotationDefault[rotation]; - } - } - - @Override - public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode, - int displayId, DisplayCutout displayCutout) { - int height = fullHeight; - // TODO(multi-display): Support navigation bar on secondary displays. - if (displayId == DEFAULT_DISPLAY && mDefaultDisplayPolicy.hasNavigationBar()) { - // For a basic navigation bar, when we are in portrait mode we place - // the navigation bar to the bottom. - if (!mDefaultDisplayPolicy.navigationBarCanMove() || fullWidth < fullHeight) { - height -= getNavigationBarHeight(rotation, uiMode); - } - } - if (displayCutout != null) { - height -= displayCutout.getSafeInsetTop() + displayCutout.getSafeInsetBottom(); - } - return height; - } - - @Override - public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode, - int displayId, DisplayCutout displayCutout) { - return getNonDecorDisplayWidth(fullWidth, fullHeight, rotation, uiMode, displayId, - displayCutout); - } - - @Override - public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode, - int displayId, DisplayCutout displayCutout) { - // There is a separate status bar at the top of the display. We don't count that as part - // of the fixed decor, since it can hide; however, for purposes of configurations, - // we do want to exclude it since applications can't generally use that part - // of the screen. - // TODO(multi-display): Support status bars on secondary displays. - if (displayId == DEFAULT_DISPLAY) { - int statusBarHeight = mStatusBarHeightForRotation[rotation]; - if (displayCutout != null) { - // If there is a cutout, it may already have accounted for some part of the status - // bar height. - statusBarHeight = Math.max(0, statusBarHeight - displayCutout.getSafeInsetTop()); - } - return getNonDecorDisplayHeight(fullWidth, fullHeight, rotation, uiMode, displayId, - displayCutout) - statusBarHeight; - } - return fullHeight; - } - @Override public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) { return attrs.type == TYPE_STATUS_BAR; @@ -3054,251 +2470,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { return context.createDisplayContext(targetDisplay); } - /** - * Preflight adding a window to the system. - * - * Currently enforces that three window types are singletons: - * <ul> - * <li>STATUS_BAR_TYPE</li> - * <li>KEYGUARD_TYPE</li> - * </ul> - * - * @param win The window to be added - * @param attrs Information about the window to be added - * - * @return If ok, WindowManagerImpl.ADD_OKAY. If too many singletons, - * WindowManagerImpl.ADD_MULTIPLE_SINGLETON - */ - @Override - public int prepareAddWindowLw(WindowState win, WindowManager.LayoutParams attrs) { - - if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.STATUS_BAR_SERVICE, - "PhoneWindowManager"); - mScreenDecorWindows.add(win); - } - - switch (attrs.type) { - case TYPE_STATUS_BAR: - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.STATUS_BAR_SERVICE, - "PhoneWindowManager"); - if (mStatusBar != null) { - if (mStatusBar.isAlive()) { - return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; - } - } - mStatusBar = win; - mStatusBarController.setWindow(win); - setKeyguardOccludedLw(mKeyguardOccluded, true /* force */); - break; - case TYPE_NAVIGATION_BAR: - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.STATUS_BAR_SERVICE, - "PhoneWindowManager"); - if (mNavigationBar != null) { - if (mNavigationBar.isAlive()) { - return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; - } - } - mNavigationBar = win; - mNavigationBarController.setWindow(win); - mNavigationBarController.setOnBarVisibilityChangedListener( - mNavBarVisibilityListener, true); - if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar); - break; - case TYPE_NAVIGATION_BAR_PANEL: - case TYPE_STATUS_BAR_PANEL: - case TYPE_STATUS_BAR_SUB_PANEL: - case TYPE_VOICE_INTERACTION_STARTING: - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.STATUS_BAR_SERVICE, - "PhoneWindowManager"); - break; - } - return ADD_OKAY; - } - - /** {@inheritDoc} */ - @Override - public void removeWindowLw(WindowState win) { - if (mStatusBar == win) { - mStatusBar = null; - mStatusBarController.setWindow(null); - } else if (mNavigationBar == win) { - mNavigationBar = null; - mNavigationBarController.setWindow(null); - } - if (mLastFocusedWindow == win) { - mLastFocusedWindow = null; - } - mScreenDecorWindows.remove(win); - } - - static final boolean PRINT_ANIM = false; - - /** {@inheritDoc} */ - @Override - public int selectAnimationLw(WindowState win, int transit) { - if (PRINT_ANIM) Log.i(TAG, "selectAnimation in " + win - + ": transit=" + transit); - if (win == mStatusBar) { - final boolean isKeyguard = (win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0; - final boolean expanded = win.getAttrs().height == MATCH_PARENT - && win.getAttrs().width == MATCH_PARENT; - if (isKeyguard || expanded) { - return -1; - } - if (transit == TRANSIT_EXIT - || transit == TRANSIT_HIDE) { - return R.anim.dock_top_exit; - } else if (transit == TRANSIT_ENTER - || transit == TRANSIT_SHOW) { - return R.anim.dock_top_enter; - } - } else if (win == mNavigationBar) { - if (win.getAttrs().windowAnimations != 0) { - return 0; - } - // This can be on either the bottom or the right or the left. - if (mNavigationBarPosition == NAV_BAR_BOTTOM) { - if (transit == TRANSIT_EXIT - || transit == TRANSIT_HIDE) { - if (isKeyguardShowingAndNotOccluded()) { - return R.anim.dock_bottom_exit_keyguard; - } else { - return R.anim.dock_bottom_exit; - } - } else if (transit == TRANSIT_ENTER - || transit == TRANSIT_SHOW) { - return R.anim.dock_bottom_enter; - } - } else if (mNavigationBarPosition == NAV_BAR_RIGHT) { - if (transit == TRANSIT_EXIT - || transit == TRANSIT_HIDE) { - return R.anim.dock_right_exit; - } else if (transit == TRANSIT_ENTER - || transit == TRANSIT_SHOW) { - return R.anim.dock_right_enter; - } - } else if (mNavigationBarPosition == NAV_BAR_LEFT) { - if (transit == TRANSIT_EXIT - || transit == TRANSIT_HIDE) { - return R.anim.dock_left_exit; - } else if (transit == TRANSIT_ENTER - || transit == TRANSIT_SHOW) { - return R.anim.dock_left_enter; - } - } - } else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) { - return selectDockedDividerAnimationLw(win, transit); - } - - if (transit == TRANSIT_PREVIEW_DONE) { - if (win.hasAppShownWindows()) { - if (PRINT_ANIM) Log.i(TAG, "**** STARTING EXIT"); - return com.android.internal.R.anim.app_starting_exit; - } - } else if (win.getAttrs().type == TYPE_DREAM && mDreamingLockscreen - && transit == TRANSIT_ENTER) { - // Special case: we are animating in a dream, while the keyguard - // is shown. We don't want an animation on the dream, because - // we need it shown immediately with the keyguard animating away - // to reveal it. - return -1; - } - - return 0; - } - - private int selectDockedDividerAnimationLw(WindowState win, int transit) { - int insets = mWindowManagerFuncs.getDockedDividerInsetsLw(); - - // 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 0; - } - if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) { - return R.anim.fade_in; - } else if (transit == TRANSIT_EXIT) { - return R.anim.fade_out; - } else { - return 0; - } - } - - @Override - public void selectRotationAnimationLw(int anim[]) { - // If the screen is off or non-interactive, force a jumpcut. - final boolean forceJumpcut = !mDefaultDisplayPolicy.isScreenOnFully() || !okToAnimate(); - if (PRINT_ANIM) Slog.i(TAG, "selectRotationAnimation mTopFullscreen=" - + mTopFullscreenOpaqueWindowState + " rotationAnimation=" - + (mTopFullscreenOpaqueWindowState == null ? - "0" : mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation) - + " forceJumpcut=" + forceJumpcut); - if (forceJumpcut) { - anim[0] = R.anim.rotation_animation_jump_exit; - anim[1] = R.anim.rotation_animation_enter; - return; - } - if (mTopFullscreenOpaqueWindowState != null) { - int animationHint = mTopFullscreenOpaqueWindowState.getRotationAnimationHint(); - if (animationHint < 0 && mTopIsFullscreen) { - animationHint = mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation; - } - switch (animationHint) { - case ROTATION_ANIMATION_CROSSFADE: - case ROTATION_ANIMATION_SEAMLESS: // Crossfade is fallback for seamless. - anim[0] = R.anim.rotation_animation_xfade_exit; - anim[1] = R.anim.rotation_animation_enter; - break; - case ROTATION_ANIMATION_JUMPCUT: - anim[0] = R.anim.rotation_animation_jump_exit; - anim[1] = R.anim.rotation_animation_enter; - break; - case ROTATION_ANIMATION_ROTATE: - default: - anim[0] = anim[1] = 0; - break; - } - } else { - anim[0] = anim[1] = 0; - } - } - - @Override - public boolean validateRotationAnimationLw(int exitAnimId, int enterAnimId, - boolean forceDefault) { - switch (exitAnimId) { - case R.anim.rotation_animation_xfade_exit: - case R.anim.rotation_animation_jump_exit: - // These are the only cases that matter. - if (forceDefault) { - return false; - } - int anim[] = new int[2]; - selectRotationAnimationLw(anim); - return (exitAnimId == anim[0] && enterAnimId == anim[1]); - default: - return true; - } - } - @Override public Animation createHiddenByKeyguardExit(boolean onWallpaper, boolean goingToNotificationShade) { @@ -4173,77 +3344,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - private final Runnable mClearHideNavigationFlag = new Runnable() { - @Override - public void run() { - synchronized (mWindowManagerFuncs.getWindowManagerLock()) { - // Clear flags. - mForceClearedSystemUiFlags &= - ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; - } - mWindowManagerFuncs.reevaluateStatusBarVisibility(); - } - }; - - /** - * Input handler used while nav bar is hidden. Captures any touch on the screen, - * to determine when the nav bar should be shown and prevent applications from - * receiving those touches. - */ - final class HideNavInputEventReceiver extends InputEventReceiver { - public HideNavInputEventReceiver(InputChannel inputChannel, Looper looper) { - super(inputChannel, looper); - } - - @Override - public void onInputEvent(InputEvent event) { - boolean handled = false; - try { - if (event instanceof MotionEvent - && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { - final MotionEvent motionEvent = (MotionEvent)event; - if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { - // When the user taps down, we re-show the nav bar. - boolean changed = false; - synchronized (mWindowManagerFuncs.getWindowManagerLock()) { - if (mInputConsumer == null) { - return; - } - // Any user activity always causes us to show the - // navigation controls, if they had been hidden. - // We also clear the low profile and only content - // flags so that tapping on the screen will atomically - // restore all currently hidden screen decorations. - int newVal = mResettingSystemUiFlags | - View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_LOW_PROFILE | - View.SYSTEM_UI_FLAG_FULLSCREEN; - if (mResettingSystemUiFlags != newVal) { - mResettingSystemUiFlags = newVal; - changed = true; - } - // We don't allow the system's nav bar to be hidden - // again for 1 second, to prevent applications from - // spamming us and keeping it from being shown. - newVal = mForceClearedSystemUiFlags | - View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; - if (mForceClearedSystemUiFlags != newVal) { - mForceClearedSystemUiFlags = newVal; - changed = true; - mHandler.postDelayed(mClearHideNavigationFlag, 1000); - } - } - if (changed) { - mWindowManagerFuncs.reevaluateStatusBarVisibility(); - } - } - } - } finally { - finishInputEvent(event, handled); - } - } - } - @Override public void setRecentsVisibilityLw(boolean visible) { mRecentsVisible = visible; @@ -4259,1177 +3359,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { mNavBarVirtualKeyHapticFeedbackEnabled = enabled; } - @Override - public int adjustSystemUiVisibilityLw(int visibility) { - mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); - mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); - - // Reset any bits in mForceClearingStatusBarVisibility that - // are now clear. - mResettingSystemUiFlags &= visibility; - // Clear any bits in the new visibility that are currently being - // force cleared, before reporting it. - return visibility & ~mResettingSystemUiFlags - & ~mForceClearedSystemUiFlags; - } - - @Override - // TODO: Should probably be moved into DisplayFrames. - public boolean getLayoutHintLw(LayoutParams attrs, Rect taskBounds, - DisplayFrames displayFrames, boolean floatingStack, Rect outFrame, - Rect outContentInsets, Rect outStableInsets, - Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) { - final int fl = PolicyControl.getWindowFlags(null, attrs); - final int pfl = attrs.privateFlags; - final int requestedSysUiVis = PolicyControl.getSystemUiVisibility(null, attrs); - final int sysUiVis = requestedSysUiVis | getImpliedSysUiFlagsForLayout(attrs); - final int displayRotation = displayFrames.mRotation; - - final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl); - if (useOutsets) { - int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources()); - if (outset > 0) { - if (displayRotation == Surface.ROTATION_0) { - outOutsets.bottom += outset; - } else if (displayRotation == Surface.ROTATION_90) { - outOutsets.right += outset; - } else if (displayRotation == Surface.ROTATION_180) { - outOutsets.top += outset; - } else if (displayRotation == Surface.ROTATION_270) { - outOutsets.left += outset; - } - } - } - - final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) != 0; - final boolean layoutInScreenAndInsetDecor = layoutInScreen && - (fl & FLAG_LAYOUT_INSET_DECOR) != 0; - final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0; - - if (layoutInScreenAndInsetDecor && !screenDecor) { - if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) { - outFrame.set(displayFrames.mUnrestricted); - } else { - outFrame.set(displayFrames.mRestricted); - } - - final Rect sf; - if (floatingStack) { - sf = null; - } else { - sf = displayFrames.mStable; - } - - final Rect cf; - if (floatingStack) { - cf = null; - } else if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) { - if ((fl & FLAG_FULLSCREEN) != 0) { - cf = displayFrames.mStableFullscreen; - } else { - cf = displayFrames.mStable; - } - } else if ((fl & FLAG_FULLSCREEN) != 0 || (fl & FLAG_LAYOUT_IN_OVERSCAN) != 0) { - cf = displayFrames.mOverscan; - } else { - cf = displayFrames.mCurrent; - } - - if (taskBounds != null) { - outFrame.intersect(taskBounds); - } - InsetUtils.insetsBetweenFrames(outFrame, cf, outContentInsets); - InsetUtils.insetsBetweenFrames(outFrame, sf, outStableInsets); - outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(outFrame) - .getDisplayCutout()); - return mForceShowSystemBars; - } else { - if (layoutInScreen) { - outFrame.set(displayFrames.mUnrestricted); - } else { - outFrame.set(displayFrames.mStable); - } - if (taskBounds != null) { - outFrame.intersect(taskBounds); - } - - outContentInsets.setEmpty(); - outStableInsets.setEmpty(); - outDisplayCutout.set(DisplayCutout.NO_CUTOUT); - return mForceShowSystemBars; - } - } - - private boolean shouldUseOutsets(WindowManager.LayoutParams attrs, int fl) { - return attrs.type == TYPE_WALLPAPER || (fl & (WindowManager.LayoutParams.FLAG_FULLSCREEN - | WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN)) != 0; - } - /** {@inheritDoc} */ @Override - public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) { - displayFrames.onBeginLayout(); - // TODO(multi-display): This doesn't seem right...Maybe only apply to default display? - mSystemGestures.screenWidth = displayFrames.mUnrestricted.width(); - mSystemGestures.screenHeight = displayFrames.mUnrestricted.height(); - mDockLayer = 0x10000000; - mStatusBarLayer = -1; - - if (displayFrames.mDisplayId == DEFAULT_DISPLAY) { - // For purposes of putting out fake window up to steal focus, we will - // drive nav being hidden only by whether it is requested. - final int sysui = mLastSystemUiFlags; - boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; - boolean navTranslucent = (sysui - & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0; - boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0; - boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; - boolean navAllowedHidden = immersive || immersiveSticky; - navTranslucent &= !immersiveSticky; // transient trumps translucent - boolean isKeyguardShowing = isStatusBarKeyguard() && !mKeyguardOccluded; - if (!isKeyguardShowing) { - navTranslucent &= areTranslucentBarsAllowed(); - } - boolean statusBarForcesShowingNavigation = !isKeyguardShowing && mStatusBar != null - && (mStatusBar.getAttrs().privateFlags - & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0; - - // When the navigation bar isn't visible, we put up a fake input window to catch all - // touch events. This way we can detect when the user presses anywhere to bring back the - // nav bar and ensure the application doesn't see the event. - if (navVisible || navAllowedHidden) { - if (mInputConsumer != null) { - mHandler.sendMessage( - mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer)); - mInputConsumer = null; - } - } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) { - mInputConsumer = mWindowManagerFuncs.createInputConsumer(mHandler.getLooper(), - INPUT_CONSUMER_NAVIGATION, - (channel, looper) -> new HideNavInputEventReceiver(channel, looper), - displayFrames.mDisplayId); - // As long as mInputConsumer is active, hover events are not dispatched to the app - // and the pointer icon is likely to become stale. Hide it to avoid confusion. - InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL); - } - - // For purposes of positioning and showing the nav bar, if we have decided that it can't - // be hidden (because of the screen aspect ratio), then take that into account. - navVisible |= !canHideNavigationBar(); - - boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, navVisible, - navTranslucent, navAllowedHidden, statusBarForcesShowingNavigation); - if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock); - updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui, isKeyguardShowing); - if (updateSysUiVisibility) { - updateSystemUiVisibilityLw(); - } - } - layoutScreenDecorWindows(displayFrames); - - if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) { - // Make sure that the zone we're avoiding for the cutout is at least as tall as the - // status bar; otherwise fullscreen apps will end up cutting halfway into the status - // bar. - displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top, - displayFrames.mStable.top); - } - } - - private void layoutScreenDecorWindows(DisplayFrames displayFrames) { - if (mScreenDecorWindows.isEmpty()) { - return; - } - - sTmpRect.setEmpty(); - sTmpDockedFrame.set(displayFrames.mDock); - - final int displayId = displayFrames.mDisplayId; - final Rect dockFrame = displayFrames.mDock; - final int displayHeight = displayFrames.mDisplayHeight; - final int displayWidth = displayFrames.mDisplayWidth; - - for (int i = mScreenDecorWindows.size() - 1; i >= 0; --i) { - final WindowState w = mScreenDecorWindows.valueAt(i); - if (w.getDisplayId() != displayId || !w.isVisibleLw()) { - // Skip if not on the same display or not visible. - continue; - } - - w.getWindowFrames().setFrames(sTmpDockedFrame /* parentFrame */, - sTmpDockedFrame /* displayFrame */, sTmpDockedFrame /* overscanFrame */, - sTmpDockedFrame /* contentFrame */, sTmpDockedFrame /* visibleFrame */, - sTmpRect /* decorFrame */, sTmpDockedFrame /* stableFrame */, - sTmpDockedFrame /* outsetFrame */); - w.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout); - w.computeFrameLw(); - final Rect frame = w.getFrameLw(); - - if (frame.left <= 0 && frame.top <= 0) { - // Docked at left or top. - if (frame.bottom >= displayHeight) { - // Docked left. - dockFrame.left = Math.max(frame.right, dockFrame.left); - } else if (frame.right >= displayWidth ) { - // Docked top. - dockFrame.top = Math.max(frame.bottom, dockFrame.top); - } else { - Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w - + " not docked on left or top of display. frame=" + frame - + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight); - } - } else if (frame.right >= displayWidth && frame.bottom >= displayHeight) { - // Docked at right or bottom. - if (frame.top <= 0) { - // Docked right. - dockFrame.right = Math.min(frame.left, dockFrame.right); - } else if (frame.left <= 0) { - // Docked bottom. - dockFrame.bottom = Math.min(frame.top, dockFrame.bottom); - } else { - Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w - + " not docked on right or bottom" + " of display. frame=" + frame - + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight); - } - } else { - // Screen decor windows are required to be docked on one of the sides of the screen. - Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w - + " not docked on one of the sides of the display. frame=" + frame - + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight); - } - } - - displayFrames.mRestricted.set(dockFrame); - displayFrames.mCurrent.set(dockFrame); - displayFrames.mVoiceContent.set(dockFrame); - displayFrames.mSystem.set(dockFrame); - displayFrames.mContent.set(dockFrame); - displayFrames.mRestrictedOverscan.set(dockFrame); - } - - private boolean layoutStatusBar(DisplayFrames displayFrames, int sysui, - boolean isKeyguardShowing) { - // decide where the status bar goes ahead of time - if (mStatusBar == null) { - return false; - } - // apply any navigation bar insets - sTmpRect.setEmpty(); - mStatusBar.getWindowFrames().setFrames(displayFrames.mUnrestricted /* parentFrame */, - displayFrames.mUnrestricted /* displayFrame */, - displayFrames.mStable /* overscanFrame */, displayFrames.mStable /* contentFrame */, - displayFrames.mStable /* visibleFrame */, sTmpRect /* decorFrame */, - displayFrames.mStable /* stableFrame */, displayFrames.mStable /* outsetFrame */); - mStatusBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout); - mStatusBarLayer = mStatusBar.getSurfaceLayer(); - - // Let the status bar determine its size. - mStatusBar.computeFrameLw(); - - // For layout, the status bar is always at the top with our fixed height. - displayFrames.mStable.top = displayFrames.mUnrestricted.top - + mStatusBarHeightForRotation[displayFrames.mRotation]; - // Make sure the status bar covers the entire cutout height - displayFrames.mStable.top = Math.max(displayFrames.mStable.top, - displayFrames.mDisplayCutoutSafe.top); - - // Tell the bar controller where the collapsed status bar content is - sTmpRect.set(mStatusBar.getContentFrameLw()); - sTmpRect.intersect(displayFrames.mDisplayCutoutSafe); - sTmpRect.top = mStatusBar.getContentFrameLw().top; // Ignore top display cutout inset - sTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size - mStatusBarController.setContentFrame(sTmpRect); - - boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0; - boolean statusBarTranslucent = (sysui - & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0; - if (!isKeyguardShowing) { - statusBarTranslucent &= areTranslucentBarsAllowed(); - } - - // If the status bar is hidden, we don't want to cause windows behind it to scroll. - if (mStatusBar.isVisibleLw() && !statusBarTransient) { - // Status bar may go away, so the screen area it occupies is available to apps but just - // covering them when the status bar is visible. - final Rect dockFrame = displayFrames.mDock; - dockFrame.top = displayFrames.mStable.top; - displayFrames.mContent.set(dockFrame); - displayFrames.mVoiceContent.set(dockFrame); - displayFrames.mCurrent.set(dockFrame); - - if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format( - "dock=%s content=%s cur=%s", dockFrame.toString(), - displayFrames.mContent.toString(), displayFrames.mCurrent.toString())); - - if (!mStatusBar.isAnimatingLw() && !statusBarTranslucent - && !mStatusBarController.wasRecentlyTranslucent()) { - // If the opaque status bar is currently requested to be visible, and not in the - // process of animating on or off, then we can tell the app that it is covered by it. - displayFrames.mSystem.top = displayFrames.mStable.top; - } - } - return mStatusBarController.checkHiddenLw(); - } - - private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, boolean navVisible, - boolean navTranslucent, boolean navAllowedHidden, - boolean statusBarForcesShowingNavigation) { - if (mNavigationBar == null) { - return false; - } - - final Rect navigationFrame = sTmpNavFrame; - boolean transientNavBarShowing = mNavigationBarController.isTransientShowing(); - // Force the navigation bar to its appropriate place and size. We need to do this directly, - // instead of relying on it to bubble up from the nav bar, because this needs to change - // atomically with screen rotations. - final int rotation = displayFrames.mRotation; - final int displayHeight = displayFrames.mDisplayHeight; - final int displayWidth = displayFrames.mDisplayWidth; - final Rect dockFrame = displayFrames.mDock; - mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation); - - final Rect cutoutSafeUnrestricted = sTmpRect; - cutoutSafeUnrestricted.set(displayFrames.mUnrestricted); - cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe); - - if (mNavigationBarPosition == NAV_BAR_BOTTOM) { - // It's a system nav bar or a portrait screen; nav bar goes on bottom. - final int top = cutoutSafeUnrestricted.bottom - - getNavigationBarHeight(rotation, uiMode); - // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed - final int topNavBar = cutoutSafeUnrestricted.bottom - - mExperiments.getNavigationBarFrameHeight(); - navigationFrame.set(0, topNavBar, displayWidth, displayFrames.mUnrestricted.bottom); - // EXPERIMENT END - displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top; - if (transientNavBarShowing) { - mNavigationBarController.setBarShowingLw(true); - } else if (navVisible) { - mNavigationBarController.setBarShowingLw(true); - dockFrame.bottom = displayFrames.mRestricted.bottom - = displayFrames.mRestrictedOverscan.bottom = top; - } else { - // We currently want to hide the navigation UI - unless we expanded the status bar. - mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation); - } - if (navVisible && !navTranslucent && !navAllowedHidden - && !mNavigationBar.isAnimatingLw() - && !mNavigationBarController.wasRecentlyTranslucent()) { - // If the opaque nav bar is currently requested to be visible and not in the process - // of animating on or off, then we can tell the app that it is covered by it. - displayFrames.mSystem.bottom = top; - } - } else if (mNavigationBarPosition == NAV_BAR_RIGHT) { - // Landscape screen; nav bar goes to the right. - final int left = cutoutSafeUnrestricted.right - - getNavigationBarWidth(rotation, uiMode); - // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed - final int leftNavBar = cutoutSafeUnrestricted.right - - mExperiments.getNavigationBarFrameWidth(); - navigationFrame.set(leftNavBar, 0, displayFrames.mUnrestricted.right, displayHeight); - // EXPERIMENT END - displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left; - if (transientNavBarShowing) { - mNavigationBarController.setBarShowingLw(true); - } else if (navVisible) { - mNavigationBarController.setBarShowingLw(true); - dockFrame.right = displayFrames.mRestricted.right - = displayFrames.mRestrictedOverscan.right = left; - } else { - // We currently want to hide the navigation UI - unless we expanded the status bar. - mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation); - } - if (navVisible && !navTranslucent && !navAllowedHidden - && !mNavigationBar.isAnimatingLw() - && !mNavigationBarController.wasRecentlyTranslucent()) { - // If the nav bar is currently requested to be visible, and not in the process of - // animating on or off, then we can tell the app that it is covered by it. - displayFrames.mSystem.right = left; - } - } else if (mNavigationBarPosition == NAV_BAR_LEFT) { - // Seascape screen; nav bar goes to the left. - final int right = cutoutSafeUnrestricted.left - + getNavigationBarWidth(rotation, uiMode); - // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed - final int rightNavBar = cutoutSafeUnrestricted.left - + mExperiments.getNavigationBarFrameWidth(); - navigationFrame.set(displayFrames.mUnrestricted.left, 0, rightNavBar, displayHeight); - // EXPERIMENT END - displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right; - if (transientNavBarShowing) { - mNavigationBarController.setBarShowingLw(true); - } else if (navVisible) { - mNavigationBarController.setBarShowingLw(true); - dockFrame.left = displayFrames.mRestricted.left = - displayFrames.mRestrictedOverscan.left = right; - } else { - // We currently want to hide the navigation UI - unless we expanded the status bar. - mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation); - } - if (navVisible && !navTranslucent && !navAllowedHidden - && !mNavigationBar.isAnimatingLw() - && !mNavigationBarController.wasRecentlyTranslucent()) { - // If the nav bar is currently requested to be visible, and not in the process of - // animating on or off, then we can tell the app that it is covered by it. - displayFrames.mSystem.left = right; - } - } - - // Make sure the content and current rectangles are updated to account for the restrictions - // from the navigation bar. - displayFrames.mCurrent.set(dockFrame); - displayFrames.mVoiceContent.set(dockFrame); - displayFrames.mContent.set(dockFrame); - mStatusBarLayer = mNavigationBar.getSurfaceLayer(); - // And compute the final frame. - sTmpRect.setEmpty(); - mNavigationBar.getWindowFrames().setFrames(navigationFrame /* parentFrame */, - navigationFrame /* displayFrame */, navigationFrame /* overscanFrame */, - displayFrames.mDisplayCutoutSafe /* contentFrame */, - navigationFrame /* visibleFrame */, sTmpRect /* decorFrame */, - navigationFrame /* stableFrame */, - displayFrames.mDisplayCutoutSafe /* outsetFrame */); - mNavigationBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout); - mNavigationBar.computeFrameLw(); - mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw()); - - if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame); - return mNavigationBarController.checkHiddenLw(); - } - - @NavigationBarPosition - private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) { - if (mDefaultDisplayPolicy.navigationBarCanMove() && displayWidth > displayHeight) { - if (displayRotation == Surface.ROTATION_270) { - return NAV_BAR_LEFT; - } else { - return NAV_BAR_RIGHT; - } - } - return NAV_BAR_BOTTOM; - } - - /** {@inheritDoc} */ - @Override - public int getSystemDecorLayerLw() { - if (mStatusBar != null && mStatusBar.isVisibleLw()) { - return mStatusBar.getSurfaceLayer(); - } - - if (mNavigationBar != null && mNavigationBar.isVisibleLw()) { - return mNavigationBar.getSurfaceLayer(); - } - - return 0; - } - - private void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached, - boolean insetDecors, Rect pf, Rect df, Rect of, Rect cf, Rect vf, - DisplayFrames displayFrames) { - if (!win.isInputMethodTarget() && attached.isInputMethodTarget()) { - // Here's a special case: if the child window is not the 'dock window' - // or input method target, and the window it is attached to is below - // the dock window, then the frames we computed for the window it is - // attached to can not be used because the dock is effectively part - // of the underlying window and the attached window is floating on top - // of the whole thing. So, we ignore the attached window and explicitly - // compute the frames that would be appropriate without the dock. - vf.set(displayFrames.mDock); - cf.set(displayFrames.mDock); - of.set(displayFrames.mDock); - df.set(displayFrames.mDock); - } else { - // The effective display frame of the attached window depends on whether it is taking - // care of insetting its content. If not, we need to use the parent's content frame so - // that the entire window is positioned within that content. Otherwise we can use the - // overscan frame and let the attached window take care of positioning its content - // appropriately. - if (adjust != SOFT_INPUT_ADJUST_RESIZE) { - // Set the content frame of the attached window to the parent's decor frame - // (same as content frame when IME isn't present) if specifically requested by - // setting {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR} flag. - // Otherwise, use the overscan frame. - cf.set((fl & FLAG_LAYOUT_ATTACHED_IN_DECOR) != 0 - ? attached.getContentFrameLw() : attached.getOverscanFrameLw()); - } else { - // If the window is resizing, then we want to base the content frame on our attached - // content frame to resize...however, things can be tricky if the attached window is - // NOT in resize mode, in which case its content frame will be larger. - // Ungh. So to deal with that, make sure the content frame we end up using is not - // covering the IM dock. - cf.set(attached.getContentFrameLw()); - if (attached.isVoiceInteraction()) { - cf.intersectUnchecked(displayFrames.mVoiceContent); - } else if (win.isInputMethodTarget() || attached.isInputMethodTarget()) { - cf.intersectUnchecked(displayFrames.mContent); - } - } - df.set(insetDecors ? attached.getDisplayFrameLw() : cf); - of.set(insetDecors ? attached.getOverscanFrameLw() : cf); - vf.set(attached.getVisibleFrameLw()); - } - // The LAYOUT_IN_SCREEN flag is used to determine whether the attached window should be - // positioned relative to its parent or the entire screen. - pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrameLw() : df); - } - - private void applyStableConstraints(int sysui, int fl, Rect r, DisplayFrames displayFrames) { - if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) == 0) { - return; - } - // If app is requesting a stable layout, don't let the content insets go below the stable - // values. - if ((fl & FLAG_FULLSCREEN) != 0) { - r.intersectUnchecked(displayFrames.mStableFullscreen); - } else { - r.intersectUnchecked(displayFrames.mStable); - } - } - - private boolean canReceiveInput(WindowState win) { - boolean notFocusable = - (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0; - boolean altFocusableIm = - (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0; - boolean notFocusableForIm = notFocusable ^ altFocusableIm; - return !notFocusableForIm; - } - - /** {@inheritDoc} */ - @Override - public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) { - // We've already done the navigation bar, status bar, and all screen decor windows. If the - // status bar can receive input, we need to layout it again to accommodate for the IME - // window. - if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar - || mScreenDecorWindows.contains(win)) { - return; - } - final WindowManager.LayoutParams attrs = win.getAttrs(); - final boolean isDefaultDisplay = win.isDefaultDisplay(); - - final int type = attrs.type; - final int fl = PolicyControl.getWindowFlags(win, attrs); - final int pfl = attrs.privateFlags; - final int sim = attrs.softInputMode; - final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs); - final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs); - - final WindowFrames windowFrames = win.getWindowFrames(); - - windowFrames.setHasOutsets(false); - sTmpLastParentFrame.set(windowFrames.mParentFrame); - final Rect pf = windowFrames.mParentFrame; - final Rect df = windowFrames.mDisplayFrame; - final Rect of = windowFrames.mOverscanFrame; - final Rect cf = windowFrames.mContentFrame; - final Rect vf = windowFrames.mVisibleFrame; - final Rect dcf = windowFrames.mDecorFrame; - final Rect sf = windowFrames.mStableFrame; - dcf.setEmpty(); - windowFrames.setParentFrameWasClippedByDisplayCutout(false); - windowFrames.setDisplayCutout(displayFrames.mDisplayCutout); - - final boolean hasNavBar = (isDefaultDisplay && mDefaultDisplayPolicy.hasNavigationBar() - && mNavigationBar != null && mNavigationBar.isVisibleLw()); - - final int adjust = sim & SOFT_INPUT_MASK_ADJUST; - - final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0 - || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; - - final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN; - final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR; - - sf.set(displayFrames.mStable); - - if (type == TYPE_INPUT_METHOD) { - vf.set(displayFrames.mDock); - cf.set(displayFrames.mDock); - of.set(displayFrames.mDock); - df.set(displayFrames.mDock); - windowFrames.mParentFrame.set(displayFrames.mDock); - // IM dock windows layout below the nav bar... - pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom; - // ...with content insets above the nav bar - cf.bottom = vf.bottom = displayFrames.mStable.bottom; - // TODO (b/111364446): Support showing IME on non-default displays - if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) { - // The status bar forces the navigation bar while it's visible. Make sure the IME - // avoids the navigation bar in that case. - if (mNavigationBarPosition == NAV_BAR_RIGHT) { - pf.right = df.right = of.right = cf.right = vf.right = - displayFrames.mStable.right; - } else if (mNavigationBarPosition == NAV_BAR_LEFT) { - pf.left = df.left = of.left = cf.left = vf.left = displayFrames.mStable.left; - } - } - - // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed - // Offset the ime to avoid overlapping with the nav bar - mExperiments.offsetWindowFramesForNavBar(mNavigationBarPosition, win); - // EXPERIMENT END - - // IM dock windows always go to the bottom of the screen. - attrs.gravity = Gravity.BOTTOM; - mDockLayer = win.getSurfaceLayer(); - } else if (type == TYPE_VOICE_INTERACTION) { - of.set(displayFrames.mUnrestricted); - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); - if (adjust != SOFT_INPUT_ADJUST_RESIZE) { - cf.set(displayFrames.mDock); - } else { - cf.set(displayFrames.mContent); - } - if (adjust != SOFT_INPUT_ADJUST_NOTHING) { - vf.set(displayFrames.mCurrent); - } else { - vf.set(cf); - } - } else if (type == TYPE_WALLPAPER) { - layoutWallpaper(displayFrames, pf, df, of, cf); - } else if (win == mStatusBar) { - of.set(displayFrames.mUnrestricted); - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); - cf.set(displayFrames.mStable); - vf.set(displayFrames.mStable); - - if (adjust == SOFT_INPUT_ADJUST_RESIZE) { - cf.bottom = displayFrames.mContent.bottom; - } else { - cf.bottom = displayFrames.mDock.bottom; - vf.bottom = displayFrames.mContent.bottom; - } - } else { - dcf.set(displayFrames.mSystem); - final boolean inheritTranslucentDecor = - (attrs.privateFlags & PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0; - final boolean isAppWindow = - type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW; - final boolean topAtRest = - win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw(); - if (isAppWindow && !inheritTranslucentDecor && !topAtRest) { - if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 - && (fl & FLAG_FULLSCREEN) == 0 - && (fl & FLAG_TRANSLUCENT_STATUS) == 0 - && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 - && (pfl & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) == 0) { - // Ensure policy decor includes status bar - dcf.top = displayFrames.mStable.top; - } - if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0 - && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0 - && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) { - // Ensure policy decor includes navigation bar - dcf.bottom = displayFrames.mStable.bottom; - dcf.right = displayFrames.mStable.right; - } - } - - if (layoutInScreen && layoutInsetDecor) { - if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() - + "): IN_SCREEN, INSET_DECOR"); - // This is the case for a normal activity window: we want it to cover all of the - // screen space, and it can take care of moving its contents to account for screen - // decorations that intrude into that space. - if (attached != null) { - // If this window is attached to another, our display - // frame is the same as the one we are attached to. - setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf, - displayFrames); - } else { - if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) { - // Status bar panels are the only windows who can go on top of the status - // bar. They are protected by the STATUS_BAR_SERVICE permission, so they - // have the same privileges as the status bar itself. - // - // However, they should still dodge the navigation bar if it exists. - - pf.left = df.left = of.left = hasNavBar - ? displayFrames.mDock.left : displayFrames.mUnrestricted.left; - pf.top = df.top = of.top = displayFrames.mUnrestricted.top; - pf.right = df.right = of.right = hasNavBar - ? displayFrames.mRestricted.right - : displayFrames.mUnrestricted.right; - pf.bottom = df.bottom = of.bottom = hasNavBar - ? displayFrames.mRestricted.bottom - : displayFrames.mUnrestricted.bottom; - - if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out status bar window: " + pf); - } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0 - && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) { - // Asking to layout into the overscan region, so give it that pure - // unrestricted area. - of.set(displayFrames.mOverscan); - df.set(displayFrames.mOverscan); - pf.set(displayFrames.mOverscan); - } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 - && (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW - || type == TYPE_VOLUME_OVERLAY)) { - // Asking for layout as if the nav bar is hidden, lets the application - // extend into the unrestricted overscan screen area. We only do this for - // application windows and certain system windows to ensure no window that - // can be above the nav bar can do this. - df.set(displayFrames.mOverscan); - pf.set(displayFrames.mOverscan); - // We need to tell the app about where the frame inside the overscan is, so - // it can inset its content by that amount -- it didn't ask to actually - // extend itself into the overscan region. - of.set(displayFrames.mUnrestricted); - } else { - df.set(displayFrames.mRestrictedOverscan); - pf.set(displayFrames.mRestrictedOverscan); - // We need to tell the app about where the frame inside the overscan - // is, so it can inset its content by that amount -- it didn't ask - // to actually extend itself into the overscan region. - of.set(displayFrames.mUnrestricted); - } - - if ((fl & FLAG_FULLSCREEN) == 0) { - if (win.isVoiceInteraction()) { - cf.set(displayFrames.mVoiceContent); - } else { - if (adjust != SOFT_INPUT_ADJUST_RESIZE) { - cf.set(displayFrames.mDock); - } else { - cf.set(displayFrames.mContent); - } - } - } else { - // Full screen windows are always given a layout that is as if the status - // bar and other transient decors are gone. This is to avoid bad states when - // moving from a window that is not hiding the status bar to one that is. - cf.set(displayFrames.mRestricted); - } - applyStableConstraints(sysUiFl, fl, cf, displayFrames); - if (adjust != SOFT_INPUT_ADJUST_NOTHING) { - vf.set(displayFrames.mCurrent); - } else { - vf.set(cf); - } - - // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed - mExperiments.offsetWindowFramesForNavBar(mNavigationBarPosition, win); - // EXPERIMENT END - } - } else if (layoutInScreen || (sysUiFl - & (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) { - if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() - + "): IN_SCREEN"); - // A window that has requested to fill the entire screen just - // gets everything, period. - if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) { - cf.set(displayFrames.mUnrestricted); - of.set(displayFrames.mUnrestricted); - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); - if (hasNavBar) { - pf.left = df.left = of.left = cf.left = displayFrames.mDock.left; - pf.right = df.right = of.right = cf.right = displayFrames.mRestricted.right; - pf.bottom = df.bottom = of.bottom = cf.bottom = - displayFrames.mRestricted.bottom; - } - if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out IN_SCREEN status bar window: " + pf); - } else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) { - // The navigation bar has Real Ultimate Power. - of.set(displayFrames.mUnrestricted); - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); - if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out navigation bar window: " + pf); - } else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT) - && ((fl & FLAG_FULLSCREEN) != 0)) { - // Fullscreen secure system overlays get what they ask for. Screenshot region - // selection overlay should also expand to full screen. - cf.set(displayFrames.mOverscan); - of.set(displayFrames.mOverscan); - df.set(displayFrames.mOverscan); - pf.set(displayFrames.mOverscan); - } else if (type == TYPE_BOOT_PROGRESS) { - // Boot progress screen always covers entire display. - cf.set(displayFrames.mOverscan); - of.set(displayFrames.mOverscan); - df.set(displayFrames.mOverscan); - pf.set(displayFrames.mOverscan); - } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0 - && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) { - // Asking to layout into the overscan region, so give it that pure unrestricted - // area. - cf.set(displayFrames.mOverscan); - of.set(displayFrames.mOverscan); - df.set(displayFrames.mOverscan); - pf.set(displayFrames.mOverscan); - } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 - && (type == TYPE_STATUS_BAR - || type == TYPE_TOAST - || type == TYPE_DOCK_DIVIDER - || type == TYPE_VOICE_INTERACTION_STARTING - || (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW))) { - // Asking for layout as if the nav bar is hidden, lets the - // application extend into the unrestricted screen area. We - // only do this for application windows (or toasts) to ensure no window that - // can be above the nav bar can do this. - // XXX This assumes that an app asking for this will also - // ask for layout in only content. We can't currently figure out - // what the screen would be if only laying out to hide the nav bar. - cf.set(displayFrames.mUnrestricted); - of.set(displayFrames.mUnrestricted); - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); - } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) { - of.set(displayFrames.mRestricted); - df.set(displayFrames.mRestricted); - pf.set(displayFrames.mRestricted); - if (adjust != SOFT_INPUT_ADJUST_RESIZE) { - cf.set(displayFrames.mDock); - } else { - cf.set(displayFrames.mContent); - } - } else { - cf.set(displayFrames.mRestricted); - of.set(displayFrames.mRestricted); - df.set(displayFrames.mRestricted); - pf.set(displayFrames.mRestricted); - } - - applyStableConstraints(sysUiFl, fl, cf,displayFrames); - - if (adjust != SOFT_INPUT_ADJUST_NOTHING) { - vf.set(displayFrames.mCurrent); - } else { - vf.set(cf); - } - } else if (attached != null) { - if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() - + "): attached to " + attached); - // A child window should be placed inside of the same visible - // frame that its parent had. - setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf, - displayFrames); - } else { - if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() + - "): normal window"); - // Otherwise, a normal window must be placed inside the content - // of all screen decorations. - if (type == TYPE_STATUS_BAR_PANEL) { - // Status bar panels can go on - // top of the status bar. They are protected by the STATUS_BAR_SERVICE - // permission, so they have the same privileges as the status bar itself. - cf.set(displayFrames.mRestricted); - of.set(displayFrames.mRestricted); - df.set(displayFrames.mRestricted); - pf.set(displayFrames.mRestricted); - } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) { - // These dialogs are stable to interim decor changes. - cf.set(displayFrames.mStable); - of.set(displayFrames.mStable); - df.set(displayFrames.mStable); - pf.set(displayFrames.mStable); - } else { - pf.set(displayFrames.mContent); - if (win.isVoiceInteraction()) { - cf.set(displayFrames.mVoiceContent); - of.set(displayFrames.mVoiceContent); - df.set(displayFrames.mVoiceContent); - } else if (adjust != SOFT_INPUT_ADJUST_RESIZE) { - cf.set(displayFrames.mDock); - of.set(displayFrames.mDock); - df.set(displayFrames.mDock); - } else { - cf.set(displayFrames.mContent); - of.set(displayFrames.mContent); - df.set(displayFrames.mContent); - } - if (adjust != SOFT_INPUT_ADJUST_NOTHING) { - vf.set(displayFrames.mCurrent); - } else { - vf.set(cf); - } - } - } - } - - final int cutoutMode = attrs.layoutInDisplayCutoutMode; - final boolean attachedInParent = attached != null && !layoutInScreen; - final boolean requestedHideNavigation = - (requestedSysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0; - - // TYPE_BASE_APPLICATION windows are never considered floating here because they don't get - // cropped / shifted to the displayFrame in WindowState. - final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen - && type != TYPE_BASE_APPLICATION; - - // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in - // the cutout safe zone. - if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) { - final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect; - displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe); - if (layoutInScreen && layoutInsetDecor && !requestedFullscreen - && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) { - // At the top we have the status bar, so apps that are - // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN - // already expect that there's an inset there and we don't need to exclude - // the window from that area. - displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE; - } - if (layoutInScreen && layoutInsetDecor && !requestedHideNavigation - && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) { - // Same for the navigation bar. - switch (mNavigationBarPosition) { - case NAV_BAR_BOTTOM: - displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE; - break; - case NAV_BAR_RIGHT: - displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE; - break; - case NAV_BAR_LEFT: - displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE; - break; - } - } - if (type == TYPE_INPUT_METHOD && mNavigationBarPosition == NAV_BAR_BOTTOM) { - // The IME can always extend under the bottom cutout if the navbar is there. - displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE; - } - // Windows that are attached to a parent and laid out in said parent already avoid - // the cutout according to that parent and don't need to be further constrained. - // Floating IN_SCREEN windows get what they ask for and lay out in the full screen. - // They will later be cropped or shifted using the displayFrame in WindowState, - // which prevents overlap with the DisplayCutout. - if (!attachedInParent && !floatingInScreenWindow) { - sTmpRect.set(pf); - pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars); - windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf)); - } - // Make sure that NO_LIMITS windows clipped to the display don't extend under the - // cutout. - df.intersectUnchecked(displayCutoutSafeExceptMaybeBars); - } - - // Content should never appear in the cutout. - cf.intersectUnchecked(displayFrames.mDisplayCutoutSafe); - - // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it. - // Also, we don't allow windows in multi-window mode to extend out of the screen. - if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR - && !win.isInMultiWindowMode()) { - df.left = df.top = -10000; - df.right = df.bottom = 10000; - if (type != TYPE_WALLPAPER) { - of.left = of.top = cf.left = cf.top = vf.left = vf.top = -10000; - of.right = of.bottom = cf.right = cf.bottom = vf.right = vf.bottom = 10000; - } - } - - // If the device has a chin (e.g. some watches), a dead area at the bottom of the screen we - // need to provide information to the clients that want to pretend that you can draw there. - // We only want to apply outsets to certain types of windows. For example, we never want to - // apply the outsets to floating dialogs, because they wouldn't make sense there. - final boolean useOutsets = shouldUseOutsets(attrs, fl); - if (isDefaultDisplay && useOutsets) { - final Rect osf = windowFrames.mOutsetFrame; - osf.set(cf.left, cf.top, cf.right, cf.bottom); - windowFrames.setHasOutsets(true); - int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources()); - if (outset > 0) { - int rotation = displayFrames.mRotation; - if (rotation == Surface.ROTATION_0) { - osf.bottom += outset; - } else if (rotation == Surface.ROTATION_90) { - osf.right += outset; - } else if (rotation == Surface.ROTATION_180) { - osf.top -= outset; - } else if (rotation == Surface.ROTATION_270) { - osf.left -= outset; - } - if (DEBUG_LAYOUT) Slog.v(TAG, "applying bottom outset of " + outset - + " with rotation " + rotation + ", result: " + osf); - } - } - - if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle() - + ": sim=#" + Integer.toHexString(sim) - + " attach=" + attached + " type=" + type - + String.format(" flags=0x%08x", fl) - + " pf=" + pf.toShortString() + " df=" + df.toShortString() - + " of=" + of.toShortString() - + " cf=" + cf.toShortString() + " vf=" + vf.toShortString() - + " dcf=" + dcf.toShortString() - + " sf=" + sf.toShortString() - + " osf=" + windowFrames.mOutsetFrame.toShortString() + " " + win); - - if (!sTmpLastParentFrame.equals(pf)) { - windowFrames.setContentChanged(true); - } - - win.computeFrameLw(); - // Dock windows carve out the bottom of the screen, so normal windows - // can't appear underneath them. - if (type == TYPE_INPUT_METHOD && win.isVisibleLw() - && !win.getGivenInsetsPendingLw()) { - offsetInputMethodWindowLw(win, displayFrames); - } - if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw() - && !win.getGivenInsetsPendingLw()) { - offsetVoiceInputWindowLw(win, displayFrames); - } - } - - private void layoutWallpaper(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect cf) { - // The wallpaper has Real Ultimate Power, but we want to tell it about the overscan area. - df.set(displayFrames.mOverscan); - pf.set(displayFrames.mOverscan); - cf.set(displayFrames.mUnrestricted); - of.set(displayFrames.mUnrestricted); - } - - private void offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) { - int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top); - top += win.getGivenContentInsetsLw().top; - displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top); - displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top); - top = win.getVisibleFrameLw().top; - top += win.getGivenVisibleInsetsLw().top; - displayFrames.mCurrent.bottom = Math.min(displayFrames.mCurrent.bottom, top); - if (DEBUG_LAYOUT) Slog.v(TAG, "Input method: mDockBottom=" - + displayFrames.mDock.bottom + " mContentBottom=" - + displayFrames.mContent.bottom + " mCurBottom=" + displayFrames.mCurrent.bottom); - } - - private void offsetVoiceInputWindowLw(WindowState win, DisplayFrames displayFrames) { - int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top); - top += win.getGivenContentInsetsLw().top; - displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top); - } - - /** {@inheritDoc} */ - @Override - public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) { - mTopFullscreenOpaqueWindowState = null; - mTopFullscreenOpaqueOrDimmingWindowState = null; - mTopDockedOpaqueWindowState = null; - mTopDockedOpaqueOrDimmingWindowState = null; - mForceStatusBar = false; - mForceStatusBarFromKeyguard = false; - mForceStatusBarTransparent = false; - mForcingShowNavBar = false; - mForcingShowNavBarLayer = -1; - - mAllowLockscreenWhenOn = false; - mShowingDream = false; - mWindowSleepTokenNeeded = false; - } - - /** {@inheritDoc} */ - @Override - public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs, - WindowState attached, WindowState imeTarget) { - final boolean affectsSystemUi = win.canAffectSystemUiFlags(); - if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi); - applyKeyguardPolicyLw(win, imeTarget); - final int fl = PolicyControl.getWindowFlags(win, attrs); - if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi - && attrs.type == TYPE_INPUT_METHOD) { - mForcingShowNavBar = true; - mForcingShowNavBarLayer = win.getSurfaceLayer(); - } - if (attrs.type == TYPE_STATUS_BAR) { - if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { - mForceStatusBarFromKeyguard = true; - } - if ((attrs.privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) { - mForceStatusBarTransparent = true; - } - } - - boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW - && attrs.type < FIRST_SYSTEM_WINDOW; - final int windowingMode = win.getWindowingMode(); - final boolean inFullScreenOrSplitScreenSecondaryWindowingMode = - windowingMode == WINDOWING_MODE_FULLSCREEN - || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; - if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi) { - if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) { - mForceStatusBar = true; - } - if (attrs.type == TYPE_DREAM) { - // If the lockscreen was showing when the dream started then wait - // for the dream to draw before hiding the lockscreen. - if (!mDreamingLockscreen - || (win.isVisibleLw() && win.hasDrawnLw())) { - mShowingDream = true; - appWindow = true; - } - } - - // For app windows that are not attached, we decide if all windows in the app they - // represent should be hidden or if we should hide the lockscreen. For attached app - // windows we defer the decision to the window it is attached to. - if (appWindow && attached == null) { - if (attrs.isFullscreen() && inFullScreenOrSplitScreenSecondaryWindowingMode) { - if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win); - mTopFullscreenOpaqueWindowState = win; - if (mTopFullscreenOpaqueOrDimmingWindowState == null) { - mTopFullscreenOpaqueOrDimmingWindowState = win; - } - if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) { - mAllowLockscreenWhenOn = true; - } - } - } - } - - // Voice interaction overrides both top fullscreen and top docked. - if (affectsSystemUi && win.getAttrs().type == TYPE_VOICE_INTERACTION) { - if (mTopFullscreenOpaqueWindowState == null) { - mTopFullscreenOpaqueWindowState = win; - if (mTopFullscreenOpaqueOrDimmingWindowState == null) { - mTopFullscreenOpaqueOrDimmingWindowState = win; - } - } - if (mTopDockedOpaqueWindowState == null) { - mTopDockedOpaqueWindowState = win; - if (mTopDockedOpaqueOrDimmingWindowState == null) { - mTopDockedOpaqueOrDimmingWindowState = win; - } - } - } - - // Keep track of the window if it's dimming but not necessarily fullscreen. - if (mTopFullscreenOpaqueOrDimmingWindowState == null && affectsSystemUi - && win.isDimming() && inFullScreenOrSplitScreenSecondaryWindowingMode) { - mTopFullscreenOpaqueOrDimmingWindowState = win; - } - - // We need to keep track of the top "fullscreen" opaque window for the docked stack - // separately, because both the "real fullscreen" opaque window and the one for the docked - // stack can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR. - if (mTopDockedOpaqueWindowState == null && affectsSystemUi && appWindow && attached == null - && attrs.isFullscreen() && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - mTopDockedOpaqueWindowState = win; - if (mTopDockedOpaqueOrDimmingWindowState == null) { - mTopDockedOpaqueOrDimmingWindowState = win; - } - } - - // Also keep track of any windows that are dimming but not necessarily fullscreen in the - // docked stack. - if (mTopDockedOpaqueOrDimmingWindowState == null && affectsSystemUi && win.isDimming() - && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - mTopDockedOpaqueOrDimmingWindowState = win; - } - - // Take note if a window wants to acquire a sleep token. - if ((attrs.privateFlags & PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN) != 0 - && win.canAcquireSleepToken()) { - mWindowSleepTokenNeeded = true; - } - } - - private void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget) { + public void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget) { if (canBeHiddenByKeyguardLw(win)) { if (shouldBeHiddenByKeyguard(win, imeTarget)) { win.hideLw(false /* doAnimation */); @@ -5441,148 +3373,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override - public int finishPostLayoutPolicyLw() { - int changes = 0; - boolean topIsFullscreen = false; - - final WindowManager.LayoutParams lp = (mTopFullscreenOpaqueWindowState != null) - ? mTopFullscreenOpaqueWindowState.getAttrs() - : null; - - // If we are not currently showing a dream then remember the current - // lockscreen state. We will use this to determine whether the dream - // started while the lockscreen was showing and remember this state - // while the dream is showing. - if (!mShowingDream) { - mDreamingLockscreen = isKeyguardShowingAndNotOccluded(); - if (mDreamingSleepTokenNeeded) { - mDreamingSleepTokenNeeded = false; - mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 0, 1).sendToTarget(); - } - } else { - if (!mDreamingSleepTokenNeeded) { - mDreamingSleepTokenNeeded = true; - mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 1, 1).sendToTarget(); - } - } - - if (mStatusBar != null) { - if (DEBUG_LAYOUT) Slog.i(TAG, "force=" + mForceStatusBar - + " forcefkg=" + mForceStatusBarFromKeyguard - + " top=" + mTopFullscreenOpaqueWindowState); - boolean shouldBeTransparent = mForceStatusBarTransparent - && !mForceStatusBar - && !mForceStatusBarFromKeyguard; - if (!shouldBeTransparent) { - mStatusBarController.setShowTransparent(false /* transparent */); - } else if (!mStatusBar.isVisibleLw()) { - mStatusBarController.setShowTransparent(true /* transparent */); - } - - boolean statusBarForcesShowingNavigation - = (mStatusBar.getAttrs().privateFlags - & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0; - boolean topAppHidesStatusBar = topAppHidesStatusBar(); - if (mForceStatusBar || mForceStatusBarFromKeyguard || mForceStatusBarTransparent - || statusBarForcesShowingNavigation) { - if (DEBUG_LAYOUT) Slog.v(TAG, "Showing status bar: forced"); - if (mStatusBarController.setBarShowingLw(true)) { - changes |= FINISH_LAYOUT_REDO_LAYOUT; - } - // Maintain fullscreen layout until incoming animation is complete. - topIsFullscreen = mTopIsFullscreen && mStatusBar.isAnimatingLw(); - // Transient status bar is not allowed if status bar is on lockscreen or status bar - // is expecting the navigation keys from the user. - if ((mForceStatusBarFromKeyguard || statusBarForcesShowingNavigation) - && mStatusBarController.isTransientShowing()) { - mStatusBarController.updateVisibilityLw(false /*transientAllowed*/, - mLastSystemUiFlags, mLastSystemUiFlags); - } - } else if (mTopFullscreenOpaqueWindowState != null) { - topIsFullscreen = topAppHidesStatusBar; - // The subtle difference between the window for mTopFullscreenOpaqueWindowState - // and mTopIsFullscreen is that mTopIsFullscreen is set only if the window - // has the FLAG_FULLSCREEN set. Not sure if there is another way that to be the - // case though. - if (mStatusBarController.isTransientShowing()) { - if (mStatusBarController.setBarShowingLw(true)) { - changes |= FINISH_LAYOUT_REDO_LAYOUT; - } - } else if (topIsFullscreen - && !mWindowManagerInternal.isStackVisible(WINDOWING_MODE_FREEFORM) - && !mWindowManagerInternal.isStackVisible( - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) { - if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar"); - if (mStatusBarController.setBarShowingLw(false)) { - changes |= FINISH_LAYOUT_REDO_LAYOUT; - } else { - if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar already hiding"); - } - } else { - if (DEBUG_LAYOUT) Slog.v(TAG, "** SHOWING status bar: top is not fullscreen"); - if (mStatusBarController.setBarShowingLw(true)) { - changes |= FINISH_LAYOUT_REDO_LAYOUT; - } - topAppHidesStatusBar = false; - } - } - mStatusBarController.setTopAppHidesStatusBar(topAppHidesStatusBar); - } - - if (mTopIsFullscreen != topIsFullscreen) { - if (!topIsFullscreen) { - // Force another layout when status bar becomes fully shown. - changes |= FINISH_LAYOUT_REDO_LAYOUT; - } - mTopIsFullscreen = topIsFullscreen; - } - - if ((updateSystemUiVisibilityLw()&SYSTEM_UI_CHANGING_LAYOUT) != 0) { - // If the navigation bar has been hidden or shown, we need to do another - // layout pass to update that window. - changes |= FINISH_LAYOUT_REDO_LAYOUT; - } - - if (mShowingDream != mLastShowingDream) { - mLastShowingDream = mShowingDream; - mWindowManagerFuncs.notifyShowingDreamChanged(); - } - - updateWindowSleepToken(); - - // update since mAllowLockscreenWhenOn might have changed - updateLockScreenTimeout(); - return changes; - } - - private void updateWindowSleepToken() { - if (mWindowSleepTokenNeeded && !mLastWindowSleepTokenNeeded) { - mHandler.removeCallbacks(mReleaseSleepTokenRunnable); - mHandler.post(mAcquireSleepTokenRunnable); - } else if (!mWindowSleepTokenNeeded && mLastWindowSleepTokenNeeded) { - mHandler.removeCallbacks(mAcquireSleepTokenRunnable); - mHandler.post(mReleaseSleepTokenRunnable); - } - mLastWindowSleepTokenNeeded = mWindowSleepTokenNeeded; - } - - /** - * @return Whether the top app should hide the statusbar based on the top fullscreen opaque - * window. - */ - private boolean topAppHidesStatusBar() { - if (mTopFullscreenOpaqueWindowState == null) { - return false; - } - final int fl = PolicyControl.getWindowFlags(null, - mTopFullscreenOpaqueWindowState.getAttrs()); - if (localLOGV) { - Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()); - Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs() - + " lp.flags=0x" + Integer.toHexString(fl)); - } - return (fl & LayoutParams.FLAG_FULLSCREEN) != 0 - || (mLastSystemUiFlags & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; + public void setKeyguardCandidateLw(WindowState win) { + mKeyguardCandidate = win; + setKeyguardOccludedLw(mKeyguardOccluded, true /* force */); } /** @@ -5598,19 +3391,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (!isOccluded && changed && showing) { mKeyguardOccluded = false; mKeyguardDelegate.setOccluded(false, true /* animate */); - if (mStatusBar != null) { - mStatusBar.getAttrs().privateFlags |= PRIVATE_FLAG_KEYGUARD; + if (mKeyguardCandidate != null) { + mKeyguardCandidate.getAttrs().privateFlags |= PRIVATE_FLAG_KEYGUARD; if (!mKeyguardDelegate.hasLockscreenWallpaper()) { - mStatusBar.getAttrs().flags |= FLAG_SHOW_WALLPAPER; + mKeyguardCandidate.getAttrs().flags |= FLAG_SHOW_WALLPAPER; } } return true; } else if (isOccluded && changed && showing) { mKeyguardOccluded = true; mKeyguardDelegate.setOccluded(true, false /* animate */); - if (mStatusBar != null) { - mStatusBar.getAttrs().privateFlags &= ~PRIVATE_FLAG_KEYGUARD; - mStatusBar.getAttrs().flags &= ~FLAG_SHOW_WALLPAPER; + if (mKeyguardCandidate != null) { + mKeyguardCandidate.getAttrs().privateFlags &= ~PRIVATE_FLAG_KEYGUARD; + mKeyguardCandidate.getAttrs().flags &= ~FLAG_SHOW_WALLPAPER; } return true; } else if (changed) { @@ -5622,28 +3415,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - private boolean isStatusBarKeyguard() { - return mStatusBar != null - && (mStatusBar.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0; - } - - @Override - public boolean allowAppAnimationsLw() { - return !mShowingDream; - } - - @Override - public int focusChangedLw(WindowState lastFocus, WindowState newFocus) { - mFocusedWindow = newFocus; - mLastFocusedWindow = lastFocus; - if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) { - // If the navigation bar has been hidden or shown, we need to do another - // layout pass to update that window. - return FINISH_LAYOUT_REDO_LAYOUT; - } - return 0; - } - /** {@inheritDoc} */ @Override public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { @@ -6469,57 +4240,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { // current user. mSettingsObserver.onChange(false); mDefaultDisplayRotation.onUserSwitch(); - - // force a re-application of focused window sysui visibility. - // the window may never have been shown for this user - // e.g. the keyguard when going through the new-user setup flow - synchronized (mWindowManagerFuncs.getWindowManagerLock()) { - mLastSystemUiFlags = 0; - updateSystemUiVisibilityLw(); - } - } - } - }; - - private final Runnable mHiddenNavPanic = new Runnable() { - @Override - public void run() { - synchronized (mWindowManagerFuncs.getWindowManagerLock()) { - if (!isUserSetupComplete()) { - // Swipe-up for navigation bar is disabled during setup - return; - } - mPendingPanicGestureUptime = SystemClock.uptimeMillis(); - if (!isNavBarEmpty(mLastSystemUiFlags)) { - mNavigationBarController.showTransient(); - } + mWindowManagerFuncs.onUserSwitched(); } } }; - private void requestTransientBars(WindowState swipeTarget) { - synchronized (mWindowManagerFuncs.getWindowManagerLock()) { - if (!isUserSetupComplete()) { - // Swipe-up for navigation bar is disabled during setup - return; - } - boolean sb = mStatusBarController.checkShowTransientBarLw(); - boolean nb = mNavigationBarController.checkShowTransientBarLw() - && !isNavBarEmpty(mLastSystemUiFlags); - if (sb || nb) { - // Don't show status bar when swiping on already visible navigation bar - if (!nb && swipeTarget == mNavigationBar) { - if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target"); - return; - } - if (sb) mStatusBarController.showTransient(); - if (nb) mNavigationBarController.showTransient(); - mImmersiveModeConfirmation.confirmCurrentPrompt(); - updateSystemUiVisibilityLw(); - } - } - } - // Called on the PowerManager's Notifier thread. @Override public void startedGoingToSleep(int why) { @@ -6852,11 +4577,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - public boolean isShowingDreamLw() { - return mShowingDream; - } - - @Override public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) { if (mKeyguardDelegate != null) { if (DEBUG_KEYGUARD) Slog.d(TAG, "PWM.startKeyguardExitAnimation"); @@ -6864,85 +4584,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - @Override - public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight, - DisplayCutout displayCutout, Rect outInsets) { - outInsets.setEmpty(); - - // Navigation bar and status bar. - getNonDecorInsetsLw(displayRotation, displayWidth, displayHeight, displayCutout, outInsets); - outInsets.top = Math.max(outInsets.top, mStatusBarHeightForRotation[displayRotation]); - } - - @Override - public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight, - DisplayCutout displayCutout, Rect outInsets) { - outInsets.setEmpty(); - - // Only navigation bar - if (mDefaultDisplayPolicy.hasNavigationBar()) { - int position = navigationBarPosition(displayWidth, displayHeight, displayRotation); - if (position == NAV_BAR_BOTTOM) { - outInsets.bottom = getNavigationBarHeight(displayRotation, mUiMode); - } else if (position == NAV_BAR_RIGHT) { - outInsets.right = getNavigationBarWidth(displayRotation, mUiMode); - } else if (position == NAV_BAR_LEFT) { - outInsets.left = getNavigationBarWidth(displayRotation, mUiMode); - } - } - - if (displayCutout != null) { - outInsets.left += displayCutout.getSafeInsetLeft(); - outInsets.top += displayCutout.getSafeInsetTop(); - outInsets.right += displayCutout.getSafeInsetRight(); - outInsets.bottom += displayCutout.getSafeInsetBottom(); - } - } - - @Override - public boolean isNavBarForcedShownLw(WindowState windowState) { - return mForceShowSystemBars; - } - - @Override - public int getNavBarPosition() { - // TODO(multi-display): Support system decor on secondary displays. - return mNavigationBarPosition; - } - - @Override - public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth, - int displayHeight, int displayRotation) { - final int barPosition = navigationBarPosition(displayWidth, displayHeight, displayRotation); - return isDockSideAllowed(dockSide, originalDockSide, barPosition, - mDefaultDisplayPolicy.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 - if (dockSide == DOCKED_LEFT && originalDockSide == DOCKED_TOP) { - return true; - } - return false; - } - void sendCloseSystemWindows() { PhoneWindow.sendCloseSystemWindows(mContext, null); } @@ -7009,9 +4650,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - mSystemGestures.systemReady(); - mImmersiveModeConfirmation.systemReady(); - mAutofillManagerInternal = LocalServices.getService(AutofillManagerInternal.class); } @@ -7173,10 +4811,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { mHandler.post(mScreenLockTimeout); } + // TODO (b/113840485): Move this logic to DisplayPolicy when lockscreen supports multi-display. + @Override + public void setAllowLockscreenWhenOn(int displayId, boolean allow) { + if (allow) { + mAllowLockscreenWhenOnDisplays.add(displayId); + } else { + mAllowLockscreenWhenOnDisplays.remove(displayId); + } + updateLockScreenTimeout(); + } + private void updateLockScreenTimeout() { synchronized (mScreenLockTimeout) { - final boolean enable = (mAllowLockscreenWhenOn && mDefaultDisplayPolicy.isAwake() && - mKeyguardDelegate != null && mKeyguardDelegate.isSecure(mCurrentUserId)); + final boolean enable = !mAllowLockscreenWhenOnDisplays.isEmpty() + && mDefaultDisplayPolicy.isAwake() + && mKeyguardDelegate != null && mKeyguardDelegate.isSecure(mCurrentUserId); if (mLockScreenTimerActive != enable) { if (enable) { if (localLOGV) Log.v(TAG, "setting lockscreen timer"); @@ -7192,21 +4842,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } // TODO (multidisplay): Support multiple displays in WindowManagerPolicy. - private void updateDreamingSleepToken(boolean acquire) { - if (acquire) { - if (mDreamingSleepToken == null) { - mDreamingSleepToken = mActivityTaskManagerInternal.acquireSleepToken( - "Dream", DEFAULT_DISPLAY); - } - } else { - if (mDreamingSleepToken != null) { - mDreamingSleepToken.release(); - mDreamingSleepToken = null; - } - } - } - - // TODO (multidisplay): Support multiple displays in WindowManagerPolicy. private void updateScreenOffSleepToken(boolean acquire) { if (acquire) { if (mScreenOffSleepToken == null) { @@ -7254,6 +4889,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + @Override + public int getUiMode() { + return mUiMode; + } + void updateRotation(boolean alwaysSendConfiguration) { try { // Set orientation on WindowManager. @@ -7510,380 +5150,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - private int updateSystemUiVisibilityLw() { - // If there is no window focused, there will be nobody to handle the events - // anyway, so just hang on in whatever state we're in until things settle down. - WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow - : mTopFullscreenOpaqueWindowState; - if (winCandidate == null) { - return 0; - } - - // The immersive mode confirmation should never affect the system bar visibility, otherwise - // it will unhide the navigation bar and hide itself. - if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) { - - // The immersive mode confirmation took the focus from mLastFocusedWindow which was - // controlling the system ui visibility. So if mLastFocusedWindow can still receive - // keys, we let it keep controlling the visibility. - final boolean lastFocusCanReceiveKeys = - (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys()); - winCandidate = isStatusBarKeyguard() ? mStatusBar - : lastFocusCanReceiveKeys ? mLastFocusedWindow - : mTopFullscreenOpaqueWindowState; - if (winCandidate == null) { - return 0; - } - } - final WindowState win = winCandidate; - if ((win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 && mKeyguardOccluded) { - // We are updating at a point where the keyguard has gotten - // focus, but we were last in a state where the top window is - // hiding it. This is probably because the keyguard as been - // shown while the top window was displayed, so we want to ignore - // it here because this is just a very transient change and it - // will quickly lose focus once it correctly gets hidden. - return 0; - } - - int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null) - & ~mResettingSystemUiFlags - & ~mForceClearedSystemUiFlags; - if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) { - tmpVisibility &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS); - } - - final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */, - mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState); - final int dockedVisibility = updateLightStatusBarLw(0 /* vis */, - mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState); - mWindowManagerFuncs.getStackBounds( - WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds); - mWindowManagerFuncs.getStackBounds( - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds); - final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility); - final int diff = visibility ^ mLastSystemUiFlags; - final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags; - final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags; - final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState); - if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu - && mFocusedApp == win.getAppToken() - && mLastNonDockedStackBounds.equals(mNonDockedStackBounds) - && mLastDockedStackBounds.equals(mDockedStackBounds)) { - return 0; - } - mLastSystemUiFlags = visibility; - mLastFullscreenStackSysUiFlags = fullscreenVisibility; - mLastDockedStackSysUiFlags = dockedVisibility; - mLastFocusNeedsMenu = needsMenu; - mFocusedApp = win.getAppToken(); - final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds); - final Rect dockedStackBounds = new Rect(mDockedStackBounds); - mHandler.post(new Runnable() { - @Override - public void run() { - StatusBarManagerInternal statusbar = getStatusBarManagerInternal(); - if (statusbar != null) { - statusbar.setSystemUiVisibility(visibility, fullscreenVisibility, - dockedVisibility, 0xffffffff, fullscreenStackBounds, - dockedStackBounds, win.toString()); - statusbar.topAppWindowChanged(needsMenu); - } - } - }); - return diff; - } - - private int updateLightStatusBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming) { - final boolean onKeyguard = isStatusBarKeyguard() && !mKeyguardOccluded; - final WindowState statusColorWin = onKeyguard ? mStatusBar : opaqueOrDimming; - if (statusColorWin != null && (statusColorWin == opaque || onKeyguard)) { - // If the top fullscreen-or-dimming window is also the top fullscreen, respect - // its light flag. - vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; - vis |= PolicyControl.getSystemUiVisibility(statusColorWin, null) - & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; - } else if (statusColorWin != null && statusColorWin.isDimming()) { - // Otherwise if it's dimming, clear the light flag. - vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; - } - return vis; - } - - @VisibleForTesting - @Nullable - static WindowState chooseNavigationColorWindowLw(WindowState opaque, - WindowState opaqueOrDimming, WindowState imeWindow, - @NavigationBarPosition int navBarPosition) { - // If the IME window is visible and FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is set, then IME - // window can be navigation color window. - final boolean imeWindowCanNavColorWindow = imeWindow != null - && imeWindow.isVisibleLw() - && navBarPosition == NAV_BAR_BOTTOM - && (PolicyControl.getWindowFlags(imeWindow, null) - & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; - - if (opaque != null && opaqueOrDimming == opaque) { - // If the top fullscreen-or-dimming window is also the top fullscreen, respect it - // unless IME window is also eligible, since currently the IME window is always show - // above the opaque fullscreen app window, regardless of the IME target window. - // TODO(b/31559891): Maybe we need to revisit this condition once b/31559891 is fixed. - return imeWindowCanNavColorWindow ? imeWindow : opaque; - } - - if (opaqueOrDimming == null || !opaqueOrDimming.isDimming()) { - // No dimming window is involved. Determine the result only with the IME window. - return imeWindowCanNavColorWindow ? imeWindow : null; - } - - if (!imeWindowCanNavColorWindow) { - // No IME window is involved. Determine the result only with opaqueOrDimming. - return opaqueOrDimming; - } - - // The IME window and the dimming window are competing. Check if the dimming window can be - // IME target or not. - if (LayoutParams.mayUseInputMethod(PolicyControl.getWindowFlags(opaqueOrDimming, null))) { - // The IME window is above the dimming window. - return imeWindow; - } else { - // The dimming window is above the IME window. - return opaqueOrDimming; - } - } - - @VisibleForTesting - static int updateLightNavigationBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming, - WindowState imeWindow, WindowState navColorWin) { - - if (navColorWin != null) { - if (navColorWin == imeWindow || navColorWin == opaque) { - // Respect the light flag. - vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; - vis |= PolicyControl.getSystemUiVisibility(navColorWin, null) - & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; - } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) { - // Clear the light flag for dimming window. - vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; - } - } - return vis; - } - - private int updateSystemBarsLw(WindowState win, int oldVis, int vis) { - final boolean dockedStackVisible = - mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - final boolean freeformStackVisible = - mWindowManagerInternal.isStackVisible(WINDOWING_MODE_FREEFORM); - final boolean resizing = mWindowManagerInternal.isDockedDividerResizing(); - - // We need to force system bars when the docked stack is visible, when the freeform stack - // is visible but also when we are resizing for the transitions when docked stack - // visibility changes. - mForceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing; - final boolean forceOpaqueStatusBar = mForceShowSystemBars && !mForceStatusBarFromKeyguard; - - // apply translucent bar vis flags - WindowState fullscreenTransWin = isStatusBarKeyguard() && !mKeyguardOccluded - ? mStatusBar - : mTopFullscreenOpaqueWindowState; - vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis); - vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis); - final int dockedVis = mStatusBarController.applyTranslucentFlagLw( - mTopDockedOpaqueWindowState, 0, 0); - - final boolean fullscreenDrawsStatusBarBackground = - drawsStatusBarBackground(vis, mTopFullscreenOpaqueWindowState); - final boolean dockedDrawsStatusBarBackground = - drawsStatusBarBackground(dockedVis, mTopDockedOpaqueWindowState); - - // prevent status bar interaction from clearing certain flags - int type = win.getAttrs().type; - boolean statusBarHasFocus = type == TYPE_STATUS_BAR; - if (statusBarHasFocus && !isStatusBarKeyguard()) { - int flags = View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_IMMERSIVE - | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; - if (mKeyguardOccluded) { - flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT; - } - vis = (vis & ~flags) | (oldVis & flags); - } - - if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) { - vis |= View.STATUS_BAR_TRANSPARENT; - vis &= ~View.STATUS_BAR_TRANSLUCENT; - } else if ((!areTranslucentBarsAllowed() && fullscreenTransWin != mStatusBar) - || forceOpaqueStatusBar) { - vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT); - } - - vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing); - - // update status bar - boolean immersiveSticky = - (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; - final boolean hideStatusBarWM = - mTopFullscreenOpaqueWindowState != null - && (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null) - & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0; - final boolean hideStatusBarSysui = - (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; - final boolean hideNavBarSysui = - (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0; - - final boolean transientStatusBarAllowed = mStatusBar != null - && (statusBarHasFocus || (!mForceShowSystemBars - && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky)))); - - final boolean transientNavBarAllowed = mNavigationBar != null - && !mForceShowSystemBars && hideNavBarSysui && immersiveSticky; - - final long now = SystemClock.uptimeMillis(); - final boolean pendingPanic = mPendingPanicGestureUptime != 0 - && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION; - if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard() - && mDefaultDisplayPolicy.isKeyguardDrawComplete()) { - // The user performed the panic gesture recently, we're about to hide the bars, - // we're no longer on the Keyguard and the screen is ready. We can now request the bars. - mPendingPanicGestureUptime = 0; - mStatusBarController.showTransient(); - if (!isNavBarEmpty(vis)) { - mNavigationBarController.showTransient(); - } - } - - final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested() - && !transientStatusBarAllowed && hideStatusBarSysui; - final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested() - && !transientNavBarAllowed; - if (denyTransientStatus || denyTransientNav || mForceShowSystemBars) { - // clear the clearable flags instead - clearClearableFlagsLw(); - vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS; - } - - final boolean immersive = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0; - immersiveSticky = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; - final boolean navAllowedHidden = immersive || immersiveSticky; - - if (hideNavBarSysui && !navAllowedHidden - && getWindowLayerLw(win) > getWindowLayerFromTypeLw(TYPE_INPUT_CONSUMER)) { - // We can't hide the navbar from this window otherwise the input consumer would not get - // the input events. - vis = (vis & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); - } - - vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis); - - // update navigation bar - boolean oldImmersiveMode = isImmersiveMode(oldVis); - boolean newImmersiveMode = isImmersiveMode(vis); - if (win != null && oldImmersiveMode != newImmersiveMode) { - final String pkg = win.getOwningPackage(); - mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode, - isUserSetupComplete(), isNavBarEmpty(win.getSystemUiVisibility())); - } - - vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis); - - final WindowState navColorWin = chooseNavigationColorWindowLw( - mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState, - mWindowManagerFuncs.getInputMethodWindowLw(), mNavigationBarPosition); - vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState, - mTopFullscreenOpaqueOrDimmingWindowState, - mWindowManagerFuncs.getInputMethodWindowLw(), navColorWin); - - return vis; - } - - private boolean drawsStatusBarBackground(int vis, WindowState win) { - if (!mStatusBarController.isTransparentAllowed(win)) { - return false; - } - if (win == null) { - return true; - } - - final boolean drawsSystemBars = - (win.getAttrs().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; - final boolean forceDrawsSystemBars = - (win.getAttrs().privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0; - - return forceDrawsSystemBars || drawsSystemBars && (vis & View.STATUS_BAR_TRANSLUCENT) == 0; - } - - /** - * @return the current visibility flags with the nav-bar opacity related flags toggled based - * on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}. - */ - private int configureNavBarOpacity(int visibility, boolean dockedStackVisible, - boolean freeformStackVisible, boolean isDockedDividerResizing) { - if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) { - if (dockedStackVisible || freeformStackVisible || isDockedDividerResizing) { - visibility = setNavBarOpaqueFlag(visibility); - } - } else if (mNavBarOpacityMode == NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE) { - if (isDockedDividerResizing) { - visibility = setNavBarOpaqueFlag(visibility); - } else if (freeformStackVisible) { - visibility = setNavBarTranslucentFlag(visibility); - } else { - visibility = setNavBarOpaqueFlag(visibility); - } - } - - if (!areTranslucentBarsAllowed()) { - visibility &= ~View.NAVIGATION_BAR_TRANSLUCENT; - } - return visibility; - } - - private int setNavBarOpaqueFlag(int visibility) { - return visibility &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT); - } - - private int setNavBarTranslucentFlag(int visibility) { - visibility &= ~View.NAVIGATION_BAR_TRANSPARENT; - return visibility |= View.NAVIGATION_BAR_TRANSLUCENT; - } - - private void clearClearableFlagsLw() { - int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_CLEARABLE_FLAGS; - if (newVal != mResettingSystemUiFlags) { - mResettingSystemUiFlags = newVal; - mWindowManagerFuncs.reevaluateStatusBarVisibility(); - } - } - - private boolean isImmersiveMode(int vis) { - final int flags = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; - return mNavigationBar != null - && (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0 - && (vis & flags) != 0 - && canHideNavigationBar(); - } - - private static boolean isNavBarEmpty(int systemUiFlags) { - final int disableNavigationBar = (View.STATUS_BAR_DISABLE_HOME - | View.STATUS_BAR_DISABLE_BACK - | View.STATUS_BAR_DISABLE_RECENT); - - return (systemUiFlags & disableNavigationBar) == disableNavigationBar; - } - - /** - * @return whether the navigation or status bar can be made translucent - * - * This should return true unless touch exploration is not enabled or - * R.boolean.config_enableTranslucentDecor is false. - */ - private boolean areTranslucentBarsAllowed() { - return mTranslucentDecorEnabled; - } - // Use this instead of checking config_showNavigationBar so that it can be consistently // overridden by qemu.hw.mainkeys in the emulator. @Override @@ -7926,44 +5192,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - public boolean shouldRotateSeamlessly(DisplayRotation displayRotation, int oldRotation, - int newRotation) { - // For the upside down rotation we don't rotate seamlessly as the navigation - // bar moves position. - // Note most apps (using orientation:sensor or user as opposed to fullSensor) - // will not enter the reverse portrait orientation, so actually the - // orientation won't change at all. - if (oldRotation == displayRotation.getUpsideDownRotation() - || newRotation == displayRotation.getUpsideDownRotation()) { - return false; - } - // If the navigation bar can't change sides, then it will - // jump when we change orientations and we don't rotate - // seamlessly. - if (!displayRotation.getDisplayPolicy().navigationBarCanMove()) { - return false; - } - - final WindowState w = mTopFullscreenOpaqueWindowState; - if (w != mFocusedWindow) { - return false; - } - - // We only enable seamless rotation if the top window has requested - // it and is in the fullscreen opaque state. Seamless rotation - // requires freezing various Surface states and won't work well - // with animations, so we disable it in the animation case for now. - if (w != null && !w.isAnimatingLw() && - w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS) { - return true; - } - return false; - } - - @Override public void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); - proto.write(LAST_SYSTEM_UI_FLAGS, mLastSystemUiFlags); proto.write(ROTATION_MODE, mDefaultDisplayRotation.getUserRotationMode()); proto.write(ROTATION, mDefaultDisplayRotation.getUserRotation()); proto.write(ORIENTATION, mDefaultDisplayRotation.getCurrentAppOrientation()); @@ -7971,30 +5201,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { proto.write(KEYGUARD_DRAW_COMPLETE, mDefaultDisplayPolicy.isKeyguardDrawComplete()); proto.write(WINDOW_MANAGER_DRAW_COMPLETE, mDefaultDisplayPolicy.isWindowManagerDrawComplete()); - if (mFocusedApp != null) { - proto.write(FOCUSED_APP_TOKEN, mFocusedApp.toString()); - } - if (mFocusedWindow != null) { - mFocusedWindow.writeIdentifierToProto(proto, FOCUSED_WINDOW); - } - if (mTopFullscreenOpaqueWindowState != null) { - mTopFullscreenOpaqueWindowState.writeIdentifierToProto( - proto, TOP_FULLSCREEN_OPAQUE_WINDOW); - } - if (mTopFullscreenOpaqueOrDimmingWindowState != null) { - mTopFullscreenOpaqueOrDimmingWindowState.writeIdentifierToProto( - proto, TOP_FULLSCREEN_OPAQUE_OR_DIMMING_WINDOW); - } proto.write(KEYGUARD_OCCLUDED, mKeyguardOccluded); proto.write(KEYGUARD_OCCLUDED_CHANGED, mKeyguardOccludedChanged); proto.write(KEYGUARD_OCCLUDED_PENDING, mPendingKeyguardOccluded); - proto.write(FORCE_STATUS_BAR, mForceStatusBar); - proto.write(FORCE_STATUS_BAR_FROM_KEYGUARD, mForceStatusBarFromKeyguard); - mStatusBarController.writeToProto(proto, STATUS_BAR); - mNavigationBarController.writeToProto(proto, NAVIGATION_BAR); - if (mDefaultOrientationListener != null) { - mDefaultOrientationListener.writeToProto(proto, ORIENTATION_LISTENER); - } if (mKeyguardDelegate != null) { mKeyguardDelegate.writeToProto(proto, KEYGUARD_DELEGATE); } @@ -8008,19 +5217,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(" mSystemBooted="); pw.println(mSystemBooted); pw.print(prefix); pw.print("mCameraLensCoverState="); pw.println(WindowManagerFuncs.cameraLensStateToString(mCameraLensCoverState)); - if (mLastSystemUiFlags != 0 || mResettingSystemUiFlags != 0 - || mForceClearedSystemUiFlags != 0) { - pw.print(prefix); pw.print("mLastSystemUiFlags=0x"); - pw.print(Integer.toHexString(mLastSystemUiFlags)); - pw.print(" mResettingSystemUiFlags=0x"); - pw.print(Integer.toHexString(mResettingSystemUiFlags)); - pw.print(" mForceClearedSystemUiFlags=0x"); - pw.println(Integer.toHexString(mForceClearedSystemUiFlags)); - } - if (mLastFocusNeedsMenu) { - pw.print(prefix); pw.print("mLastFocusNeedsMenu="); - pw.println(mLastFocusNeedsMenu); - } pw.print(prefix); pw.print("mWakeGestureEnabledSetting="); pw.println(mWakeGestureEnabledSetting); @@ -8083,50 +5279,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { final int key = mDisplayHomeButtonHandlers.keyAt(i); pw.println(mDisplayHomeButtonHandlers.get(key)); } - pw.print(prefix); pw.print("mDockLayer="); pw.print(mDockLayer); - pw.print(" mStatusBarLayer="); pw.println(mStatusBarLayer); - pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream); - pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen); - pw.print(" mDreamingSleepToken="); pw.println(mDreamingSleepToken); - if (mStatusBar != null) { - pw.print(prefix); pw.print("mStatusBar="); - pw.print(mStatusBar); pw.print(" isStatusBarKeyguard="); - pw.println(isStatusBarKeyguard()); - } - if (mNavigationBar != null) { - pw.print(prefix); pw.print("mNavigationBar="); - pw.println(mNavigationBar); - } - if (mFocusedWindow != null) { - pw.print(prefix); pw.print("mFocusedWindow="); - pw.println(mFocusedWindow); - } - if (mFocusedApp != null) { - pw.print(prefix); pw.print("mFocusedApp="); - pw.println(mFocusedApp); - } - if (mTopFullscreenOpaqueWindowState != null) { - pw.print(prefix); pw.print("mTopFullscreenOpaqueWindowState="); - pw.println(mTopFullscreenOpaqueWindowState); - } - if (mTopFullscreenOpaqueOrDimmingWindowState != null) { - pw.print(prefix); pw.print("mTopFullscreenOpaqueOrDimmingWindowState="); - pw.println(mTopFullscreenOpaqueOrDimmingWindowState); - } - if (mForcingShowNavBar) { - pw.print(prefix); pw.print("mForcingShowNavBar="); - pw.println(mForcingShowNavBar); pw.print( "mForcingShowNavBarLayer="); - pw.println(mForcingShowNavBarLayer); - } - pw.print(prefix); pw.print("mTopIsFullscreen="); pw.print(mTopIsFullscreen); - pw.print(" mKeyguardOccluded="); pw.println(mKeyguardOccluded); - pw.print(prefix); - pw.print("mKeyguardOccludedChanged="); pw.print(mKeyguardOccludedChanged); + pw.print(prefix); pw.print("mKeyguardOccluded="); pw.print(mKeyguardOccluded); + pw.print(" mKeyguardOccludedChanged="); pw.print(mKeyguardOccludedChanged); pw.print(" mPendingKeyguardOccluded="); pw.println(mPendingKeyguardOccluded); - pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar); - pw.print(" mForceStatusBarFromKeyguard="); - pw.println(mForceStatusBarFromKeyguard); - pw.print(prefix); pw.print("mAllowLockscreenWhenOn="); pw.print(mAllowLockscreenWhenOn); + pw.print(prefix); pw.print("mAllowLockscreenWhenOnDisplays="); + pw.print(!mAllowLockscreenWhenOnDisplays.isEmpty()); pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout); pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive); if (mHasFeatureLeanback) { @@ -8139,16 +5296,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { } mGlobalKeyManager.dump(prefix, pw); - mStatusBarController.dump(pw, prefix); - mNavigationBarController.dump(pw, prefix); - PolicyControl.dump(prefix, pw); if (mWakeGestureListener != null) { mWakeGestureListener.dump(pw, prefix); } - if (mDefaultOrientationListener != null) { - mDefaultOrientationListener.dump(pw, prefix); - } if (mBurnInProtectionHelper != null) { mBurnInProtectionHelper.dump(prefix, pw); } @@ -8310,11 +5461,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - public void onLockTaskStateChangedLw(int lockTaskState) { - mImmersiveModeConfirmation.onLockTaskModeChangedLw(lockTaskState); - } - - @Override public boolean setAodShowing(boolean aodShowing) { if (mAodShowing != aodShowing) { mAodShowing = aodShowing; diff --git a/services/core/java/com/android/server/policy/StatusBarController.java b/services/core/java/com/android/server/policy/StatusBarController.java deleted file mode 100644 index e6e4d7f98250..000000000000 --- a/services/core/java/com/android/server/policy/StatusBarController.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.policy; - -import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; -import static android.view.WindowManager.LayoutParams.MATCH_PARENT; -import static com.android.server.wm.WindowManagerInternal.AppTransitionListener; - -import android.app.StatusBarManager; -import android.os.IBinder; -import android.os.SystemClock; -import android.view.View; -import android.view.animation.Animation; -import android.view.animation.AnimationSet; -import android.view.animation.Interpolator; -import android.view.animation.TranslateAnimation; - -import com.android.server.LocalServices; -import com.android.server.statusbar.StatusBarManagerInternal; - -/** - * Implements status bar specific behavior. - */ -public class StatusBarController extends BarController { - - private final AppTransitionListener mAppTransitionListener - = new AppTransitionListener() { - - @Override - public void onAppTransitionPendingLocked() { - mHandler.post(new Runnable() { - @Override - public void run() { - StatusBarManagerInternal statusbar = getStatusBarInternal(); - if (statusbar != null) { - statusbar.appTransitionPending(); - } - } - }); - } - - @Override - public int onAppTransitionStartingLocked(int transit, IBinder openToken, - IBinder closeToken, long duration, long statusBarAnimationStartTime, - long statusBarAnimationDuration) { - mHandler.post(new Runnable() { - @Override - public void run() { - StatusBarManagerInternal statusbar = getStatusBarInternal(); - if (statusbar != null) { - statusbar.appTransitionStarting(statusBarAnimationStartTime, - statusBarAnimationDuration); - } - } - }); - return 0; - } - - @Override - public void onAppTransitionCancelledLocked(int transit) { - mHandler.post(new Runnable() { - @Override - public void run() { - StatusBarManagerInternal statusbar = getStatusBarInternal(); - if (statusbar != null) { - statusbar.appTransitionCancelled(); - } - } - }); - } - - @Override - public void onAppTransitionFinishedLocked(IBinder token) { - mHandler.post(new Runnable() { - @Override - public void run() { - StatusBarManagerInternal statusbar = LocalServices.getService( - StatusBarManagerInternal.class); - if (statusbar != null) { - statusbar.appTransitionFinished(); - } - } - }); - } - }; - - public StatusBarController() { - super("StatusBar", - View.STATUS_BAR_TRANSIENT, - View.STATUS_BAR_UNHIDE, - View.STATUS_BAR_TRANSLUCENT, - StatusBarManager.WINDOW_STATUS_BAR, - FLAG_TRANSLUCENT_STATUS, - View.STATUS_BAR_TRANSPARENT); - } - - - public void setTopAppHidesStatusBar(boolean hidesStatusBar) { - StatusBarManagerInternal statusbar = getStatusBarInternal(); - if (statusbar != null) { - statusbar.setTopAppHidesStatusBar(hidesStatusBar); - } - } - - @Override - protected boolean skipAnimation() { - return mWin.getAttrs().height == MATCH_PARENT; - } - - public AppTransitionListener getAppTransitionListener() { - return mAppTransitionListener; - } -} diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index e1c4acf2ffa1..3d474e331fb3 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -65,7 +65,6 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.annotation.Nullable; -import android.app.ActivityManager; import android.app.WindowConfiguration; import android.content.Context; import android.content.res.CompatibilityInfo; @@ -78,7 +77,6 @@ import android.os.RemoteException; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.Display; -import android.view.DisplayCutout; import android.view.IApplicationToken; import android.view.IWindowManager; import android.view.InputEventReceiver; @@ -90,7 +88,6 @@ import android.view.animation.Animation; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.IShortcutService; -import com.android.server.wm.DisplayFrames; import com.android.server.wm.DisplayRotation; import com.android.server.wm.WindowFrames; @@ -173,11 +170,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { void onKeyguardOccludedChangedLw(boolean occluded); /** - * Called when the resource overlays change. - */ - default void onOverlayChangedLw(DisplayContentInfo displayContentInfo) {} - - /** * Interface to the Window Manager state associated with a particular * window. You can hold on to an instance of this interface from the call * to prepareAddWindow() until removeWindow(). @@ -526,11 +518,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public static final int CAMERA_LENS_COVERED = 1; /** - * Ask the window manager to re-evaluate the system UI flags. - */ - public void reevaluateStatusBarVisibility(); - - /** * Add a input consumer which will consume all input events going to any window below it. */ public InputConsumer createInputConsumer(Looper looper, String name, @@ -573,22 +560,12 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { void unregisterPointerEventListener(PointerEventListener listener, int displayId); /** - * @return The content insets of the docked divider window. - */ - int getDockedDividerInsetsLw(); - - /** * Retrieves the {@param outBounds} from the stack matching the {@param windowingMode} and * {@param activityType}. */ void getStackBounds(int windowingMode, int activityType, Rect outBounds); /** - * Notifies window manager that {@link #isShowingDreamLw} has changed. - */ - void notifyShowingDreamChanged(); - - /** * @return The currently active input method window. */ WindowState getInputMethodWindowLw(); @@ -648,7 +625,15 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { */ void onKeyguardShowingAndNotOccludedChanged(); - DisplayContentInfo getDefaultDisplayContentInfo(); + /** + * Notifies window manager that power key is being pressed. + */ + void onPowerKeyDown(boolean isScreenOn); + + /** + * Notifies window manager that user is switched. + */ + void onUserSwitched(); } /** @@ -735,17 +720,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public boolean checkShowToOwnerOnly(WindowManager.LayoutParams attrs); /** - * Sanitize the layout parameters coming from a client. Allows the policy - * to do things like ensure that windows of a specific type can't take - * input focus. - * - * @param attrs The window layout parameters to be modified. These values - * are modified in-place. - */ - public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs, - boolean hasStatusBarServicePermission); - - /** * After the window manager has computed the current configuration based * on its knowledge of the display and input devices, it gives the policy * a chance to adjust the information contained in it. If you want to @@ -941,40 +915,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public int getMaxWallpaperLayer(); /** - * Return the display width available after excluding any screen - * decorations that could never be removed in Honeycomb. That is, system bar or - * button bar. - */ - public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation, - int uiMode, int displayId, DisplayCutout displayCutout); - - /** - * Return the display height available after excluding any screen - * decorations that could never be removed in Honeycomb. That is, system bar or - * button bar. - */ - public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation, - int uiMode, int displayId, DisplayCutout displayCutout); - - /** - * Return the available screen width that we should report for the - * configuration. This must be no larger than - * {@link #getNonDecorDisplayWidth(int, int, int, int, int, DisplayCutout)}; it may be smaller - * than that to account for more transient decoration like a status bar. - */ - public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation, - int uiMode, int displayId, DisplayCutout displayCutout); - - /** - * Return the available screen height that we should report for the - * configuration. This must be no larger than - * {@link #getNonDecorDisplayHeight(int, int, int, int, int, DisplayCutout)}; it may be smaller - * than that to account for more transient decoration like a status bar. - */ - public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, - int uiMode, int displayId, DisplayCutout displayCutout); - - /** * Return whether the given window can become the Keyguard window. Typically returns true for * the StatusBar. */ @@ -1013,65 +953,11 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { int logo, int windowFlags, Configuration overrideConfig, int displayId); /** - * Prepare for a window being added to the window manager. You can throw an - * exception here to prevent the window being added, or do whatever setup - * you need to keep track of the window. - * - * @param win The window being added. - * @param attrs The window's LayoutParams. - * - * @return {@link WindowManagerGlobal#ADD_OKAY} if the add can proceed, else an - * error code to abort the add. - */ - public int prepareAddWindowLw(WindowState win, - WindowManager.LayoutParams attrs); - - /** - * Called when a window is being removed from a window manager. Must not - * throw an exception -- clean up as much as possible. - * - * @param win The window being removed. - */ - public void removeWindowLw(WindowState win); - - /** - * Control the animation to run when a window's state changes. Return a - * non-0 number to force the animation to a specific resource ID, or 0 - * to use the default animation. + * Set or clear a window which can behave as the keyguard. * - * @param win The window that is changing. - * @param transit What is happening to the window: {@link #TRANSIT_ENTER}, - * {@link #TRANSIT_EXIT}, {@link #TRANSIT_SHOW}, or - * {@link #TRANSIT_HIDE}. - * - * @return Resource ID of the actual animation to use, or 0 for none. - */ - public int selectAnimationLw(WindowState win, int transit); - - /** - * Determine the animation to run for a rotation transition based on the - * top fullscreen windows {@link WindowManager.LayoutParams#rotationAnimation} - * and whether it is currently fullscreen and frontmost. - * - * @param anim The exiting animation resource id is stored in anim[0], the - * entering animation resource id is stored in anim[1]. - */ - public void selectRotationAnimationLw(int anim[]); - - /** - * Validate whether the current top fullscreen has specified the same - * {@link WindowManager.LayoutParams#rotationAnimation} value as that - * being passed in from the previous top fullscreen window. - * - * @param exitAnimId exiting resource id from the previous window. - * @param enterAnimId entering resource id from the previous window. - * @param forceDefault For rotation animations only, if true ignore the - * animation values and just return false. - * @return true if the previous values are still valid, false if they - * should be replaced with the default. + * @param win The window which can behave as the keyguard. */ - public boolean validateRotationAnimationLw(int exitAnimId, int enterAnimId, - boolean forceDefault); + void setKeyguardCandidateLw(@Nullable WindowState win); /** * Create and return an animation to re-display a window that was force hidden by Keyguard. @@ -1148,100 +1034,21 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags); /** - * Called when layout of the windows is about to start. + * Apply the keyguard policy to a specific window. * - * @param displayFrames frames of the display we are doing layout on. - * @param uiMode The current uiMode in configuration. + * @param win The window to apply the keyguard policy. + * @param imeTarget The current IME target window. */ - default void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {} + void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget); /** - * Returns the bottom-most layer of the system decor, above which no policy decor should - * be applied. - */ - public int getSystemDecorLayerLw(); - - /** - * Called for each window attached to the window manager as layout is proceeding. The - * implementation of this function must take care of setting the window's frame, either here or - * in finishLayout(). + * Called when the state of allow-lockscreen-when-on of the display is changed. See + * {@link WindowManager.LayoutParams#FLAG_ALLOW_LOCK_WHILE_SCREEN_ON} * - * @param win The window being positioned. - * @param attached For sub-windows, the window it is attached to; this - * window will already have had layoutWindow() called on it - * so you can use its Rect. Otherwise null. - * @param displayFrames The display frames. + * @param displayId The ID of the display. + * @param allow Whether the display allows showing lockscreen when it is on. */ - default void layoutWindowLw( - WindowState win, WindowState attached, DisplayFrames displayFrames) {} - - /** - * Return the layout hints for a newly added window. These values are computed on the - * most recent layout, so they are not guaranteed to be correct. - * - * @param attrs The LayoutParams of the window. - * @param taskBounds The bounds of the task this window is on or {@code null} if no task is - * associated with the window. - * @param displayFrames display frames. - * @param floatingStack Whether the window's stack is floating. - * @param outFrame The frame of the window. - * @param outContentInsets The areas covered by system windows, expressed as positive insets. - * @param outStableInsets The areas covered by stable system windows irrespective of their - * current visibility. Expressed as positive insets. - * @param outOutsets The areas that are not real display, but we would like to treat as such. - * @param outDisplayCutout The area that has been cut away from the display. - * @return Whether to always consume the navigation bar. - * See {@link #isNavBarForcedShownLw(WindowState)}. - */ - default boolean getLayoutHintLw(WindowManager.LayoutParams attrs, Rect taskBounds, - DisplayFrames displayFrames, boolean floatingStack, - Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, - DisplayCutout.ParcelableWrapper outDisplayCutout) { - return false; - } - - /** - * Called following layout of all windows before each window has policy applied. - * - * @param displayWidth The current full width of the screen. - * @param displayHeight The current full height of the screen. - */ - public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight); - - /** - * Called following layout of all window to apply policy to each window. - * - * @param win The window being positioned. - * @param attrs The LayoutParams of the window. - * @param attached For sub-windows, the window it is attached to. Otherwise null. - */ - public void applyPostLayoutPolicyLw(WindowState win, - WindowManager.LayoutParams attrs, WindowState attached, WindowState imeTarget); - - /** - * Called following layout of all windows and after policy has been applied - * to each window. If in this function you do - * something that may have modified the animation state of another window, - * be sure to return non-zero in order to perform another pass through layout. - * - * @return Return any bit set of {@link #FINISH_LAYOUT_REDO_LAYOUT}, - * {@link #FINISH_LAYOUT_REDO_CONFIG}, {@link #FINISH_LAYOUT_REDO_WALLPAPER}, - * or {@link #FINISH_LAYOUT_REDO_ANIM}. - */ - public int finishPostLayoutPolicyLw(); - - /** - * Return true if it is okay to perform animations for an app transition - * that is about to occur. You may return false for this if, for example, - * the dream window is currently displayed so the switch should happen - * immediately. - */ - public boolean allowAppAnimationsLw(); - - /** - * A new window has been focused. - */ - public int focusChangedLw(WindowState lastFocus, WindowState newFocus); + void setAllowLockscreenWhenOn(int displayId, boolean allow); /** * Called when the device has started waking up. @@ -1430,8 +1237,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { */ public boolean isKeyguardDrawnLw(); - public boolean isShowingDreamLw(); - /** * Called when the system is mostly done booting to set whether * the system should go into safe mode. @@ -1491,14 +1296,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public void keepScreenOnStoppedLw(); /** - * Called when a new system UI visibility is being reported, allowing - * the policy to adjust what is actually reported. - * @param visibility The raw visibility reported by the status bar. - * @return The new desired visibility. - */ - public int adjustSystemUiVisibilityLw(int visibility); - - /** * Called by System UI to notify of changes to the visibility of Recents. */ public void setRecentsVisibilityLw(boolean visible); @@ -1548,6 +1345,16 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public void showGlobalActions(); /** + * Returns whether the user setup is complete. + */ + boolean isUserSetupComplete(); + + /** + * Returns the current UI mode. + */ + int getUiMode(); + + /** * Called when the current user changes. Guaranteed to be called before the broadcast * of the new user id is made to all listeners. * @@ -1602,69 +1409,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public void startKeyguardExitAnimation(long startTime, long fadeoutDuration); /** - * Calculates the stable insets without running a layout. - * - * @param displayRotation the current display rotation - * @param displayWidth the current display width - * @param displayHeight the current display height - * @param displayCutout the current display cutout - * @param outInsets the insets to return - */ - public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight, - DisplayCutout displayCutout, Rect outInsets); - - - /** - * @return true if the navigation bar is forced to stay visible - */ - public boolean isNavBarForcedShownLw(WindowState win); - - /** - * @return The side of the screen where navigation bar is positioned. - * @see #NAV_BAR_LEFT - * @see #NAV_BAR_RIGHT - * @see #NAV_BAR_BOTTOM - */ - @NavigationBarPosition - int getNavBarPosition(); - - /** - * Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system - * bar or button bar. See {@link #getNonDecorDisplayWidth}. - * - * @param displayRotation the current display rotation - * @param displayWidth the current display width - * @param displayHeight the current display height - * @param displayCutout the current display cutout - * @param outInsets the insets to return - */ - public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight, - DisplayCutout displayCutout, Rect outInsets); - - /** - * @param displayRotation the current display rotation - * @param displayWidth the current display width - * @param displayHeight the current display height - * @param dockSide the dockside asking if allowed - * @param originalDockSide the side that was original docked to in split screen - * @return True if a specified {@param dockSide} is allowed on the current device, or false - * otherwise. It is guaranteed that at least one dock side for a particular orientation - * is allowed, so for example, if DOCKED_RIGHT is not allowed, DOCKED_LEFT is allowed. - * If navigation bar is movable then the docked side would bias towards the - * {@param originalDockSide}. - */ - public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth, - int displayHeight, int displayRotation); - - /** - * Called when the configuration has changed, and it's safe to load new values from resources. - */ - public void onConfigurationChanged(DisplayContentInfo displayContentInfo); - - public boolean shouldRotateSeamlessly(DisplayRotation displayRotation, - int oldRotation, int newRotation); - - /** * Called when System UI has been started. */ void onSystemUiStarted(); @@ -1697,17 +1441,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public void requestUserActivityNotification(); /** - * Called when the state of lock task mode changes. This should be used to disable immersive - * mode confirmation. - * - * @param lockTaskState the new lock task mode state. One of - * {@link ActivityManager#LOCK_TASK_MODE_NONE}, - * {@link ActivityManager#LOCK_TASK_MODE_LOCKED}, - * {@link ActivityManager#LOCK_TASK_MODE_PINNED}. - */ - void onLockTaskStateChangedLw(int lockTaskState); - - /** * Updates the flag about whether AOD is showing. * * @return whether the value was changed. diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 6f6846dc0363..3179ce90a6f9 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -194,8 +194,10 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { "/system/bin/traced", // Perfetto. "/system/bin/traced_probes", // Perfetto. "webview_zygote", - "zygote", - "zygote64", + // Temporarily excluded zygote to investigate its forking consequences in + // NativeProcessMemoryState. + // "zygote", + // "zygote64", }; diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index 095eaa5fde67..a66f0caee56c 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -44,36 +44,42 @@ public interface StatusBarManagerInternal { */ void showPictureInPictureMenu(); - void setWindowState(int window, int state); + void setWindowState(int displayId, int window, int state); /** * Notifies the status bar that an app transition is pending to delay applying some flags with * visual impact until {@link #appTransitionReady} is called. + * + * @param displayId the ID of the display which has this event. */ - void appTransitionPending(); + void appTransitionPending(int displayId); /** * Notifies the status bar that a pending app transition has been cancelled. + * + * @param displayId the ID of the display which has this event. */ - void appTransitionCancelled(); + void appTransitionCancelled(int displayId); /** * Notifies the status bar that an app transition is now being executed. * + * @param displayId the ID of the display which has this event. * @param statusBarAnimationsStartTime the desired start time for all visual animations in the * status bar caused by this app transition in uptime millis * @param statusBarAnimationsDuration the duration for all visual animations in the status * bar caused by this app transition in millis */ - void appTransitionStarting(long statusBarAnimationsStartTime, long statusBarAnimationsDuration); + void appTransitionStarting(int displayId, long statusBarAnimationsStartTime, + long statusBarAnimationsDuration); void startAssist(Bundle args); void onCameraLaunchGestureDetected(int source); - void topAppWindowChanged(boolean menuVisible); - void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask, - Rect fullscreenBounds, Rect dockedBounds, String cause); + void topAppWindowChanged(int displayId, boolean menuVisible); + void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis, + int mask, Rect fullscreenBounds, Rect dockedBounds, String cause); void toggleSplitScreen(); - void appTransitionFinished(); + void appTransitionFinished(int displayId); void toggleRecentApps(); diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 3e07ebea6a17..1eb44a058d61 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -39,6 +39,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; +import android.view.Display; import com.android.internal.R; import com.android.internal.statusbar.IStatusBar; @@ -237,14 +238,22 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } @Override - public void topAppWindowChanged(boolean menuVisible) { + public void topAppWindowChanged(int displayId, boolean menuVisible) { + if (displayId != Display.DEFAULT_DISPLAY) { + // TODO (b/117478341): Resolve one status bar/ navigation bar assumption + return; + } StatusBarManagerService.this.topAppWindowChanged(menuVisible); } @Override - public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, - int mask, - Rect fullscreenBounds, Rect dockedBounds, String cause) { + public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, + int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds, + String cause) { + if (displayId != Display.DEFAULT_DISPLAY) { + // TODO (b/117478341): Resolve one status bar/ navigation bar assumption + return; + } StatusBarManagerService.this.setSystemUiVisibility(vis, fullscreenStackVis, dockedStackVis, mask, fullscreenBounds, dockedBounds, cause); } @@ -259,8 +268,13 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } } - public void appTransitionFinished() { + @Override + public void appTransitionFinished(int displayId) { enforceStatusBarService(); + if (displayId != Display.DEFAULT_DISPLAY) { + // TODO (b/117478341): Resolve one status bar/ navigation bar assumption + return; + } if (mBar != null) { try { mBar.appTransitionFinished(); @@ -358,7 +372,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } @Override - public void setWindowState(int window, int state) { + public void setWindowState(int displayId, int window, int state) { + if (displayId != Display.DEFAULT_DISPLAY) { + // TODO (b/117478341): Resolve one status bar/ navigation bar assumption + return; + } if (mBar != null) { try { mBar.setWindowState(window, state); @@ -367,7 +385,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } @Override - public void appTransitionPending() { + public void appTransitionPending(int displayId) { + if (displayId != Display.DEFAULT_DISPLAY) { + // TODO (b/117478341): Resolve one status bar/ navigation bar assumption + return; + } if (mBar != null) { try { mBar.appTransitionPending(); @@ -376,7 +398,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } @Override - public void appTransitionCancelled() { + public void appTransitionCancelled(int displayId) { + if (displayId != Display.DEFAULT_DISPLAY) { + // TODO (b/117478341): Resolve one status bar/ navigation bar assumption + return; + } if (mBar != null) { try { mBar.appTransitionCancelled(); @@ -385,8 +411,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } @Override - public void appTransitionStarting(long statusBarAnimationsStartTime, + public void appTransitionStarting(int displayId, long statusBarAnimationsStartTime, long statusBarAnimationsDuration) { + if (displayId != Display.DEFAULT_DISPLAY) { + // TODO (b/117478341): Resolve one status bar/ navigation bar assumption + return; + } if (mBar != null) { try { mBar.appTransitionStarting( diff --git a/media/java/android/media/update/SessionToken2Provider.java b/services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java index 95d6ce07b8a8..7310af3a6eb6 100644 --- a/media/java/android/media/update/SessionToken2Provider.java +++ b/services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 The Android Open Source Project + * 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. @@ -14,21 +14,15 @@ * limitations under the License. */ -package android.media.update; +package com.android.server.updates; -import android.os.Bundle; +public class ConversationActionsInstallReceiver extends ConfigUpdateInstallReceiver { -/** - * @hide - */ -public interface SessionToken2Provider { - String getPackageName_impl(); - String getId_imp(); - int getType_impl(); - int getUid_impl(); - Bundle toBundle_impl(); - - int hashCode_impl(); - boolean equals_impl(Object obj); - String toString_impl(); + public ConversationActionsInstallReceiver() { + super( + "/data/misc/textclassifier/", + "actions_suggestions.model", + "metadata/actions_suggestions", + "version"); + } } diff --git a/services/core/java/com/android/server/updates/LangIdInstallReceiver.java b/services/core/java/com/android/server/updates/LangIdInstallReceiver.java index dfe02ece7160..05dad2178798 100644 --- a/services/core/java/com/android/server/updates/LangIdInstallReceiver.java +++ b/services/core/java/com/android/server/updates/LangIdInstallReceiver.java @@ -21,8 +21,8 @@ public class LangIdInstallReceiver extends ConfigUpdateInstallReceiver { public LangIdInstallReceiver() { super( "/data/misc/textclassifier/", - "textclassifier.langid.model", - "metadata/langid", + "lang_id.model", + "metadata/lang_id", "version"); } } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 409d2b43f694..6ede423f63c8 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -88,7 +88,6 @@ import android.util.SparseArray; import android.util.Xml; import android.view.Display; import android.view.IWindowManager; -import android.view.WindowManager; import com.android.internal.R; import com.android.internal.content.PackageMonitor; @@ -487,6 +486,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub private void generateCrop(WallpaperData wallpaper) { boolean success = false; + // Only generate crop for default display. + final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY); Rect cropHint = new Rect(wallpaper.cropHint); if (DEBUG) { @@ -494,7 +495,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub + Integer.toHexString(wallpaper.whichPending) + " to " + wallpaper.cropFile.getName() + " crop=(" + cropHint.width() + 'x' + cropHint.height() - + ") dim=(" + wallpaper.width + 'x' + wallpaper.height + ')'); + + ") dim=(" + wpData.mWidth + 'x' + wpData.mHeight + ')'); } // Analyse the source; needed in multiple cases @@ -533,11 +534,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } // scale if the crop height winds up not matching the recommended metrics - needScale = (wallpaper.height != cropHint.height()); + needScale = (wpData.mHeight != cropHint.height()); if (DEBUG) { Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height()); - Slog.v(TAG, "dims: w=" + wallpaper.width + " h=" + wallpaper.height); + Slog.v(TAG, "dims: w=" + wpData.mWidth + " h=" + wpData.mHeight); Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight); Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale); } @@ -567,7 +568,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // just let the decode take care of it because we also want to remap where the // cropHint rectangle lies in the decoded [super]rect. final BitmapFactory.Options scaler; - final int actualScale = cropHint.height() / wallpaper.height; + final int actualScale = cropHint.height() / wpData.mHeight; int scale = 1; while (2*scale < actualScale) { scale *= 2; @@ -593,17 +594,18 @@ public class WallpaperManagerService extends IWallpaperManager.Stub cropHint.offsetTo(0, 0); cropHint.right /= scale; // adjust by downsampling factor cropHint.bottom /= scale; - final float heightR = ((float)wallpaper.height) / ((float)cropHint.height()); + final float heightR = + ((float) wpData.mHeight) / ((float) cropHint.height()); if (DEBUG) { Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint); } final int destWidth = (int)(cropHint.width() * heightR); final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped, - destWidth, wallpaper.height, true); + destWidth, wpData.mHeight, true); if (DEBUG) { Slog.v(TAG, "Final extract:"); - Slog.v(TAG, " dims: w=" + wallpaper.width - + " h=" + wallpaper.height); + Slog.v(TAG, " dims: w=" + wpData.mWidth + + " h=" + wpData.mHeight); Slog.v(TAG, " out: w=" + finalCrop.getWidth() + " h=" + finalCrop.getHeight()); } @@ -670,13 +672,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (connector == null) return; connector.disconnectLocked(); mLastWallpaper.connection.removeDisplayConnector(displayId); + mLastWallpaper.removeDisplayData(displayId); } } } @Override public void onDisplayChanged(int displayId) { - // TODO(b/115486823) Review that do we need to handle display changes. } }; @@ -778,16 +780,23 @@ public class WallpaperManagerService extends IWallpaperManager.Stub private RemoteCallbackList<IWallpaperManagerCallback> callbacks = new RemoteCallbackList<IWallpaperManagerCallback>(); - int width = -1; - int height = -1; + private static final class DisplayData { + int mWidth = -1; + int mHeight = -1; + final Rect mPadding = new Rect(0, 0, 0, 0); + final int mDisplayId; + + DisplayData(int displayId) { + mDisplayId = displayId; + } + } + private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>(); /** * The crop hint supplied for displaying a subset of the source image */ final Rect cropHint = new Rect(0, 0, 0, 0); - final Rect padding = new Rect(0, 0, 0, 0); - WallpaperData(int userId, String inputFileName, String cropFileName) { this.userId = userId; final File wallpaperDir = getWallpaperDir(userId); @@ -803,6 +812,44 @@ public class WallpaperManagerService extends IWallpaperManager.Stub boolean sourceExists() { return wallpaperFile.exists(); } + + void removeDisplayData(int displayId) { + mDisplayDatas.remove(displayId); + } + } + + private WallpaperData.DisplayData getDisplayDataOrCreate(WallpaperData data, int displayId) { + WallpaperData.DisplayData wpdData = data.mDisplayDatas.get(displayId); + if (wpdData == null) { + wpdData = new WallpaperData.DisplayData(displayId); + ensureSaneWallpaperDisplaySize(wpdData, displayId); + data.mDisplayDatas.append(displayId, wpdData); + } + return wpdData; + } + + private void ensureSaneWallpaperDisplaySize(WallpaperData.DisplayData wpdData, + int displayId) { + // We always want to have some reasonable width hint. + final int baseSize = getMaximumSizeDimension(displayId); + if (wpdData.mWidth < baseSize) { + wpdData.mWidth = baseSize; + } + if (wpdData.mHeight < baseSize) { + wpdData.mHeight = baseSize; + } + } + + private int getMaximumSizeDimension(int displayId) { + Display display = mDisplayManager.getDisplay(displayId); + return display.getMaximumSizeDimension(); + } + + void forEachDisplayData(WallpaperData data, Consumer<WallpaperData.DisplayData> action) { + for (int i = data.mDisplayDatas.size() - 1; i >= 0; i--) { + final WallpaperData.DisplayData wpdData = data.mDisplayDatas.valueAt(i); + action.accept(wpdData); + } } int makeWallpaperIdLocked() { @@ -830,9 +877,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } void ensureStatusHandled() { + final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(mWallpaper, + mDisplayId); if (mDimensionsChanged) { try { - mEngine.setDesiredSize(mWallpaper.width, mWallpaper.height); + mEngine.setDesiredSize(wpdData.mWidth, wpdData.mHeight); } catch (RemoteException e) { Slog.w(TAG, "Failed to set wallpaper dimensions", e); } @@ -840,7 +889,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } if (mPaddingChanged) { try { - mEngine.setDisplayPadding(mWallpaper.padding); + mEngine.setDisplayPadding(wpdData.mPadding); } catch (RemoteException e) { Slog.w(TAG, "Failed to set wallpaper padding", e); } @@ -857,16 +906,16 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return; } + final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, + mDisplayId); try { - // TODO(b/115486823) Consider the size of non-default display connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false, - wallpaper.width, wallpaper.height, - wallpaper.padding, mDisplayId); + wpdData.mWidth, wpdData.mHeight, + wpdData.mPadding, mDisplayId); } catch (RemoteException e) { Slog.w(TAG, "Failed attaching wallpaper on display", e); - // TODO(b/115486823) Failed when attaching a new engine, however, other engines - // may still working. Should we abandon them all or just ignore this one. - if (mLastWallpaper != null && !mLastWallpaper.wallpaperUpdating) { + if (mLastWallpaper != null && !mLastWallpaper.wallpaperUpdating + && connection.getConnectedEngineSize() == 0) { bindWallpaperComponentLocked(null /* componentName */, false /* force */, false /* fromUser */, wallpaper, null /* reply */); } @@ -952,11 +1001,20 @@ public class WallpaperManagerService extends IWallpaperManager.Stub void forEachDisplayConnector(Consumer<DisplayConnector> action) { for (int i = mDisplayConnector.size() - 1; i >= 0; i--) { - final DisplayConnector connector = mDisplayConnector.get(i); + final DisplayConnector connector = mDisplayConnector.valueAt(i); action.accept(connector); } } + int getConnectedEngineSize() { + int engineSize = 0; + for (int i = mDisplayConnector.size() - 1; i >= 0; i--) { + final DisplayConnector connector = mDisplayConnector.valueAt(i); + if (connector.mEngine != null) engineSize++; + } + return engineSize; + } + DisplayConnector getDisplayConnectorOrCreate(int displayId) { DisplayConnector connector = mDisplayConnector.get(displayId); if (connector == null) { @@ -1128,7 +1186,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub Slog.w(TAG, "Failed to set ambient mode state", e); } } - // TODO(b/115486823) Extends for secondary display. + // TODO(multi-display) So far, we have shared the same wallpaper on each display. + // Once we have multiple wallpapers on multiple displays, please complete here. if (displayId == DEFAULT_DISPLAY) { try { // This will trigger onComputeColors in the wallpaper engine. @@ -1560,7 +1619,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent; final WallpaperData fallback = new WallpaperData(wallpaper.userId, WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP); - ensureSaneWallpaperData(fallback); + ensureSaneWallpaperData(fallback, DEFAULT_DISPLAY); bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply); mWaitingForUnlock = true; } @@ -1705,8 +1764,15 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return false; } - // TODO(b/115486823) Extends this method with specific display. - public void setDimensionHints(int width, int height, String callingPackage) + private boolean isValidDisplay(int displayId) { + return mDisplayManager.getDisplay(displayId) != null; + } + + /** + * Sets the dimension hint for the wallpaper. These hints indicate the desired + * minimum width and height for the wallpaper in a particular display. + */ + public void setDimensionHints(int width, int height, String callingPackage, int displayId) throws RemoteException { checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS); if (!isWallpaperSupported(callingPackage)) { @@ -1719,90 +1785,113 @@ public class WallpaperManagerService extends IWallpaperManager.Stub throw new IllegalArgumentException("width and height must be > 0"); } - if (width != wallpaper.width || height != wallpaper.height) { - wallpaper.width = width; - wallpaper.height = height; - saveSettingsLocked(userId); + if (!isValidDisplay(displayId)) { + throw new IllegalArgumentException("Cannot find display with id=" + displayId); + } + + final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId); + if (width != wpdData.mWidth || height != wpdData.mHeight) { + wpdData.mWidth = width; + wpdData.mHeight = height; + if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId); if (mCurrentUserId != userId) return; // Don't change the properties now if (wallpaper.connection != null) { - // TODO(b/115486823) Extends this method with specific display. - final IWallpaperEngine engine = wallpaper.connection - .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine; + final WallpaperConnection.DisplayConnector connector = wallpaper.connection + .getDisplayConnectorOrCreate(displayId); + final IWallpaperEngine engine = connector != null ? connector.mEngine : null; if (engine != null) { try { engine.setDesiredSize(width, height); } catch (RemoteException e) { } notifyCallbacksLocked(wallpaper); - } else if (wallpaper.connection.mService != null) { + } else if (wallpaper.connection.mService != null && connector != null) { // We've attached to the service but the engine hasn't attached back to us // yet. This means it will be created with the previous dimensions, so we // need to update it to the new dimensions once it attaches. - // TODO(b/115486823) Extends this method with specific display. - wallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY) - .mDimensionsChanged = true; + connector.mDimensionsChanged = true; } } } } } - public int getWidthHint() throws RemoteException { + /** + * Returns the desired minimum width for the wallpaper in a particular display. + */ + public int getWidthHint(int displayId) throws RemoteException { synchronized (mLock) { + if (!isValidDisplay(displayId)) { + throw new IllegalArgumentException("Cannot find display with id=" + displayId); + } WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId()); if (wallpaper != null) { - return wallpaper.width; + final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, + displayId); + return wpdData.mWidth; } else { return 0; } } } - public int getHeightHint() throws RemoteException { + /** + * Returns the desired minimum height for the wallpaper in a particular display. + */ + public int getHeightHint(int displayId) throws RemoteException { synchronized (mLock) { + if (!isValidDisplay(displayId)) { + throw new IllegalArgumentException("Cannot find display with id=" + displayId); + } WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId()); if (wallpaper != null) { - return wallpaper.height; + final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, + displayId); + return wpdData.mHeight; } else { return 0; } } } - // TODO(b/115486823) Extends this method with specific display. - public void setDisplayPadding(Rect padding, String callingPackage) { + /** + * Sets extra padding that we would like the wallpaper to have outside of the display. + */ + public void setDisplayPadding(Rect padding, String callingPackage, int displayId) { checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS); if (!isWallpaperSupported(callingPackage)) { return; } synchronized (mLock) { + if (!isValidDisplay(displayId)) { + throw new IllegalArgumentException("Cannot find display with id=" + displayId); + } int userId = UserHandle.getCallingUserId(); WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM); if (padding.left < 0 || padding.top < 0 || padding.right < 0 || padding.bottom < 0) { throw new IllegalArgumentException("padding must be positive: " + padding); } - if (!padding.equals(wallpaper.padding)) { - wallpaper.padding.set(padding); - saveSettingsLocked(userId); + final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId); + if (!padding.equals(wpdData.mPadding)) { + wpdData.mPadding.set(padding); + if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId); if (mCurrentUserId != userId) return; // Don't change the properties now if (wallpaper.connection != null) { - // TODO(b/115486823) Extends this method with specific display. - final IWallpaperEngine engine = wallpaper.connection - .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine; + final WallpaperConnection.DisplayConnector connector = wallpaper.connection + .getDisplayConnectorOrCreate(displayId); + final IWallpaperEngine engine = connector != null ? connector.mEngine : null; if (engine != null) { try { engine.setDisplayPadding(padding); } catch (RemoteException e) { } notifyCallbacksLocked(wallpaper); - } else if (wallpaper.connection.mService != null) { + } else if (wallpaper.connection.mService != null && connector != null) { // We've attached to the service but the engine hasn't attached back to us // yet. This means it will be created with the previous dimensions, so we // need to update it to the new dimensions once it attaches. - // TODO(b/115486823) Extends this method with specific display. - wallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY) - .mPaddingChanged = true; + connector.mPaddingChanged = true; } } } @@ -1850,10 +1939,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // user switch) return null; } + // Only for default display. + final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, + DEFAULT_DISPLAY); try { if (outParams != null) { - outParams.putInt("width", wallpaper.width); - outParams.putInt("height", wallpaper.height); + outParams.putInt("width", wpdData.mWidth); + outParams.putInt("height", wpdData.mHeight); } if (cb != null) { wallpaper.callbacks.register(cb); @@ -2075,10 +2167,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // We know a-priori that there is no lock-only wallpaper currently WallpaperData lockWP = new WallpaperData(userId, WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP); + final WallpaperData.DisplayData lockWPDData = getDisplayDataOrCreate(lockWP, + DEFAULT_DISPLAY); + final WallpaperData.DisplayData sysWPDData = getDisplayDataOrCreate(sysWP, + DEFAULT_DISPLAY); lockWP.wallpaperId = sysWP.wallpaperId; lockWP.cropHint.set(sysWP.cropHint); - lockWP.width = sysWP.width; - lockWP.height = sysWP.height; + lockWPDData.mWidth = sysWPDData.mWidth; + lockWPDData.mHeight = sysWPDData.mHeight; lockWP.allowBackup = sysWP.allowBackup; lockWP.primaryColors = sysWP.primaryColors; @@ -2478,27 +2574,29 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (DEBUG) { Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId); } + final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, + DEFAULT_DISPLAY); out.startTag(null, tag); out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId)); - out.attribute(null, "width", Integer.toString(wallpaper.width)); - out.attribute(null, "height", Integer.toString(wallpaper.height)); + out.attribute(null, "width", Integer.toString(wpdData.mWidth)); + out.attribute(null, "height", Integer.toString(wpdData.mHeight)); out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left)); out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top)); out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right)); out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom)); - if (wallpaper.padding.left != 0) { - out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left)); + if (wpdData.mPadding.left != 0) { + out.attribute(null, "paddingLeft", Integer.toString(wpdData.mPadding.left)); } - if (wallpaper.padding.top != 0) { - out.attribute(null, "paddingTop", Integer.toString(wallpaper.padding.top)); + if (wpdData.mPadding.top != 0) { + out.attribute(null, "paddingTop", Integer.toString(wpdData.mPadding.top)); } - if (wallpaper.padding.right != 0) { - out.attribute(null, "paddingRight", Integer.toString(wallpaper.padding.right)); + if (wpdData.mPadding.right != 0) { + out.attribute(null, "paddingRight", Integer.toString(wpdData.mPadding.right)); } - if (wallpaper.padding.bottom != 0) { - out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom)); + if (wpdData.mPadding.bottom != 0) { + out.attribute(null, "paddingBottom", Integer.toString(wpdData.mPadding.bottom)); } if (wallpaper.primaryColors != null) { @@ -2601,14 +2699,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub wallpaper = new WallpaperData(userId, WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP); mLockWallpaperMap.put(userId, wallpaper); - ensureSaneWallpaperData(wallpaper); + ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY); } else { // sanity fallback: we're in bad shape, but establishing a known // valid system+lock WallpaperData will keep us from dying. Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!"); wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP); mWallpaperMap.put(userId, wallpaper); - ensureSaneWallpaperData(wallpaper); + ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY); } } } @@ -2637,6 +2735,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } boolean success = false; + final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, + DEFAULT_DISPLAY); try { stream = new FileInputStream(file); XmlPullParser parser = Xml.newPullParser(); @@ -2663,8 +2763,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } if (DEBUG) { - Slog.v(TAG, "mWidth:" + wallpaper.width); - Slog.v(TAG, "mHeight:" + wallpaper.height); + Slog.v(TAG, "mWidth:" + wpdData.mWidth); + Slog.v(TAG, "mHeight:" + wpdData.mHeight); Slog.v(TAG, "cropRect:" + wallpaper.cropHint); Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors); Slog.v(TAG, "mName:" + wallpaper.name); @@ -2700,10 +2800,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub IoUtils.closeQuietly(stream); if (!success) { - wallpaper.width = -1; - wallpaper.height = -1; + wpdData.mWidth = -1; + wpdData.mHeight = -1; wallpaper.cropHint.set(0, 0, 0, 0); - wallpaper.padding.set(0, 0, 0, 0); + wpdData.mPadding.set(0, 0, 0, 0); wallpaper.name = ""; mLockWallpaperMap.remove(userId); @@ -2717,26 +2817,22 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - ensureSaneWallpaperData(wallpaper); + ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY); WallpaperData lockWallpaper = mLockWallpaperMap.get(userId); if (lockWallpaper != null) { - ensureSaneWallpaperData(lockWallpaper); + ensureSaneWallpaperData(lockWallpaper, DEFAULT_DISPLAY); } } - private void ensureSaneWallpaperData(WallpaperData wallpaper) { - // We always want to have some reasonable width hint. - int baseSize = getMaximumSizeDimension(); - if (wallpaper.width < baseSize) { - wallpaper.width = baseSize; - } - if (wallpaper.height < baseSize) { - wallpaper.height = baseSize; - } - // and crop, if not previously specified - if (wallpaper.cropHint.width() <= 0 - || wallpaper.cropHint.height() <= 0) { - wallpaper.cropHint.set(0, 0, wallpaper.width, wallpaper.height); + private void ensureSaneWallpaperData(WallpaperData wallpaper, int displayId) { + final WallpaperData.DisplayData size = getDisplayDataOrCreate(wallpaper, displayId); + + if (displayId == DEFAULT_DISPLAY) { + // crop, if not previously specified + if (wallpaper.cropHint.width() <= 0 + || wallpaper.cropHint.height() <= 0) { + wallpaper.cropHint.set(0, 0, size.mWidth, size.mHeight); + } } } @@ -2752,19 +2848,20 @@ public class WallpaperManagerService extends IWallpaperManager.Stub wallpaper.wallpaperId = makeWallpaperIdLocked(); } + final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY); + if (!keepDimensionHints) { - wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width")); - wallpaper.height = Integer.parseInt(parser - .getAttributeValue(null, "height")); + wpData.mWidth = Integer.parseInt(parser.getAttributeValue(null, "width")); + wpData.mHeight = Integer.parseInt(parser.getAttributeValue(null, "height")); } wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0); wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0); wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0); wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0); - wallpaper.padding.left = getAttributeInt(parser, "paddingLeft", 0); - wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0); - wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0); - wallpaper.padding.bottom = getAttributeInt(parser, "paddingBottom", 0); + wpData.mPadding.left = getAttributeInt(parser, "paddingLeft", 0); + wpData.mPadding.top = getAttributeInt(parser, "paddingTop", 0); + wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0); + wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0); int colorsCount = getAttributeInt(parser, "colorsCount", 0); if (colorsCount > 0) { Color primary = null, secondary = null, tertiary = null; @@ -2787,12 +2884,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub wallpaper.allowBackup = "true".equals(parser.getAttributeValue(null, "backup")); } - private int getMaximumSizeDimension() { - WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); - Display d = wm.getDefaultDisplay(); - return d.getMaximumSizeDimension(); - } - // Called by SystemBackupAgent after files are restored to disk. public void settingsRestored() { // Verify caller is the system @@ -2832,7 +2923,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success + " id=" + wallpaper.wallpaperId); if (success) { - generateCrop(wallpaper); // based on the new image + metadata + generateCrop(wallpaper); // based on the new image + metadata bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false, wallpaper, null); } @@ -2937,12 +3028,16 @@ public class WallpaperManagerService extends IWallpaperManager.Stub WallpaperData wallpaper = mWallpaperMap.valueAt(i); pw.print(" User "); pw.print(wallpaper.userId); pw.print(": id="); pw.println(wallpaper.wallpaperId); - pw.print(" mWidth="); - pw.print(wallpaper.width); - pw.print(" mHeight="); - pw.println(wallpaper.height); + forEachDisplayData(wallpaper, wpSize -> { + pw.print(" displayId="); + pw.println(wpSize.mDisplayId); + pw.print(" mWidth="); + pw.print(wpSize.mWidth); + pw.print(" mHeight="); + pw.println(wpSize.mHeight); + pw.print(" mPadding="); pw.println(wpSize.mPadding); + }); pw.print(" mCropHint="); pw.println(wallpaper.cropHint); - pw.print(" mPadding="); pw.println(wallpaper.padding); pw.print(" mName="); pw.println(wallpaper.name); pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup); pw.print(" mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent); @@ -2973,11 +3068,15 @@ public class WallpaperManagerService extends IWallpaperManager.Stub for (int i = 0; i < mLockWallpaperMap.size(); i++) { WallpaperData wallpaper = mLockWallpaperMap.valueAt(i); pw.print(" User "); pw.print(wallpaper.userId); - pw.print(": id="); pw.println(wallpaper.wallpaperId); - pw.print(" mWidth="); pw.print(wallpaper.width); - pw.print(" mHeight="); pw.println(wallpaper.height); + pw.print(": id="); pw.println(wallpaper.wallpaperId); + forEachDisplayData(wallpaper, wpSize -> { + pw.print(" displayId="); + pw.println(wpSize.mDisplayId); + pw.print(" mWidth="); pw.print(wpSize.mWidth); + pw.print(" mHeight="); pw.println(wpSize.mHeight); + pw.print(" mPadding="); pw.println(wpSize.mPadding); + }); pw.print(" mCropHint="); pw.println(wallpaper.cropHint); - pw.print(" mPadding="); pw.println(wallpaper.padding); pw.print(" mName="); pw.println(wallpaper.name); pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup); } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index c43e64ec5b98..5e92b9e4d46a 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2354,7 +2354,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // bounds would end up too small. outBounds.set(0, 0, maxActivityWidth + appBounds.left, maxActivityHeight + appBounds.top); - if (service.mWindowManager.getNavBarPosition() == NAV_BAR_LEFT) { + if (service.mWindowManager.getNavBarPosition(getDisplayId()) == NAV_BAR_LEFT) { // Position the activity frame on the opposite side of the nav bar. outBounds.left = appBounds.right - maxActivityWidth; outBounds.right = appBounds.right; diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 694e9d1d6917..14b2f01c4c35 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -1039,11 +1039,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D boolean didSomething = false; for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - if (!isTopDisplayFocusedStack(stack)) { - continue; - } + final ActivityStack stack = display.getFocusedStack(); + if (stack != null) { stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList); final ActivityRecord top = stack.topRunningActivityLocked(); final int size = mTmpActivityList.size(); @@ -2429,6 +2426,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // We give preference to the launch preference in activity options. if (options != null) { taskId = options.getLaunchTaskId(); + displayId = options.getLaunchDisplayId(); } // First preference for stack goes to the task Id set in the activity options. Use the stack @@ -2448,7 +2446,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D T stack; // Next preference for stack goes to the display Id set the candidate display. - if (launchParams != null) { + if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) { displayId = launchParams.mPreferredDisplayId; } if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) { @@ -2566,10 +2564,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D windowingMode = options != null ? options.getLaunchWindowingMode() : r.getWindowingMode(); } - return activityDisplay.createStack( - windowingMode, - options != null ? options.getLaunchActivityType() : r.getActivityType(), - true /*onTop*/); + final int activityType = + options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED + ? options.getLaunchActivityType() : r.getActivityType(); + return activityDisplay.createStack(windowingMode, activityType, true /*onTop*/); } Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index d4c1bcad57b3..90f3ff84a027 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1859,7 +1859,8 @@ class ActivityStarter { } } - if (mStartActivity.isActivityTypeHome() && intentActivity != null + if (intentActivity != null + && (mStartActivity.isActivityTypeHome() || intentActivity.isActivityTypeHome()) && intentActivity.getDisplayId() != mPreferredDisplayId) { // Do not reuse home activity on other displays. intentActivity = null; diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index e65f3b417429..32a6f74b5a11 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -96,14 +96,15 @@ public class AppTransitionController { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady"); if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO"); - int transit = mDisplayContent.mAppTransition.getAppTransition(); + final AppTransition appTransition = mDisplayContent.mAppTransition; + int transit = appTransition.getAppTransition(); if (mDisplayContent.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) { transit = WindowManager.TRANSIT_UNSET; } mDisplayContent.mSkipAppTransitionAnimation = false; mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear(); - mDisplayContent.mAppTransition.removeAppTransitionTimeoutCallbacks(); + appTransition.removeAppTransitionTimeoutCallbacks(); mDisplayContent.mWallpaperMayChange = false; @@ -141,7 +142,7 @@ public class AppTransitionController { // done behind a dream window. final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps); - final boolean allowAnimations = mService.mPolicy.allowAppAnimationsLw(); + final boolean allowAnimations = mDisplayContent.getDisplayPolicy().allowAppAnimationsLw(); final AppWindowToken animLpToken = allowAnimations ? findAnimLayoutParamsToken(transit, activityTypes) : null; @@ -165,15 +166,15 @@ public class AppTransitionController { handleClosingApps(transit, animLp, voiceInteraction); handleOpeningApps(transit, animLp, voiceInteraction); - mDisplayContent.mAppTransition.setLastAppTransition(transit, topOpeningApp, + appTransition.setLastAppTransition(transit, topOpeningApp, topClosingApp); - final int flags = mDisplayContent.mAppTransition.getTransitFlags(); - layoutRedo = mDisplayContent.mAppTransition.goodToGo(transit, topOpeningApp, + final int flags = appTransition.getTransitFlags(); + layoutRedo = appTransition.goodToGo(transit, topOpeningApp, topClosingApp, mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps); handleNonAppWindowsInTransition(transit, flags); - mDisplayContent.mAppTransition.postAnimationCallback(); - mDisplayContent.mAppTransition.clear(); + appTransition.postAnimationCallback(); + appTransition.clear(); } finally { mService.mSurfaceAnimationRunner.continueStartingAnimations(); } @@ -254,8 +255,8 @@ public class AppTransitionController { } /** - * @return The set of {@link WindowConfiguration.ActivityType}s contained in the set of apps in - * {@code array1} and {@code array2}. + * @return The set of {@link android.app.WindowConfiguration.ActivityType}s contained in the set + * of apps in {@code array1} and {@code array2}. */ private static ArraySet<Integer> collectActivityTypes(ArraySet<AppWindowToken> array1, ArraySet<AppWindowToken> array2) { diff --git a/services/core/java/com/android/server/policy/BarController.java b/services/core/java/com/android/server/wm/BarController.java index 14c985c090a3..a335fa26dfcf 100644 --- a/services/core/java/com/android/server/policy/BarController.java +++ b/services/core/java/com/android/server/wm/BarController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.policy; +package com.android.server.wm; import static com.android.server.wm.BarControllerProto.STATE; import static com.android.server.wm.BarControllerProto.TRANSIENT_STATE; @@ -30,7 +30,7 @@ import android.view.View; import android.view.WindowManager; import com.android.server.LocalServices; -import com.android.server.policy.WindowManagerPolicy.WindowState; +import com.android.server.UiThread; import com.android.server.statusbar.StatusBarManagerInternal; import java.io.PrintWriter; @@ -59,7 +59,7 @@ public class BarController { private final int mTranslucentWmFlag; protected final Handler mHandler; private final Object mServiceAquireLock = new Object(); - protected StatusBarManagerInternal mStatusBarInternal; + private StatusBarManagerInternal mStatusBarInternal; protected WindowState mWin; private int mState = StatusBarManager.WINDOW_STATE_SHOWING; @@ -73,7 +73,7 @@ public class BarController { private OnBarVisibilityChangedListener mVisibilityChangeListener; - public BarController(String tag, int transientFlag, int unhideFlag, int translucentFlag, + BarController(String tag, int transientFlag, int unhideFlag, int translucentFlag, int statusBarManagerId, int translucentWmFlag, int transparentFlag) { mTag = "BarController." + tag; mTransientFlag = transientFlag; @@ -85,7 +85,7 @@ public class BarController { mHandler = new BarHandler(); } - public void setWindow(WindowState win) { + void setWindow(WindowState win) { mWin = win; } @@ -94,11 +94,11 @@ public class BarController { * * This is used to determine if letterboxes interfere with the display of such content. */ - public void setContentFrame(Rect frame) { + void setContentFrame(Rect frame) { mContentFrame.set(frame); } - public void setShowTransparent(boolean transparent) { + void setShowTransparent(boolean transparent) { if (transparent != mShowTransparent) { mShowTransparent = transparent; mSetUnHideFlagWhenNextTransparent = transparent; @@ -106,27 +106,27 @@ public class BarController { } } - public void showTransient() { + void showTransient() { if (mWin != null) { setTransientBarState(TRANSIENT_BAR_SHOW_REQUESTED); } } - public boolean isTransientShowing() { + boolean isTransientShowing() { return mTransientBarState == TRANSIENT_BAR_SHOWING; } - public boolean isTransientShowRequested() { + boolean isTransientShowRequested() { return mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED; } - public boolean wasRecentlyTranslucent() { + boolean wasRecentlyTranslucent() { return (SystemClock.uptimeMillis() - mLastTranslucent) < TRANSLUCENT_ANIMATION_DELAY_MS; } - public void adjustSystemUiVisibilityLw(int oldVis, int vis) { - if (mWin != null && mTransientBarState == TRANSIENT_BAR_SHOWING && - (vis & mTransientFlag) == 0) { + void adjustSystemUiVisibilityLw(int oldVis, int vis) { + if (mWin != null && mTransientBarState == TRANSIENT_BAR_SHOWING + && (vis & mTransientFlag) == 0) { // sysui requests hide setTransientBarState(TRANSIENT_BAR_HIDING); setBarShowingLw(false); @@ -136,7 +136,7 @@ public class BarController { } } - public int applyTranslucentFlagLw(WindowState win, int vis, int oldVis) { + int applyTranslucentFlagLw(WindowState win, int vis, int oldVis) { if (mWin != null) { if (win != null && (win.getAttrs().privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) == 0) { @@ -164,7 +164,7 @@ public class BarController { return win == null || !win.isLetterboxedOverlappingWith(mContentFrame); } - public boolean setBarShowingLw(final boolean show) { + boolean setBarShowingLw(final boolean show) { if (mWin == null) return false; if (show && mTransientBarState == TRANSIENT_BAR_HIDING) { mPendingShow = true; @@ -227,7 +227,7 @@ public class BarController { public void run() { StatusBarManagerInternal statusbar = getStatusBarInternal(); if (statusbar != null) { - statusbar.setWindowState(mStatusBarManagerId, state); + statusbar.setWindowState(mWin.getDisplayId(), mStatusBarManagerId, state); } } }); @@ -236,7 +236,7 @@ public class BarController { return false; } - public boolean checkHiddenLw() { + boolean checkHiddenLw() { if (mWin != null && mWin.isDrawnLw()) { if (!mWin.isVisibleLw() && !mWin.isAnimatingLw()) { updateStateLw(StatusBarManager.WINDOW_STATE_HIDDEN); @@ -254,7 +254,7 @@ public class BarController { return false; } - public boolean checkShowTransientBarLw() { + boolean checkShowTransientBarLw() { if (mTransientBarState == TRANSIENT_BAR_SHOWING) { if (DEBUG) Slog.d(mTag, "Not showing transient bar, already shown"); return false; @@ -272,7 +272,7 @@ public class BarController { } } - public int updateVisibilityLw(boolean transientAllowed, int oldVis, int vis) { + int updateVisibilityLw(boolean transientAllowed, int oldVis, int vis) { if (mWin == null) return vis; if (isTransientShowing() || isTransientShowRequested()) { // transient bar requested if (transientAllowed) { @@ -296,8 +296,8 @@ public class BarController { vis |= mTransientFlag; // ignore clear requests until transition completes vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE; // never show transient bars in low profile } - if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0 || - ((vis | oldVis) & mTransparentFlag) != 0) { + if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0 + || ((vis | oldVis) & mTransparentFlag) != 0) { mLastTranslucent = SystemClock.uptimeMillis(); } return vis; @@ -330,14 +330,14 @@ public class BarController { throw new IllegalArgumentException("Unknown state " + state); } - public void writeToProto(ProtoOutputStream proto, long fieldId) { + void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(STATE, mState); proto.write(TRANSIENT_STATE, mTransientBarState); proto.end(token); } - public void dump(PrintWriter pw, String prefix) { + void dump(PrintWriter pw, String prefix) { if (mWin != null) { pw.print(prefix); pw.println(mTag); pw.print(prefix); pw.print(" "); pw.print("mState"); pw.print('='); @@ -349,6 +349,10 @@ public class BarController { } private class BarHandler extends Handler { + BarHandler() { + super(UiThread.getHandler().getLooper()); + } + @Override public void handleMessage(Message msg) { switch (msg.what) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 2f7956e86531..886b2ff1ce54 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -156,6 +156,7 @@ import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.SurfaceSession; +import android.view.View; import android.view.WindowManager; import android.view.WindowManagerPolicyConstants.PointerEventListener; @@ -182,9 +183,6 @@ import java.util.function.Predicate; /** * Utility class for keeping track of the WindowStates and other pertinent contents of a * particular Display. - * - * IMPORTANT: No method from this class should ever be used without holding - * WindowManagerService.mWindowMap. */ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer> implements WindowManagerPolicy.DisplayContentInfo { @@ -289,7 +287,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * @see WindowManagerService#createWatermarkInTransaction() */ final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics(); - /** @see #computeCompatSmallestWidth(boolean, int, int, int, int) */ + + /** @see #computeCompatSmallestWidth(boolean, int, int, int, DisplayCutout) */ private final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics(); /** @@ -508,6 +507,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final PointerEventDispatcher mPointerEventDispatcher; + // Last systemUiVisibility we received from status bar. + private int mLastStatusBarVisibility = 0; + // Last systemUiVisibility we dispatched to windows. + private int mLastDispatchedSystemUiVisibility = 0; + private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> { WindowStateAnimator winAnimator = w.mWinAnimator; final AppWindowToken atoken = w.mAppToken; @@ -649,7 +653,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo w.mLayoutNeeded = false; w.prelayout(); final boolean firstLayout = !w.isLaidOut(); - mService.mPolicy.layoutWindowLw(w, null, mDisplayFrames); + getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames); w.mLayoutSeq = mLayoutSeq; // If this is the first layout, we need to initialize the last inset values as @@ -688,7 +692,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } w.mLayoutNeeded = false; w.prelayout(); - mService.mPolicy.layoutWindowLw(w, w.getParentWindow(), mDisplayFrames); + getDisplayPolicy().layoutWindowLw(w, w.getParentWindow(), mDisplayFrames); w.mLayoutSeq = mLayoutSeq; if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrameLw() + " mContainingFrame=" + w.getContainingFrame() @@ -708,7 +712,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo }; private final Consumer<WindowState> mApplyPostLayoutPolicy = - w -> mService.mPolicy.applyPostLayoutPolicyLw(w, w.mAttrs, w.getParentWindow(), + w -> getDisplayPolicy().applyPostLayoutPolicyLw(w, w.mAttrs, w.getParentWindow(), mInputMethodTarget); private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> { @@ -838,15 +842,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mDisplayFrames = new DisplayFrames(mDisplayId, mDisplayInfo, calculateDisplayCutoutForRotation(mDisplayInfo.rotation)); initializeDisplayBaseInfo(); - mDisplayPolicy = new DisplayPolicy(service); - mDisplayRotation = new DisplayRotation(service, this); - if (isDefaultDisplay) { - // The policy may be invoked right after here, so it requires the necessary default - // fields of this display content. - mService.mPolicy.setDefaultDisplay(this); - } - mDividerControllerLocked = new DockedStackDividerController(service, this); - mPinnedStackControllerLocked = new PinnedStackController(service, this); mAppTransition = new AppTransition(service.mContext, service, this); mAppTransition.registerListenerLocked(service.mActivityManagerAppTransitionNotifier); @@ -857,6 +852,30 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mBoundsAnimationController = new BoundsAnimationController(service.mContext, mAppTransition, SurfaceAnimationThread.getHandler(), animationHandler); + if (mService.mInputManager != null) { + final InputChannel inputChannel = mService.mInputManager.monitorInput("Display " + + mDisplayId, mDisplayId); + mPointerEventDispatcher = inputChannel != null + ? new PointerEventDispatcher(inputChannel) : null; + } else { + mPointerEventDispatcher = null; + } + mDisplayPolicy = new DisplayPolicy(service, this); + mDisplayRotation = new DisplayRotation(service, this); + if (isDefaultDisplay) { + // The policy may be invoked right after here, so it requires the necessary default + // fields of this display content. + mService.mPolicy.setDefaultDisplay(this); + } + if (mService.mDisplayReady) { + mDisplayPolicy.onConfigurationChanged(); + } + if (mService.mSystemReady) { + mDisplayPolicy.systemReady(); + } + mDividerControllerLocked = new DockedStackDividerController(service, this); + mPinnedStackControllerLocked = new PinnedStackController(service, this); + // We use this as our arbitrary surface size for buffer-less parents // that don't impose cropping on their children. It may need to be larger // than the display size because fullscreen windows can be shifted offscreen @@ -895,15 +914,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mService.mAnimator.addDisplayLocked(mDisplayId); mInputMonitor = new InputMonitor(service, mDisplayId); - - if (mService.mInputManager != null) { - final InputChannel inputChannel = mService.mInputManager.monitorInput("Display " - + mDisplayId, mDisplayId); - mPointerEventDispatcher = inputChannel != null - ? new PointerEventDispatcher(inputChannel) : null; - } else { - mPointerEventDispatcher = null; - } } boolean isReady() { @@ -1223,7 +1233,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Computed rotation=" + rotation + " for display id=" + mDisplayId + " based on lastOrientation=" + lastOrientation + " and oldRotation=" + oldRotation); - boolean mayRotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(mDisplayRotation, + boolean mayRotateSeamlessly = mDisplayPolicy.shouldRotateSeamlessly(mDisplayRotation, oldRotation, rotation); if (mayRotateSeamlessly) { @@ -1286,7 +1296,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo setLayoutNeeded(); final int[] anim = new int[2]; - mService.mPolicy.selectRotationAnimationLw(anim); + mDisplayPolicy.selectRotationAnimationLw(anim); if (!rotateSeamlessly) { mService.startFreezingDisplayLocked(anim[0], anim[1], this); @@ -1445,10 +1455,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(mRotation); final DisplayCutout displayCutout = wmDisplayCutout.getDisplayCutout(); - final int appWidth = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation, uiMode, - mDisplayId, displayCutout); - final int appHeight = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation, uiMode, - mDisplayId, displayCutout); + final int appWidth = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, mRotation, uiMode, + displayCutout); + final int appHeight = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, mRotation, uiMode, + displayCutout); mDisplayInfo.rotation = mRotation; mDisplayInfo.logicalWidth = dw; mDisplayInfo.logicalHeight = dh; @@ -1527,13 +1537,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final float density = mDisplayMetrics.density; config.screenWidthDp = - (int)(mService.mPolicy.getConfigDisplayWidth(dw, dh, displayInfo.rotation, - config.uiMode, mDisplayId, displayInfo.displayCutout) / density); + (int)(mDisplayPolicy.getConfigDisplayWidth(dw, dh, displayInfo.rotation, + config.uiMode, displayInfo.displayCutout) / density); config.screenHeightDp = - (int)(mService.mPolicy.getConfigDisplayHeight(dw, dh, displayInfo.rotation, - config.uiMode, mDisplayId, displayInfo.displayCutout) / density); + (int)(mDisplayPolicy.getConfigDisplayHeight(dw, dh, displayInfo.rotation, + config.uiMode, displayInfo.displayCutout) / density); - mService.mPolicy.getNonDecorInsetsLw(displayInfo.rotation, dw, dh, + mDisplayPolicy.getNonDecorInsetsLw(displayInfo.rotation, dw, dh, displayInfo.displayCutout, mTmpRect); final int leftInset = mTmpRect.left; final int topInset = mTmpRect.top; @@ -1544,8 +1554,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final boolean rotated = (displayInfo.rotation == Surface.ROTATION_90 || displayInfo.rotation == Surface.ROTATION_270); - computeSizeRangesAndScreenLayout(displayInfo, mDisplayId, rotated, config.uiMode, dw, dh, - density, config); + computeSizeRangesAndScreenLayout(displayInfo, rotated, config.uiMode, dw, dh, density, + config); config.screenLayout = (config.screenLayout & ~Configuration.SCREENLAYOUT_ROUND_MASK) | ((displayInfo.flags & Display.FLAG_ROUND) != 0 @@ -1555,7 +1565,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale); config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale); config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, config.uiMode, dw, - dh, displayInfo.displayCutout, mDisplayId); + dh, displayInfo.displayCutout); config.densityDpi = displayInfo.logicalDensityDpi; config.colorMode = @@ -1624,6 +1634,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mService.mH.sendEmptyMessage(WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE); } + mDisplayPolicy.updateConfigurationDependentBehaviors(); + // Let the policy update hidden states. config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO; config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; @@ -1632,7 +1644,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh, - DisplayCutout displayCutout, int displayId) { + DisplayCutout displayCutout) { mTmpDisplayMetrics.setTo(mDisplayMetrics); final DisplayMetrics tmpDm = mTmpDisplayMetrics; final int unrotDw, unrotDh; @@ -1644,22 +1656,22 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo unrotDh = dh; } int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw, unrotDh, - displayCutout, displayId); + displayCutout); sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh, unrotDw, - displayCutout, displayId); + displayCutout); sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw, unrotDh, - displayCutout, displayId); + displayCutout); sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh, unrotDw, - displayCutout, displayId); + displayCutout); return sw; } private int reduceCompatConfigWidthSize(int curSize, int rotation, int uiMode, - DisplayMetrics dm, int dw, int dh, DisplayCutout displayCutout, int displayId) { - dm.noncompatWidthPixels = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, - displayId, displayCutout); - dm.noncompatHeightPixels = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, rotation, - uiMode, displayId, displayCutout); + DisplayMetrics dm, int dw, int dh, DisplayCutout displayCutout) { + dm.noncompatWidthPixels = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, + displayCutout); + dm.noncompatHeightPixels = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, + displayCutout); float scale = CompatibilityInfo.computeCompatibleScaling(dm, null); int size = (int)(((dm.noncompatWidthPixels / scale) / dm.density) + .5f); if (curSize == 0 || size < curSize) { @@ -1668,8 +1680,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return curSize; } - private void computeSizeRangesAndScreenLayout(DisplayInfo displayInfo, int displayId, - boolean rotated, int uiMode, int dw, int dh, float density, Configuration outConfig) { + private void computeSizeRangesAndScreenLayout(DisplayInfo displayInfo, boolean rotated, + int uiMode, int dw, int dh, float density, Configuration outConfig) { // We need to determine the smallest width that will occur under normal // operation. To this, start with the base screen size and compute the @@ -1687,34 +1699,28 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo displayInfo.smallestNominalAppHeight = 1<<30; displayInfo.largestNominalAppWidth = 0; displayInfo.largestNominalAppHeight = 0; - adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_0, uiMode, unrotDw, - unrotDh); - adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_90, uiMode, unrotDh, - unrotDw); - adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_180, uiMode, unrotDw, - unrotDh); - adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_270, uiMode, unrotDh, - unrotDw); + adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_0, uiMode, unrotDw, unrotDh); + adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_90, uiMode, unrotDh, unrotDw); + adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_180, uiMode, unrotDw, unrotDh); + adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_270, uiMode, unrotDh, unrotDw); int sl = Configuration.resetScreenLayout(outConfig.screenLayout); sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode, - displayInfo.displayCutout, displayId); + displayInfo.displayCutout); sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode, - displayInfo.displayCutout, displayId); + displayInfo.displayCutout); sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode, - displayInfo.displayCutout, displayId); + displayInfo.displayCutout); sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode, - displayInfo.displayCutout, displayId); + displayInfo.displayCutout); outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density); outConfig.screenLayout = sl; } private int reduceConfigLayout(int curLayout, int rotation, float density, int dw, int dh, - int uiMode, DisplayCutout displayCutout, int displayId) { + int uiMode, DisplayCutout displayCutout) { // Get the app screen size at this rotation. - int w = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayId, - displayCutout); - int h = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, displayId, - displayCutout); + int w = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayCutout); + int h = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, displayCutout); // Compute the screen layout size class for this rotation. int longSize = w; @@ -1729,20 +1735,20 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return Configuration.reduceScreenLayout(curLayout, longSize, shortSize); } - private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int displayId, int rotation, + private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int rotation, int uiMode, int dw, int dh) { final DisplayCutout displayCutout = calculateDisplayCutoutForRotation( rotation).getDisplayCutout(); - final int width = mService.mPolicy.getConfigDisplayWidth(dw, dh, rotation, uiMode, - displayId, displayCutout); + final int width = mDisplayPolicy.getConfigDisplayWidth(dw, dh, rotation, uiMode, + displayCutout); if (width < displayInfo.smallestNominalAppWidth) { displayInfo.smallestNominalAppWidth = width; } if (width > displayInfo.largestNominalAppWidth) { displayInfo.largestNominalAppWidth = width; } - final int height = mService.mPolicy.getConfigDisplayHeight(dw, dh, rotation, uiMode, - displayId, displayCutout); + final int height = mDisplayPolicy.getConfigDisplayHeight(dw, dh, rotation, uiMode, + displayCutout); if (height < displayInfo.smallestNominalAppHeight) { displayInfo.smallestNominalAppHeight = height; } @@ -1887,6 +1893,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override public void onConfigurationChanged(Configuration newParentConfig) { super.onConfigurationChanged(newParentConfig); + if (mDisplayPolicy != null) { + mDisplayPolicy.onConfigurationChanged(); + } // If there was no pinned stack, we still need to notify the controller of the display info // update as a result of the config change. @@ -2383,7 +2392,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mDisplayReady = false; mRemovingDisplay = false; } - + mDisplayPolicy.onDisplayRemoved(); mInputMonitor.onRemoved(); mService.mWindowPlacerLocked.requestTraversal(); } @@ -2659,6 +2668,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } pw.print(" mFocusedApp="); pw.println(mFocusedApp); + if (mLastStatusBarVisibility != 0) { + pw.print(" mLastStatusBarVisibility=0x"); + pw.println(Integer.toHexString(mLastStatusBarVisibility)); + } pw.println(); mWallpaperController.dump(pw, " "); @@ -2852,9 +2865,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } - // System UI is only shown on the default display. - int focusChanged = isDefaultDisplay - ? mService.mPolicy.focusChangedLw(oldFocus, newFocus) : 0; + int focusChanged = getDisplayPolicy().focusChangedLw(oldFocus, newFocus); if (imWindowChanged && oldFocus != mInputMethodWindow) { // Focus of the input method window changed. Perform layout if needed. @@ -3321,6 +3332,31 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return win != null; } + void statusBarVisibilityChanged(int visibility) { + mLastStatusBarVisibility = visibility; + visibility = getDisplayPolicy().adjustSystemUiVisibilityLw(visibility); + updateStatusBarVisibilityLocked(visibility); + } + + private boolean updateStatusBarVisibilityLocked(int visibility) { + if (mLastDispatchedSystemUiVisibility == visibility) { + return false; + } + final int globalDiff = (visibility ^ mLastDispatchedSystemUiVisibility) + // We are only interested in differences of one of the + // clearable flags... + & View.SYSTEM_UI_CLEARABLE_FLAGS + // ...if it has actually been cleared. + & ~visibility; + + mLastDispatchedSystemUiVisibility = visibility; + if (isDefaultDisplay) { + mService.mInputManager.setSystemUiVisibility(visibility); + } + updateSystemUiVisibility(visibility, globalDiff); + return true; + } + void updateSystemUiVisibility(int visibility, int globalDiff) { forAllWindows(w -> { try { @@ -3341,6 +3377,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo }, true /* traverseTopToBottom */); } + void reevaluateStatusBarVisibility() { + int visibility = getDisplayPolicy().adjustSystemUiVisibilityLw(mLastStatusBarVisibility); + if (updateStatusBarVisibilityLocked(visibility)) { + mService.mWindowPlacerLocked.requestTraversal(); + } + } + void onWindowFreezeTimeout() { Slog.w(TAG_WM, "Window freeze timeout expired."); mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT; @@ -3372,9 +3415,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // TODO: Super crazy long method that should be broken down... void applySurfaceChangesTransaction(boolean recoveringMemory) { - - final int dw = mDisplayInfo.logicalWidth; - final int dh = mDisplayInfo.logicalHeight; final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked; mTmpUpdateAllDrawn.clear(); @@ -3417,13 +3457,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think it is animating. pendingLayoutChanges = 0; - if (isDefaultDisplay) { - mService.mPolicy.beginPostLayoutPolicyLw(dw, dh); - forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */); - pendingLayoutChanges |= mService.mPolicy.finishPostLayoutPolicyLw(); - if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats( - "after finishPostLayoutPolicyLw", pendingLayoutChanges); - } + mDisplayPolicy.beginPostLayoutPolicyLw(); + forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */); + pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw(); + if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats( + "after finishPostLayoutPolicyLw", pendingLayoutChanges); } while (pendingLayoutChanges != 0); mTmpApplySurfaceChangesTransactionState.reset(); @@ -3513,12 +3551,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // TODO: Not sure if we really need to set the rotation here since we are updating from the // display info above... mDisplayFrames.mRotation = mRotation; - mService.mPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode); - if (isDefaultDisplay) { - // Not needed on non-default displays. - mService.mSystemDecorLayer = mService.mPolicy.getSystemDecorLayerLw(); - mService.mScreenRect.set(0, 0, dw, dh); - } + mDisplayPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode); int seq = mLayoutSeq + 1; if (seq < 0) seq = 0; @@ -4548,7 +4581,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * However we need child windows of the applications to be above the IME (Text drag handles). * This is a non-strictly hierarcical layering and we need to break out of the Z ordering * somehow. We do this by relatively ordering children of the target to the IME in cooperation - * with {@link #WindowState#assignLayer} + * with {@link WindowState#assignLayer} */ void assignRelativeLayerForImeTargetChild(SurfaceControl.Transaction t, WindowContainer child) { child.assignRelativeLayer(t, mImeWindowsContainers.getSurfaceControl(), 1); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 9151ddf17173..c16f95ee1160 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -16,20 +16,143 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_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.res.Configuration.UI_MODE_TYPE_CAR; +import static android.content.res.Configuration.UI_MODE_TYPE_MASK; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION; +import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW; +import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON; +import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; +import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN; +import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; +import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; +import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; +import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; +import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; +import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT; +import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; +import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; +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_CONSUMER; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT; +import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; +import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; +import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_TOAST; +import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; +import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING; +import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; +import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED; import static android.view.WindowManagerPolicyConstants.EXTRA_HDMI_PLUGGED_STATE; +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 com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT; +import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; +import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER; +import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT; +import static com.android.server.policy.WindowManagerPolicy.TRANSIT_HIDE; +import static com.android.server.policy.WindowManagerPolicy.TRANSIT_PREVIEW_DONE; +import static com.android.server.policy.WindowManagerPolicy.TRANSIT_SHOW; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT; +import static com.android.server.wm.ActivityTaskManagerInternal.SleepToken; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import static com.android.server.wm.WindowManagerService.localLOGV; +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.ActivityThread; +import android.app.StatusBarManager; +import android.content.Context; import android.content.Intent; +import android.content.res.Resources; +import android.graphics.Rect; +import android.hardware.input.InputManager; +import android.hardware.power.V1_0.PowerHint; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; +import android.util.ArraySet; +import android.util.PrintWriterPrinter; import android.util.Slog; +import android.view.DisplayCutout; +import android.view.Gravity; +import android.view.IApplicationToken; +import android.view.InputChannel; +import android.view.InputDevice; +import android.view.InputEvent; +import android.view.InputEventReceiver; +import android.view.MotionEvent; +import android.view.PointerIcon; +import android.view.Surface; +import android.view.View; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; +import android.view.WindowManagerGlobal; +import android.view.WindowManagerPolicyConstants; +import android.view.accessibility.AccessibilityManager; +import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ScreenShapeHelper; +import com.android.internal.util.ScreenshotHelper; +import com.android.server.LocalServices; +import com.android.server.UiThread; +import com.android.server.policy.WindowManagerPolicy; +import com.android.server.policy.WindowManagerPolicy.InputConsumer; +import com.android.server.policy.WindowManagerPolicy.NavigationBarPosition; import com.android.server.policy.WindowManagerPolicy.ScreenOnListener; import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs; +import com.android.server.policy.WindowOrientationListener; +import com.android.server.statusbar.StatusBarManagerInternal; +import com.android.server.wm.utils.InsetUtils; import java.io.PrintWriter; @@ -38,12 +161,61 @@ import java.io.PrintWriter; */ public class DisplayPolicy { private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayPolicy" : TAG_WM; + private static final boolean DEBUG = false; + + private static final boolean ALTERNATE_CAR_MODE_NAV_SIZE = false; + + // The panic gesture may become active only after the keyguard is dismissed and the immersive + // app shows again. If that doesn't happen for 30s we drop the gesture. + private static final long PANIC_GESTURE_EXPIRATION = 30000; + + // Controls navigation bar opacity depending on which workspace stacks are currently + // visible. + // Nav bar is always opaque when either the freeform stack or docked stack is visible. + private static final int NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED = 0; + // Nav bar is always translucent when the freeform stack is visible, otherwise always opaque. + private static final int NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE = 1; + + /** + * These are the system UI flags that, when changing, can cause the layout + * of the screen to change. + */ + private static final int SYSTEM_UI_CHANGING_LAYOUT = + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.STATUS_BAR_TRANSLUCENT + | View.NAVIGATION_BAR_TRANSLUCENT + | View.STATUS_BAR_TRANSPARENT + | View.NAVIGATION_BAR_TRANSPARENT; private final WindowManagerService mService; + private final Context mContext; + private final DisplayContent mDisplayContent; private final Object mLock; + private final Handler mHandler; private final boolean mCarDockEnablesAccelerometer; private final boolean mDeskDockEnablesAccelerometer; + private final boolean mTranslucentDecorEnabled; + private final AccessibilityManager mAccessibilityManager; + private final ImmersiveModeConfirmation mImmersiveModeConfirmation; + private final ScreenshotHelper mScreenshotHelper; + + private final Object mServiceAcquireLock = new Object(); + private StatusBarManagerInternal mStatusBarManagerInternal; + + private StatusBarManagerInternal getStatusBarManagerInternal() { + synchronized (mServiceAcquireLock) { + if (mStatusBarManagerInternal == null) { + mStatusBarManagerInternal = + LocalServices.getService(StatusBarManagerInternal.class); + } + return mStatusBarManagerInternal; + } + } + + @VisibleForTesting + private final SystemGesturesPointerEventListener mSystemGestures; private volatile int mLidState = LID_ABSENT; private volatile int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED; @@ -64,32 +236,311 @@ public class DisplayPolicy { private volatile boolean mKeyguardDrawComplete; private volatile boolean mWindowManagerDrawComplete; - DisplayPolicy(WindowManagerService service) { + private final ArraySet<WindowState> mScreenDecorWindows = new ArraySet<>(); + private WindowState mStatusBar = null; + private final int[] mStatusBarHeightForRotation = new int[4]; + private WindowState mNavigationBar = null; + @NavigationBarPosition + private int mNavigationBarPosition = NAV_BAR_BOTTOM; + private int[] mNavigationBarHeightForRotationDefault = new int[4]; + private int[] mNavigationBarWidthForRotationDefault = new int[4]; + private int[] mNavigationBarHeightForRotationInCarMode = new int[4]; + private int[] mNavigationBarWidthForRotationInCarMode = new int[4]; + + private final StatusBarController mStatusBarController = new StatusBarController(); + + private final BarController mNavigationBarController = new BarController("NavigationBar", + View.NAVIGATION_BAR_TRANSIENT, + View.NAVIGATION_BAR_UNHIDE, + View.NAVIGATION_BAR_TRANSLUCENT, + StatusBarManager.WINDOW_NAVIGATION_BAR, + FLAG_TRANSLUCENT_NAVIGATION, + View.NAVIGATION_BAR_TRANSPARENT); + + private final BarController.OnBarVisibilityChangedListener mNavBarVisibilityListener = + new BarController.OnBarVisibilityChangedListener() { + @Override + public void onBarVisibilityChanged(boolean visible) { + if (mAccessibilityManager == null) { + return; + } + mAccessibilityManager.notifyAccessibilityButtonVisibilityChanged(visible); + } + }; + + // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed + private NavigationBarExperiments mExperiments = new NavigationBarExperiments(); + // EXPERIMENT END + + @GuardedBy("mHandler") + private SleepToken mDreamingSleepToken; + + @GuardedBy("mHandler") + private SleepToken mWindowSleepToken; + + private final Runnable mAcquireSleepTokenRunnable; + private final Runnable mReleaseSleepTokenRunnable; + + // The windows we were told about in focusChanged. + private WindowState mFocusedWindow; + private WindowState mLastFocusedWindow; + + IApplicationToken mFocusedApp; + + int mLastSystemUiFlags; + // Bits that we are in the process of clearing, so we want to prevent + // them from being set by applications until everything has been updated + // to have them clear. + private int mResettingSystemUiFlags = 0; + // Bits that we are currently always keeping cleared. + private int mForceClearedSystemUiFlags = 0; + private int mLastFullscreenStackSysUiFlags; + private int mLastDockedStackSysUiFlags; + private final Rect mNonDockedStackBounds = new Rect(); + private final Rect mDockedStackBounds = new Rect(); + private final Rect mLastNonDockedStackBounds = new Rect(); + private final Rect mLastDockedStackBounds = new Rect(); + + // What we last reported to system UI about whether the compatibility + // menu needs to be displayed. + private boolean mLastFocusNeedsMenu = false; + // If nonzero, a panic gesture was performed at that time in uptime millis and is still pending. + private long mPendingPanicGestureUptime; + + private static final Rect sTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect(); + private static final Rect sTmpRect = new Rect(); + private static final Rect sTmpDockedFrame = new Rect(); + private static final Rect sTmpNavFrame = new Rect(); + private static final Rect sTmpLastParentFrame = new Rect(); + + private WindowState mTopFullscreenOpaqueWindowState; + private WindowState mTopFullscreenOpaqueOrDimmingWindowState; + private WindowState mTopDockedOpaqueWindowState; + private WindowState mTopDockedOpaqueOrDimmingWindowState; + private boolean mTopIsFullscreen; + private boolean mForceStatusBar; + private boolean mForceStatusBarFromKeyguard; + private boolean mForceStatusBarTransparent; + private int mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED; + private boolean mForcingShowNavBar; + private int mForcingShowNavBarLayer; + private boolean mForceShowSystemBars; + + private boolean mShowingDream; + private boolean mLastShowingDream; + private boolean mDreamingLockscreen; + private boolean mDreamingSleepTokenNeeded; + private boolean mWindowSleepTokenNeeded; + private boolean mLastWindowSleepTokenNeeded; + private boolean mAllowLockscreenWhenOn; + + private InputConsumer mInputConsumer = null; + + // -------- PolicyHandler -------- + private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 1; + private static final int MSG_REQUEST_TRANSIENT_BARS = 2; + private static final int MSG_DISPOSE_INPUT_CONSUMER = 3; + + private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0; + private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1; + + private class PolicyHandler extends Handler { + + PolicyHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE_DREAMING_SLEEP_TOKEN: + updateDreamingSleepToken(msg.arg1 != 0); + break; + case MSG_REQUEST_TRANSIENT_BARS: + WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS) + ? mStatusBar : mNavigationBar; + if (targetBar != null) { + requestTransientBars(targetBar); + } + break; + case MSG_DISPOSE_INPUT_CONSUMER: + disposeInputConsumer((InputConsumer) msg.obj); + break; + } + } + } + + DisplayPolicy(WindowManagerService service, DisplayContent displayContent) { mService = service; + mContext = displayContent.isDefaultDisplay ? service.mContext + : service.mContext.createDisplayContext(displayContent.getDisplay()); + mDisplayContent = displayContent; mLock = service.getWindowManagerLock(); - mCarDockEnablesAccelerometer = service.mContext.getResources().getBoolean( - com.android.internal.R.bool.config_carDockEnablesAccelerometer); - mDeskDockEnablesAccelerometer = service.mContext.getResources().getBoolean( - com.android.internal.R.bool.config_deskDockEnablesAccelerometer); + + final Resources r = mContext.getResources(); + mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer); + mDeskDockEnablesAccelerometer = r.getBoolean(R.bool.config_deskDockEnablesAccelerometer); + mTranslucentDecorEnabled = r.getBoolean(R.bool.config_enableTranslucentDecor); + updateConfigurationDependentBehaviors(); + + mAccessibilityManager = (AccessibilityManager) mContext.getSystemService( + Context.ACCESSIBILITY_SERVICE); + if (!displayContent.isDefaultDisplay) { + mAwake = true; + mScreenOnEarly = true; + mScreenOnFully = true; + } + + final Looper looper = UiThread.getHandler().getLooper(); + mHandler = new PolicyHandler(looper); + mSystemGestures = new SystemGesturesPointerEventListener(mContext, mHandler, + new SystemGesturesPointerEventListener.Callbacks() { + @Override + public void onSwipeFromTop() { + if (mStatusBar != null) { + requestTransientBars(mStatusBar); + } + } + + @Override + public void onSwipeFromBottom() { + if (mNavigationBar != null + && mNavigationBarPosition == NAV_BAR_BOTTOM) { + requestTransientBars(mNavigationBar); + } + } + + @Override + public void onSwipeFromRight() { + if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_RIGHT) { + requestTransientBars(mNavigationBar); + } + } + + @Override + public void onSwipeFromLeft() { + if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_LEFT) { + requestTransientBars(mNavigationBar); + } + } + + @Override + public void onFling(int duration) { + if (mService.mPowerManagerInternal != null) { + mService.mPowerManagerInternal.powerHint( + PowerHint.INTERACTION, duration); + } + } + + @Override + public void onDebug() { + // no-op + } + + private WindowOrientationListener getOrientationListener() { + final DisplayRotation rotation = mDisplayContent.getDisplayRotation(); + return rotation != null ? rotation.getOrientationListener() : null; + } + + @Override + public void onDown() { + final WindowOrientationListener listener = getOrientationListener(); + if (listener != null) { + listener.onTouchStart(); + } + } + + @Override + public void onUpOrCancel() { + final WindowOrientationListener listener = getOrientationListener(); + if (listener != null) { + listener.onTouchEnd(); + } + } + + @Override + public void onMouseHoverAtTop() { + mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS); + Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS); + msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS; + mHandler.sendMessageDelayed(msg, 500 /* delayMillis */); + } + + @Override + public void onMouseHoverAtBottom() { + mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS); + Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS); + msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION; + mHandler.sendMessageDelayed(msg, 500 /* delayMillis */); + } + + @Override + public void onMouseLeaveFromEdge() { + mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS); + } + }); + displayContent.registerPointerEventListener(mSystemGestures); + displayContent.mAppTransition.registerListenerLocked( + mStatusBarController.getAppTransitionListener()); + mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext, looper, + mService.mVrModeEnabled); + mAcquireSleepTokenRunnable = () -> { + if (mWindowSleepToken != null) { + return; + } + final int displayId = displayContent.getDisplayId(); + mWindowSleepToken = service.mAtmInternal.acquireSleepToken( + "WindowSleepTokenOnDisplay" + displayId, displayId); + }; + mReleaseSleepTokenRunnable = () -> { + if (mWindowSleepToken == null) { + return; + } + mWindowSleepToken.release(); + mWindowSleepToken = null; + }; + + // TODO: Make it can take screenshot on external display + mScreenshotHelper = displayContent.isDefaultDisplay + ? new ScreenshotHelper(mContext) : null; + } + + void systemReady() { + mSystemGestures.systemReady(); + } + + private int getDisplayId() { + return mDisplayContent.getDisplayId(); + } + + void onDisplayRemoved() { + mDisplayContent.unregisterPointerEventListener(mSystemGestures); } void configure(int width, int height, int shortSizeDp) { // Allow the navigation bar to move on non-square small devices (phones). mNavigationBarCanMove = width != height && shortSizeDp < 600; - mHasNavigationBar = mService.mContext.getResources().getBoolean( - com.android.internal.R.bool.config_showNavigationBar); + if (mDisplayContent.isDefaultDisplay) { + mHasNavigationBar = mContext.getResources().getBoolean(R.bool.config_showNavigationBar); - // Allow a system property to override this. Used by the emulator. - // See also hasNavigationBar(). - String navBarOverride = SystemProperties.get("qemu.hw.mainkeys"); - if ("1".equals(navBarOverride)) { - mHasNavigationBar = false; - } else if ("0".equals(navBarOverride)) { - mHasNavigationBar = true; + // Allow a system property to override this. Used by the emulator. + // See also hasNavigationBar(). + String navBarOverride = SystemProperties.get("qemu.hw.mainkeys"); + if ("1".equals(navBarOverride)) { + mHasNavigationBar = false; + } else if ("0".equals(navBarOverride)) { + mHasNavigationBar = true; + } + } else { + mHasNavigationBar = mDisplayContent.getDisplay().supportsSystemDecorations(); } } + void updateConfigurationDependentBehaviors() { + mNavBarOpacityMode = mContext.getResources().getInteger(R.integer.config_navBarOpacityMode); + } + public void setHdmiPlugged(boolean plugged) { setHdmiPlugged(plugged, false /* force */); } @@ -101,7 +552,7 @@ public class DisplayPolicy { final Intent intent = new Intent(ACTION_HDMI_PLUGGED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); intent.putExtra(EXTRA_HDMI_PLUGGED_STATE, plugged); - mService.mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } } @@ -244,17 +695,2642 @@ public class DisplayPolicy { return true; } + /** + * Sanitize the layout parameters coming from a client. Allows the policy + * to do things like ensure that windows of a specific type can't take + * input focus. + * + * @param attrs The window layout parameters to be modified. These values + * are modified in-place. + */ + public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs, + boolean hasStatusBarServicePermission) { + + final boolean isScreenDecor = (attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0; + if (mScreenDecorWindows.contains(win)) { + if (!isScreenDecor) { + // No longer has the flag set, so remove from the set. + mScreenDecorWindows.remove(win); + } + } else if (isScreenDecor && hasStatusBarServicePermission) { + mScreenDecorWindows.add(win); + } + + switch (attrs.type) { + case TYPE_SYSTEM_OVERLAY: + case TYPE_SECURE_SYSTEM_OVERLAY: + // These types of windows can't receive input events. + attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; + break; + case TYPE_DREAM: + case TYPE_WALLPAPER: + // Dreams and wallpapers don't have an app window token and can thus not be + // letterboxed. Hence always let them extend under the cutout. + attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + break; + case TYPE_STATUS_BAR: + + // If the Keyguard is in a hidden state (occluded by another window), we force to + // remove the wallpaper and keyguard flag so that any change in-flight after setting + // the keyguard as occluded wouldn't set these flags again. + // See {@link #processKeyguardSetHiddenResultLw}. + if (mService.mPolicy.isKeyguardOccluded()) { + attrs.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; + attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; + } + break; + + case TYPE_SCREENSHOT: + attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + break; + + case TYPE_TOAST: + // While apps should use the dedicated toast APIs to add such windows + // it possible legacy apps to add the window directly. Therefore, we + // make windows added directly by the app behave as a toast as much + // as possible in terms of timeout and animation. + if (attrs.hideTimeoutMilliseconds < 0 + || attrs.hideTimeoutMilliseconds > TOAST_WINDOW_TIMEOUT) { + attrs.hideTimeoutMilliseconds = TOAST_WINDOW_TIMEOUT; + } + attrs.windowAnimations = com.android.internal.R.style.Animation_Toast; + break; + } + + if (attrs.type != TYPE_STATUS_BAR) { + // The status bar is the only window allowed to exhibit keyguard behavior. + attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; + } + } + + /** + * Preflight adding a window to the system. + * + * Currently enforces that three window types are singletons per display: + * <ul> + * <li>{@link WindowManager.LayoutParams#TYPE_STATUS_BAR}</li> + * <li>{@link WindowManager.LayoutParams#TYPE_NAVIGATION_BAR}</li> + * </ul> + * + * @param win The window to be added + * @param attrs Information about the window to be added + * + * @return If ok, WindowManagerImpl.ADD_OKAY. If too many singletons, + * WindowManagerImpl.ADD_MULTIPLE_SINGLETON + */ + public int prepareAddWindowLw(WindowState win, WindowManager.LayoutParams attrs) { + + if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.STATUS_BAR_SERVICE, + "DisplayPolicy"); + mScreenDecorWindows.add(win); + } + + switch (attrs.type) { + case TYPE_STATUS_BAR: + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.STATUS_BAR_SERVICE, + "DisplayPolicy"); + if (mStatusBar != null) { + if (mStatusBar.isAlive()) { + return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; + } + } + mStatusBar = win; + mStatusBarController.setWindow(win); + if (mDisplayContent.isDefaultDisplay) { + mService.mPolicy.setKeyguardCandidateLw(win); + } + break; + case TYPE_NAVIGATION_BAR: + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.STATUS_BAR_SERVICE, + "DisplayPolicy"); + if (mNavigationBar != null) { + if (mNavigationBar.isAlive()) { + return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; + } + } + mNavigationBar = win; + mNavigationBarController.setWindow(win); + mNavigationBarController.setOnBarVisibilityChangedListener( + mNavBarVisibilityListener, true); + if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar); + break; + case TYPE_NAVIGATION_BAR_PANEL: + case TYPE_STATUS_BAR_PANEL: + case TYPE_STATUS_BAR_SUB_PANEL: + case TYPE_VOICE_INTERACTION_STARTING: + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.STATUS_BAR_SERVICE, + "DisplayPolicy"); + break; + } + return ADD_OKAY; + } + + /** + * Called when a window is being removed from a window manager. Must not + * throw an exception -- clean up as much as possible. + * + * @param win The window being removed. + */ + public void removeWindowLw(WindowState win) { + if (mStatusBar == win) { + mStatusBar = null; + mStatusBarController.setWindow(null); + if (mDisplayContent.isDefaultDisplay) { + mService.mPolicy.setKeyguardCandidateLw(null); + } + } else if (mNavigationBar == win) { + mNavigationBar = null; + mNavigationBarController.setWindow(null); + } + if (mLastFocusedWindow == win) { + mLastFocusedWindow = null; + } + mScreenDecorWindows.remove(win); + } + + /** + * Control the animation to run when a window's state changes. Return a + * non-0 number to force the animation to a specific resource ID, or 0 + * to use the default animation. + * + * @param win The window that is changing. + * @param transit What is happening to the window: + * {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_ENTER}, + * {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_EXIT}, + * {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_SHOW}, or + * {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_HIDE}. + * + * @return Resource ID of the actual animation to use, or 0 for none. + */ + public int selectAnimationLw(WindowState win, int transit) { + if (DEBUG_ANIM) Slog.i(TAG, "selectAnimation in " + win + + ": transit=" + transit); + if (win == mStatusBar) { + final boolean isKeyguard = (win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0; + final boolean expanded = win.getAttrs().height == MATCH_PARENT + && win.getAttrs().width == MATCH_PARENT; + if (isKeyguard || expanded) { + return -1; + } + if (transit == TRANSIT_EXIT + || transit == TRANSIT_HIDE) { + return R.anim.dock_top_exit; + } else if (transit == TRANSIT_ENTER + || transit == TRANSIT_SHOW) { + return R.anim.dock_top_enter; + } + } else if (win == mNavigationBar) { + if (win.getAttrs().windowAnimations != 0) { + return 0; + } + // This can be on either the bottom or the right or the left. + if (mNavigationBarPosition == NAV_BAR_BOTTOM) { + if (transit == TRANSIT_EXIT + || transit == TRANSIT_HIDE) { + if (mService.mPolicy.isKeyguardShowingAndNotOccluded()) { + return R.anim.dock_bottom_exit_keyguard; + } else { + return R.anim.dock_bottom_exit; + } + } else if (transit == TRANSIT_ENTER + || transit == TRANSIT_SHOW) { + return R.anim.dock_bottom_enter; + } + } else if (mNavigationBarPosition == NAV_BAR_RIGHT) { + if (transit == TRANSIT_EXIT + || transit == TRANSIT_HIDE) { + return R.anim.dock_right_exit; + } else if (transit == TRANSIT_ENTER + || transit == TRANSIT_SHOW) { + return R.anim.dock_right_enter; + } + } else if (mNavigationBarPosition == NAV_BAR_LEFT) { + if (transit == TRANSIT_EXIT + || transit == TRANSIT_HIDE) { + return R.anim.dock_left_exit; + } else if (transit == TRANSIT_ENTER + || transit == TRANSIT_SHOW) { + return R.anim.dock_left_enter; + } + } + } else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) { + return selectDockedDividerAnimationLw(win, transit); + } + + if (transit == TRANSIT_PREVIEW_DONE) { + if (win.hasAppShownWindows()) { + if (DEBUG_ANIM) Slog.i(TAG, "**** STARTING EXIT"); + return R.anim.app_starting_exit; + } + } else if (win.getAttrs().type == TYPE_DREAM && mDreamingLockscreen + && transit == TRANSIT_ENTER) { + // Special case: we are animating in a dream, while the keyguard + // is shown. We don't want an animation on the dream, because + // we need it shown immediately with the keyguard animating away + // to reveal it. + return -1; + } + + return 0; + } + + private int selectDockedDividerAnimationLw(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 0; + } + if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) { + return R.anim.fade_in; + } else if (transit == TRANSIT_EXIT) { + return R.anim.fade_out; + } else { + return 0; + } + } + + /** + * Determine the animation to run for a rotation transition based on the + * top fullscreen windows {@link WindowManager.LayoutParams#rotationAnimation} + * and whether it is currently fullscreen and frontmost. + * + * @param anim The exiting animation resource id is stored in anim[0], the + * entering animation resource id is stored in anim[1]. + */ + public void selectRotationAnimationLw(int anim[]) { + // If the screen is off or non-interactive, force a jumpcut. + final boolean forceJumpcut = !mScreenOnFully || !mService.mPolicy.okToAnimate(); + if (DEBUG_ANIM) Slog.i(TAG, "selectRotationAnimation mTopFullscreen=" + + mTopFullscreenOpaqueWindowState + " rotationAnimation=" + + (mTopFullscreenOpaqueWindowState == null + ? "0" : mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation) + + " forceJumpcut=" + forceJumpcut); + if (forceJumpcut) { + anim[0] = R.anim.rotation_animation_jump_exit; + anim[1] = R.anim.rotation_animation_enter; + return; + } + if (mTopFullscreenOpaqueWindowState != null) { + int animationHint = mTopFullscreenOpaqueWindowState.getRotationAnimationHint(); + if (animationHint < 0 && mTopIsFullscreen) { + animationHint = mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation; + } + switch (animationHint) { + case ROTATION_ANIMATION_CROSSFADE: + case ROTATION_ANIMATION_SEAMLESS: // Crossfade is fallback for seamless. + anim[0] = R.anim.rotation_animation_xfade_exit; + anim[1] = R.anim.rotation_animation_enter; + break; + case ROTATION_ANIMATION_JUMPCUT: + anim[0] = R.anim.rotation_animation_jump_exit; + anim[1] = R.anim.rotation_animation_enter; + break; + case ROTATION_ANIMATION_ROTATE: + default: + anim[0] = anim[1] = 0; + break; + } + } else { + anim[0] = anim[1] = 0; + } + } + + /** + * Validate whether the current top fullscreen has specified the same + * {@link WindowManager.LayoutParams#rotationAnimation} value as that + * being passed in from the previous top fullscreen window. + * + * @param exitAnimId exiting resource id from the previous window. + * @param enterAnimId entering resource id from the previous window. + * @param forceDefault For rotation animations only, if true ignore the + * animation values and just return false. + * @return true if the previous values are still valid, false if they + * should be replaced with the default. + */ + public boolean validateRotationAnimationLw(int exitAnimId, int enterAnimId, + boolean forceDefault) { + switch (exitAnimId) { + case R.anim.rotation_animation_xfade_exit: + case R.anim.rotation_animation_jump_exit: + // These are the only cases that matter. + if (forceDefault) { + return false; + } + int anim[] = new int[2]; + selectRotationAnimationLw(anim); + return (exitAnimId == anim[0] && enterAnimId == anim[1]); + default: + return true; + } + } + + /** + * Called when a new system UI visibility is being reported, allowing + * the policy to adjust what is actually reported. + * @param visibility The raw visibility reported by the status bar. + * @return The new desired visibility. + */ + public int adjustSystemUiVisibilityLw(int visibility) { + mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); + mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); + + // Reset any bits in mForceClearingStatusBarVisibility that + // are now clear. + mResettingSystemUiFlags &= visibility; + // Clear any bits in the new visibility that are currently being + // force cleared, before reporting it. + return visibility & ~mResettingSystemUiFlags + & ~mForceClearedSystemUiFlags; + } + + /** + * @return true if the navigation bar is forced to stay visible + */ + public boolean isNavBarForcedShownLw(WindowState windowState) { + return mForceShowSystemBars; + } + + // TODO: Should probably be moved into DisplayFrames. + /** + * Return the layout hints for a newly added window. These values are computed on the + * most recent layout, so they are not guaranteed to be correct. + * + * @param attrs The LayoutParams of the window. + * @param taskBounds The bounds of the task this window is on or {@code null} if no task is + * associated with the window. + * @param displayFrames display frames. + * @param floatingStack Whether the window's stack is floating. + * @param outFrame The frame of the window. + * @param outContentInsets The areas covered by system windows, expressed as positive insets. + * @param outStableInsets The areas covered by stable system windows irrespective of their + * current visibility. Expressed as positive insets. + * @param outOutsets The areas that are not real display, but we would like to treat as such. + * @param outDisplayCutout The area that has been cut away from the display. + * @return Whether to always consume the navigation bar. + * See {@link #isNavBarForcedShownLw(WindowState)}. + */ + public boolean getLayoutHintLw(LayoutParams attrs, Rect taskBounds, + DisplayFrames displayFrames, boolean floatingStack, Rect outFrame, + Rect outContentInsets, Rect outStableInsets, + Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) { + final int fl = PolicyControl.getWindowFlags(null, attrs); + final int pfl = attrs.privateFlags; + final int requestedSysUiVis = PolicyControl.getSystemUiVisibility(null, attrs); + final int sysUiVis = requestedSysUiVis | getImpliedSysUiFlagsForLayout(attrs); + final int displayRotation = displayFrames.mRotation; + + final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl); + if (useOutsets) { + int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources()); + if (outset > 0) { + if (displayRotation == Surface.ROTATION_0) { + outOutsets.bottom += outset; + } else if (displayRotation == Surface.ROTATION_90) { + outOutsets.right += outset; + } else if (displayRotation == Surface.ROTATION_180) { + outOutsets.top += outset; + } else if (displayRotation == Surface.ROTATION_270) { + outOutsets.left += outset; + } + } + } + + final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) != 0; + final boolean layoutInScreenAndInsetDecor = layoutInScreen + && (fl & FLAG_LAYOUT_INSET_DECOR) != 0; + final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0; + + if (layoutInScreenAndInsetDecor && !screenDecor) { + if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) { + outFrame.set(displayFrames.mUnrestricted); + } else { + outFrame.set(displayFrames.mRestricted); + } + + final Rect sf; + if (floatingStack) { + sf = null; + } else { + sf = displayFrames.mStable; + } + + final Rect cf; + if (floatingStack) { + cf = null; + } else if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) { + if ((fl & FLAG_FULLSCREEN) != 0) { + cf = displayFrames.mStableFullscreen; + } else { + cf = displayFrames.mStable; + } + } else if ((fl & FLAG_FULLSCREEN) != 0 || (fl & FLAG_LAYOUT_IN_OVERSCAN) != 0) { + cf = displayFrames.mOverscan; + } else { + cf = displayFrames.mCurrent; + } + + if (taskBounds != null) { + outFrame.intersect(taskBounds); + } + InsetUtils.insetsBetweenFrames(outFrame, cf, outContentInsets); + InsetUtils.insetsBetweenFrames(outFrame, sf, outStableInsets); + outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(outFrame) + .getDisplayCutout()); + return mForceShowSystemBars; + } else { + if (layoutInScreen) { + outFrame.set(displayFrames.mUnrestricted); + } else { + outFrame.set(displayFrames.mStable); + } + if (taskBounds != null) { + outFrame.intersect(taskBounds); + } + + outContentInsets.setEmpty(); + outStableInsets.setEmpty(); + outDisplayCutout.set(DisplayCutout.NO_CUTOUT); + return mForceShowSystemBars; + } + } + + private static int getImpliedSysUiFlagsForLayout(LayoutParams attrs) { + int impliedFlags = 0; + if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) { + impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + } + final boolean forceWindowDrawsStatusBarBackground = + (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0; + if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 + || forceWindowDrawsStatusBarBackground + && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT) { + impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + } + return impliedFlags; + } + + private static boolean shouldUseOutsets(WindowManager.LayoutParams attrs, int fl) { + return attrs.type == TYPE_WALLPAPER || (fl & (WindowManager.LayoutParams.FLAG_FULLSCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN)) != 0; + } + + private final Runnable mClearHideNavigationFlag = new Runnable() { + @Override + public void run() { + synchronized (mLock) { + mForceClearedSystemUiFlags &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + mDisplayContent.reevaluateStatusBarVisibility(); + } + } + }; + + /** + * Input handler used while nav bar is hidden. Captures any touch on the screen, + * to determine when the nav bar should be shown and prevent applications from + * receiving those touches. + */ + private final class HideNavInputEventReceiver extends InputEventReceiver { + HideNavInputEventReceiver(InputChannel inputChannel, Looper looper) { + super(inputChannel, looper); + } + + @Override + public void onInputEvent(InputEvent event) { + try { + if (event instanceof MotionEvent + && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { + final MotionEvent motionEvent = (MotionEvent) event; + if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { + // When the user taps down, we re-show the nav bar. + boolean changed = false; + synchronized (mLock) { + if (mInputConsumer == null) { + return; + } + // Any user activity always causes us to show the + // navigation controls, if they had been hidden. + // We also clear the low profile and only content + // flags so that tapping on the screen will atomically + // restore all currently hidden screen decorations. + int newVal = mResettingSystemUiFlags + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LOW_PROFILE + | View.SYSTEM_UI_FLAG_FULLSCREEN; + if (mResettingSystemUiFlags != newVal) { + mResettingSystemUiFlags = newVal; + changed = true; + } + // We don't allow the system's nav bar to be hidden + // again for 1 second, to prevent applications from + // spamming us and keeping it from being shown. + newVal = mForceClearedSystemUiFlags + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + if (mForceClearedSystemUiFlags != newVal) { + mForceClearedSystemUiFlags = newVal; + changed = true; + mHandler.postDelayed(mClearHideNavigationFlag, 1000); + } + if (changed) { + mDisplayContent.reevaluateStatusBarVisibility(); + } + } + } + } + } finally { + finishInputEvent(event, false /* handled */); + } + } + } + + /** + * Called when layout of the windows is about to start. + * + * @param displayFrames frames of the display we are doing layout on. + * @param uiMode The current uiMode in configuration. + */ + public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) { + displayFrames.onBeginLayout(); + mSystemGestures.screenWidth = displayFrames.mUnrestricted.width(); + mSystemGestures.screenHeight = displayFrames.mUnrestricted.height(); + + // For purposes of putting out fake window up to steal focus, we will + // drive nav being hidden only by whether it is requested. + final int sysui = mLastSystemUiFlags; + boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; + boolean navTranslucent = (sysui + & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0; + boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0; + boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; + boolean navAllowedHidden = immersive || immersiveSticky; + navTranslucent &= !immersiveSticky; // transient trumps translucent + boolean isKeyguardShowing = isStatusBarKeyguard() + && !mService.mPolicy.isKeyguardOccluded(); + if (!isKeyguardShowing) { + navTranslucent &= areTranslucentBarsAllowed(); + } + boolean statusBarForcesShowingNavigation = !isKeyguardShowing && mStatusBar != null + && (mStatusBar.getAttrs().privateFlags + & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0; + + // When the navigation bar isn't visible, we put up a fake input window to catch all + // touch events. This way we can detect when the user presses anywhere to bring back the + // nav bar and ensure the application doesn't see the event. + if (navVisible || navAllowedHidden) { + if (mInputConsumer != null) { + mHandler.sendMessage( + mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer)); + mInputConsumer = null; + } + } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) { + mInputConsumer = mService.createInputConsumer(mHandler.getLooper(), + INPUT_CONSUMER_NAVIGATION, + HideNavInputEventReceiver::new, + displayFrames.mDisplayId); + // As long as mInputConsumer is active, hover events are not dispatched to the app + // and the pointer icon is likely to become stale. Hide it to avoid confusion. + InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL); + } + + // For purposes of positioning and showing the nav bar, if we have decided that it can't + // be hidden (because of the screen aspect ratio), then take that into account. + navVisible |= !canHideNavigationBar(); + + boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, navVisible, + navTranslucent, navAllowedHidden, statusBarForcesShowingNavigation); + if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock); + updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui, isKeyguardShowing); + if (updateSysUiVisibility) { + updateSystemUiVisibilityLw(); + } + layoutScreenDecorWindows(displayFrames); + + if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) { + // Make sure that the zone we're avoiding for the cutout is at least as tall as the + // status bar; otherwise fullscreen apps will end up cutting halfway into the status + // bar. + displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top, + displayFrames.mStable.top); + } + } + + private void layoutScreenDecorWindows(DisplayFrames displayFrames) { + if (mScreenDecorWindows.isEmpty()) { + return; + } + + sTmpRect.setEmpty(); + sTmpDockedFrame.set(displayFrames.mDock); + + final int displayId = displayFrames.mDisplayId; + final Rect dockFrame = displayFrames.mDock; + final int displayHeight = displayFrames.mDisplayHeight; + final int displayWidth = displayFrames.mDisplayWidth; + + for (int i = mScreenDecorWindows.size() - 1; i >= 0; --i) { + final WindowState w = mScreenDecorWindows.valueAt(i); + if (w.getDisplayId() != displayId || !w.isVisibleLw()) { + // Skip if not on the same display or not visible. + continue; + } + + w.getWindowFrames().setFrames(sTmpDockedFrame /* parentFrame */, + sTmpDockedFrame /* displayFrame */, sTmpDockedFrame /* overscanFrame */, + sTmpDockedFrame /* contentFrame */, sTmpDockedFrame /* visibleFrame */, + sTmpRect /* decorFrame */, sTmpDockedFrame /* stableFrame */, + sTmpDockedFrame /* outsetFrame */); + w.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout); + w.computeFrameLw(); + final Rect frame = w.getFrameLw(); + + if (frame.left <= 0 && frame.top <= 0) { + // Docked at left or top. + if (frame.bottom >= displayHeight) { + // Docked left. + dockFrame.left = Math.max(frame.right, dockFrame.left); + } else if (frame.right >= displayWidth) { + // Docked top. + dockFrame.top = Math.max(frame.bottom, dockFrame.top); + } else { + Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w + + " not docked on left or top of display. frame=" + frame + + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight); + } + } else if (frame.right >= displayWidth && frame.bottom >= displayHeight) { + // Docked at right or bottom. + if (frame.top <= 0) { + // Docked right. + dockFrame.right = Math.min(frame.left, dockFrame.right); + } else if (frame.left <= 0) { + // Docked bottom. + dockFrame.bottom = Math.min(frame.top, dockFrame.bottom); + } else { + Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w + + " not docked on right or bottom" + " of display. frame=" + frame + + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight); + } + } else { + // Screen decor windows are required to be docked on one of the sides of the screen. + Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w + + " not docked on one of the sides of the display. frame=" + frame + + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight); + } + } + + displayFrames.mRestricted.set(dockFrame); + displayFrames.mCurrent.set(dockFrame); + displayFrames.mVoiceContent.set(dockFrame); + displayFrames.mSystem.set(dockFrame); + displayFrames.mContent.set(dockFrame); + displayFrames.mRestrictedOverscan.set(dockFrame); + } + + private boolean layoutStatusBar(DisplayFrames displayFrames, int sysui, + boolean isKeyguardShowing) { + // decide where the status bar goes ahead of time + if (mStatusBar == null) { + return false; + } + // apply any navigation bar insets + sTmpRect.setEmpty(); + mStatusBar.getWindowFrames().setFrames(displayFrames.mUnrestricted /* parentFrame */, + displayFrames.mUnrestricted /* displayFrame */, + displayFrames.mStable /* overscanFrame */, displayFrames.mStable /* contentFrame */, + displayFrames.mStable /* visibleFrame */, sTmpRect /* decorFrame */, + displayFrames.mStable /* stableFrame */, displayFrames.mStable /* outsetFrame */); + mStatusBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout); + + // Let the status bar determine its size. + mStatusBar.computeFrameLw(); + + // For layout, the status bar is always at the top with our fixed height. + displayFrames.mStable.top = displayFrames.mUnrestricted.top + + mStatusBarHeightForRotation[displayFrames.mRotation]; + // Make sure the status bar covers the entire cutout height + displayFrames.mStable.top = Math.max(displayFrames.mStable.top, + displayFrames.mDisplayCutoutSafe.top); + + // Tell the bar controller where the collapsed status bar content is + sTmpRect.set(mStatusBar.getContentFrameLw()); + sTmpRect.intersect(displayFrames.mDisplayCutoutSafe); + sTmpRect.top = mStatusBar.getContentFrameLw().top; // Ignore top display cutout inset + sTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size + mStatusBarController.setContentFrame(sTmpRect); + + boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0; + boolean statusBarTranslucent = (sysui + & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0; + if (!isKeyguardShowing) { + statusBarTranslucent &= areTranslucentBarsAllowed(); + } + + // If the status bar is hidden, we don't want to cause windows behind it to scroll. + if (mStatusBar.isVisibleLw() && !statusBarTransient) { + // Status bar may go away, so the screen area it occupies is available to apps but just + // covering them when the status bar is visible. + final Rect dockFrame = displayFrames.mDock; + dockFrame.top = displayFrames.mStable.top; + displayFrames.mContent.set(dockFrame); + displayFrames.mVoiceContent.set(dockFrame); + displayFrames.mCurrent.set(dockFrame); + + if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format( + "dock=%s content=%s cur=%s", dockFrame.toString(), + displayFrames.mContent.toString(), displayFrames.mCurrent.toString())); + + if (!mStatusBar.isAnimatingLw() && !statusBarTranslucent + && !mStatusBarController.wasRecentlyTranslucent()) { + // If the opaque status bar is currently requested to be visible, and not in the + // process of animating on or off, then we can tell the app that it is covered by + // it. + displayFrames.mSystem.top = displayFrames.mStable.top; + } + } + return mStatusBarController.checkHiddenLw(); + } + + private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, boolean navVisible, + boolean navTranslucent, boolean navAllowedHidden, + boolean statusBarForcesShowingNavigation) { + if (mNavigationBar == null) { + return false; + } + + final Rect navigationFrame = sTmpNavFrame; + boolean transientNavBarShowing = mNavigationBarController.isTransientShowing(); + // Force the navigation bar to its appropriate place and size. We need to do this directly, + // instead of relying on it to bubble up from the nav bar, because this needs to change + // atomically with screen rotations. + final int rotation = displayFrames.mRotation; + final int displayHeight = displayFrames.mDisplayHeight; + final int displayWidth = displayFrames.mDisplayWidth; + final Rect dockFrame = displayFrames.mDock; + mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation); + + final Rect cutoutSafeUnrestricted = sTmpRect; + cutoutSafeUnrestricted.set(displayFrames.mUnrestricted); + cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe); + + if (mNavigationBarPosition == NAV_BAR_BOTTOM) { + // It's a system nav bar or a portrait screen; nav bar goes on bottom. + final int top = cutoutSafeUnrestricted.bottom + - getNavigationBarHeight(rotation, uiMode); + // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed + final int topNavBar = cutoutSafeUnrestricted.bottom + - mExperiments.getNavigationBarFrameHeight(); + navigationFrame.set(0, topNavBar, displayWidth, displayFrames.mUnrestricted.bottom); + // EXPERIMENT END + displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top; + if (transientNavBarShowing) { + mNavigationBarController.setBarShowingLw(true); + } else if (navVisible) { + mNavigationBarController.setBarShowingLw(true); + dockFrame.bottom = displayFrames.mRestricted.bottom = + displayFrames.mRestrictedOverscan.bottom = top; + } else { + // We currently want to hide the navigation UI - unless we expanded the status bar. + mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation); + } + if (navVisible && !navTranslucent && !navAllowedHidden + && !mNavigationBar.isAnimatingLw() + && !mNavigationBarController.wasRecentlyTranslucent()) { + // If the opaque nav bar is currently requested to be visible and not in the process + // of animating on or off, then we can tell the app that it is covered by it. + displayFrames.mSystem.bottom = top; + } + } else if (mNavigationBarPosition == NAV_BAR_RIGHT) { + // Landscape screen; nav bar goes to the right. + final int left = cutoutSafeUnrestricted.right + - getNavigationBarWidth(rotation, uiMode); + // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed + final int leftNavBar = cutoutSafeUnrestricted.right + - mExperiments.getNavigationBarFrameWidth(); + navigationFrame.set(leftNavBar, 0, displayFrames.mUnrestricted.right, displayHeight); + // EXPERIMENT END + displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left; + if (transientNavBarShowing) { + mNavigationBarController.setBarShowingLw(true); + } else if (navVisible) { + mNavigationBarController.setBarShowingLw(true); + dockFrame.right = displayFrames.mRestricted.right = + displayFrames.mRestrictedOverscan.right = left; + } else { + // We currently want to hide the navigation UI - unless we expanded the status bar. + mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation); + } + if (navVisible && !navTranslucent && !navAllowedHidden + && !mNavigationBar.isAnimatingLw() + && !mNavigationBarController.wasRecentlyTranslucent()) { + // If the nav bar is currently requested to be visible, and not in the process of + // animating on or off, then we can tell the app that it is covered by it. + displayFrames.mSystem.right = left; + } + } else if (mNavigationBarPosition == NAV_BAR_LEFT) { + // Seascape screen; nav bar goes to the left. + final int right = cutoutSafeUnrestricted.left + + getNavigationBarWidth(rotation, uiMode); + // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed + final int rightNavBar = cutoutSafeUnrestricted.left + + mExperiments.getNavigationBarFrameWidth(); + navigationFrame.set(displayFrames.mUnrestricted.left, 0, rightNavBar, displayHeight); + // EXPERIMENT END + displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right; + if (transientNavBarShowing) { + mNavigationBarController.setBarShowingLw(true); + } else if (navVisible) { + mNavigationBarController.setBarShowingLw(true); + dockFrame.left = displayFrames.mRestricted.left = + displayFrames.mRestrictedOverscan.left = right; + } else { + // We currently want to hide the navigation UI - unless we expanded the status bar. + mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation); + } + if (navVisible && !navTranslucent && !navAllowedHidden + && !mNavigationBar.isAnimatingLw() + && !mNavigationBarController.wasRecentlyTranslucent()) { + // If the nav bar is currently requested to be visible, and not in the process of + // animating on or off, then we can tell the app that it is covered by it. + displayFrames.mSystem.left = right; + } + } + + // Make sure the content and current rectangles are updated to account for the restrictions + // from the navigation bar. + displayFrames.mCurrent.set(dockFrame); + displayFrames.mVoiceContent.set(dockFrame); + displayFrames.mContent.set(dockFrame); + // And compute the final frame. + sTmpRect.setEmpty(); + mNavigationBar.getWindowFrames().setFrames(navigationFrame /* parentFrame */, + navigationFrame /* displayFrame */, navigationFrame /* overscanFrame */, + displayFrames.mDisplayCutoutSafe /* contentFrame */, + navigationFrame /* visibleFrame */, sTmpRect /* decorFrame */, + navigationFrame /* stableFrame */, + displayFrames.mDisplayCutoutSafe /* outsetFrame */); + mNavigationBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout); + mNavigationBar.computeFrameLw(); + mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw()); + + if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame); + return mNavigationBarController.checkHiddenLw(); + } + + private void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached, + boolean insetDecors, Rect pf, Rect df, Rect of, Rect cf, Rect vf, + DisplayFrames displayFrames) { + if (!win.isInputMethodTarget() && attached.isInputMethodTarget()) { + // Here's a special case: if the child window is not the 'dock window' + // or input method target, and the window it is attached to is below + // the dock window, then the frames we computed for the window it is + // attached to can not be used because the dock is effectively part + // of the underlying window and the attached window is floating on top + // of the whole thing. So, we ignore the attached window and explicitly + // compute the frames that would be appropriate without the dock. + vf.set(displayFrames.mDock); + cf.set(displayFrames.mDock); + of.set(displayFrames.mDock); + df.set(displayFrames.mDock); + } else { + // The effective display frame of the attached window depends on whether it is taking + // care of insetting its content. If not, we need to use the parent's content frame so + // that the entire window is positioned within that content. Otherwise we can use the + // overscan frame and let the attached window take care of positioning its content + // appropriately. + if (adjust != SOFT_INPUT_ADJUST_RESIZE) { + // Set the content frame of the attached window to the parent's decor frame + // (same as content frame when IME isn't present) if specifically requested by + // setting {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR} flag. + // Otherwise, use the overscan frame. + cf.set((fl & FLAG_LAYOUT_ATTACHED_IN_DECOR) != 0 + ? attached.getContentFrameLw() : attached.getOverscanFrameLw()); + } else { + // If the window is resizing, then we want to base the content frame on our attached + // content frame to resize...however, things can be tricky if the attached window is + // NOT in resize mode, in which case its content frame will be larger. + // Ungh. So to deal with that, make sure the content frame we end up using is not + // covering the IM dock. + cf.set(attached.getContentFrameLw()); + if (attached.isVoiceInteraction()) { + cf.intersectUnchecked(displayFrames.mVoiceContent); + } else if (win.isInputMethodTarget() || attached.isInputMethodTarget()) { + cf.intersectUnchecked(displayFrames.mContent); + } + } + df.set(insetDecors ? attached.getDisplayFrameLw() : cf); + of.set(insetDecors ? attached.getOverscanFrameLw() : cf); + vf.set(attached.getVisibleFrameLw()); + } + // The LAYOUT_IN_SCREEN flag is used to determine whether the attached window should be + // positioned relative to its parent or the entire screen. + pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrameLw() : df); + } + + private void applyStableConstraints(int sysui, int fl, Rect r, DisplayFrames displayFrames) { + if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) == 0) { + return; + } + // If app is requesting a stable layout, don't let the content insets go below the stable + // values. + if ((fl & FLAG_FULLSCREEN) != 0) { + r.intersectUnchecked(displayFrames.mStableFullscreen); + } else { + r.intersectUnchecked(displayFrames.mStable); + } + } + + private boolean canReceiveInput(WindowState win) { + boolean notFocusable = + (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0; + boolean altFocusableIm = + (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0; + boolean notFocusableForIm = notFocusable ^ altFocusableIm; + return !notFocusableForIm; + } + + /** + * Called for each window attached to the window manager as layout is proceeding. The + * implementation of this function must take care of setting the window's frame, either here or + * in finishLayout(). + * + * @param win The window being positioned. + * @param attached For sub-windows, the window it is attached to; this + * window will already have had layoutWindow() called on it + * so you can use its Rect. Otherwise null. + * @param displayFrames The display frames. + */ + public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) { + // We've already done the navigation bar, status bar, and all screen decor windows. If the + // status bar can receive input, we need to layout it again to accommodate for the IME + // window. + if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar + || mScreenDecorWindows.contains(win)) { + return; + } + final WindowManager.LayoutParams attrs = win.getAttrs(); + final boolean isDefaultDisplay = win.isDefaultDisplay(); + + final int type = attrs.type; + final int fl = PolicyControl.getWindowFlags(win, attrs); + final int pfl = attrs.privateFlags; + final int sim = attrs.softInputMode; + final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs); + final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs); + + final WindowFrames windowFrames = win.getWindowFrames(); + + windowFrames.setHasOutsets(false); + sTmpLastParentFrame.set(windowFrames.mParentFrame); + final Rect pf = windowFrames.mParentFrame; + final Rect df = windowFrames.mDisplayFrame; + final Rect of = windowFrames.mOverscanFrame; + final Rect cf = windowFrames.mContentFrame; + final Rect vf = windowFrames.mVisibleFrame; + final Rect dcf = windowFrames.mDecorFrame; + final Rect sf = windowFrames.mStableFrame; + dcf.setEmpty(); + windowFrames.setParentFrameWasClippedByDisplayCutout(false); + windowFrames.setDisplayCutout(displayFrames.mDisplayCutout); + + final boolean hasNavBar = hasNavigationBar() && mNavigationBar != null + && mNavigationBar.isVisibleLw(); + + final int adjust = sim & SOFT_INPUT_MASK_ADJUST; + + final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0 + || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; + + final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN; + final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR; + + sf.set(displayFrames.mStable); + + if (type == TYPE_INPUT_METHOD) { + vf.set(displayFrames.mDock); + cf.set(displayFrames.mDock); + of.set(displayFrames.mDock); + df.set(displayFrames.mDock); + windowFrames.mParentFrame.set(displayFrames.mDock); + // IM dock windows layout below the nav bar... + pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom; + // ...with content insets above the nav bar + cf.bottom = vf.bottom = displayFrames.mStable.bottom; + if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) { + // The status bar forces the navigation bar while it's visible. Make sure the IME + // avoids the navigation bar in that case. + if (mNavigationBarPosition == NAV_BAR_RIGHT) { + pf.right = df.right = of.right = cf.right = vf.right = + displayFrames.mStable.right; + } else if (mNavigationBarPosition == NAV_BAR_LEFT) { + pf.left = df.left = of.left = cf.left = vf.left = displayFrames.mStable.left; + } + } + + // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed + // Offset the ime to avoid overlapping with the nav bar + mExperiments.offsetWindowFramesForNavBar(mNavigationBarPosition, win); + // EXPERIMENT END + + // IM dock windows always go to the bottom of the screen. + attrs.gravity = Gravity.BOTTOM; + } else if (type == TYPE_VOICE_INTERACTION) { + of.set(displayFrames.mUnrestricted); + df.set(displayFrames.mUnrestricted); + pf.set(displayFrames.mUnrestricted); + if (adjust != SOFT_INPUT_ADJUST_RESIZE) { + cf.set(displayFrames.mDock); + } else { + cf.set(displayFrames.mContent); + } + if (adjust != SOFT_INPUT_ADJUST_NOTHING) { + vf.set(displayFrames.mCurrent); + } else { + vf.set(cf); + } + } else if (type == TYPE_WALLPAPER) { + layoutWallpaper(displayFrames, pf, df, of, cf); + } else if (win == mStatusBar) { + of.set(displayFrames.mUnrestricted); + df.set(displayFrames.mUnrestricted); + pf.set(displayFrames.mUnrestricted); + cf.set(displayFrames.mStable); + vf.set(displayFrames.mStable); + + if (adjust == SOFT_INPUT_ADJUST_RESIZE) { + cf.bottom = displayFrames.mContent.bottom; + } else { + cf.bottom = displayFrames.mDock.bottom; + vf.bottom = displayFrames.mContent.bottom; + } + } else { + dcf.set(displayFrames.mSystem); + final boolean inheritTranslucentDecor = + (attrs.privateFlags & PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0; + final boolean isAppWindow = + type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW; + final boolean topAtRest = + win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw(); + if (isAppWindow && !inheritTranslucentDecor && !topAtRest) { + if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 + && (fl & FLAG_FULLSCREEN) == 0 + && (fl & FLAG_TRANSLUCENT_STATUS) == 0 + && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 + && (pfl & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) == 0) { + // Ensure policy decor includes status bar + dcf.top = displayFrames.mStable.top; + } + if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0 + && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0 + && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) { + // Ensure policy decor includes navigation bar + dcf.bottom = displayFrames.mStable.bottom; + dcf.right = displayFrames.mStable.right; + } + } + + if (layoutInScreen && layoutInsetDecor) { + if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() + + "): IN_SCREEN, INSET_DECOR"); + // This is the case for a normal activity window: we want it to cover all of the + // screen space, and it can take care of moving its contents to account for screen + // decorations that intrude into that space. + if (attached != null) { + // If this window is attached to another, our display + // frame is the same as the one we are attached to. + setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf, + displayFrames); + } else { + if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) { + // Status bar panels are the only windows who can go on top of the status + // bar. They are protected by the STATUS_BAR_SERVICE permission, so they + // have the same privileges as the status bar itself. + // + // However, they should still dodge the navigation bar if it exists. + + pf.left = df.left = of.left = hasNavBar + ? displayFrames.mDock.left : displayFrames.mUnrestricted.left; + pf.top = df.top = of.top = displayFrames.mUnrestricted.top; + pf.right = df.right = of.right = hasNavBar + ? displayFrames.mRestricted.right + : displayFrames.mUnrestricted.right; + pf.bottom = df.bottom = of.bottom = hasNavBar + ? displayFrames.mRestricted.bottom + : displayFrames.mUnrestricted.bottom; + + if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out status bar window: " + pf); + } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0 + && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) { + // Asking to layout into the overscan region, so give it that pure + // unrestricted area. + of.set(displayFrames.mOverscan); + df.set(displayFrames.mOverscan); + pf.set(displayFrames.mOverscan); + } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 + && (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW + || type == TYPE_VOLUME_OVERLAY)) { + // Asking for layout as if the nav bar is hidden, lets the application + // extend into the unrestricted overscan screen area. We only do this for + // application windows and certain system windows to ensure no window that + // can be above the nav bar can do this. + df.set(displayFrames.mOverscan); + pf.set(displayFrames.mOverscan); + // We need to tell the app about where the frame inside the overscan is, so + // it can inset its content by that amount -- it didn't ask to actually + // extend itself into the overscan region. + of.set(displayFrames.mUnrestricted); + } else { + df.set(displayFrames.mRestrictedOverscan); + pf.set(displayFrames.mRestrictedOverscan); + // We need to tell the app about where the frame inside the overscan + // is, so it can inset its content by that amount -- it didn't ask + // to actually extend itself into the overscan region. + of.set(displayFrames.mUnrestricted); + } + + if ((fl & FLAG_FULLSCREEN) == 0) { + if (win.isVoiceInteraction()) { + cf.set(displayFrames.mVoiceContent); + } else { + if (adjust != SOFT_INPUT_ADJUST_RESIZE) { + cf.set(displayFrames.mDock); + } else { + cf.set(displayFrames.mContent); + } + } + } else { + // Full screen windows are always given a layout that is as if the status + // bar and other transient decors are gone. This is to avoid bad states when + // moving from a window that is not hiding the status bar to one that is. + cf.set(displayFrames.mRestricted); + } + applyStableConstraints(sysUiFl, fl, cf, displayFrames); + if (adjust != SOFT_INPUT_ADJUST_NOTHING) { + vf.set(displayFrames.mCurrent); + } else { + vf.set(cf); + } + + // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed + mExperiments.offsetWindowFramesForNavBar(mNavigationBarPosition, win); + // EXPERIMENT END + } + } else if (layoutInScreen || (sysUiFl + & (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) { + if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() + + "): IN_SCREEN"); + // A window that has requested to fill the entire screen just + // gets everything, period. + if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) { + cf.set(displayFrames.mUnrestricted); + of.set(displayFrames.mUnrestricted); + df.set(displayFrames.mUnrestricted); + pf.set(displayFrames.mUnrestricted); + if (hasNavBar) { + pf.left = df.left = of.left = cf.left = displayFrames.mDock.left; + pf.right = df.right = of.right = cf.right = displayFrames.mRestricted.right; + pf.bottom = df.bottom = of.bottom = cf.bottom = + displayFrames.mRestricted.bottom; + } + if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out IN_SCREEN status bar window: " + pf); + } else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) { + // The navigation bar has Real Ultimate Power. + of.set(displayFrames.mUnrestricted); + df.set(displayFrames.mUnrestricted); + pf.set(displayFrames.mUnrestricted); + if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out navigation bar window: " + pf); + } else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT) + && ((fl & FLAG_FULLSCREEN) != 0)) { + // Fullscreen secure system overlays get what they ask for. Screenshot region + // selection overlay should also expand to full screen. + cf.set(displayFrames.mOverscan); + of.set(displayFrames.mOverscan); + df.set(displayFrames.mOverscan); + pf.set(displayFrames.mOverscan); + } else if (type == TYPE_BOOT_PROGRESS) { + // Boot progress screen always covers entire display. + cf.set(displayFrames.mOverscan); + of.set(displayFrames.mOverscan); + df.set(displayFrames.mOverscan); + pf.set(displayFrames.mOverscan); + } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0 + && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) { + // Asking to layout into the overscan region, so give it that pure unrestricted + // area. + cf.set(displayFrames.mOverscan); + of.set(displayFrames.mOverscan); + df.set(displayFrames.mOverscan); + pf.set(displayFrames.mOverscan); + } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 + && (type == TYPE_STATUS_BAR + || type == TYPE_TOAST + || type == TYPE_DOCK_DIVIDER + || type == TYPE_VOICE_INTERACTION_STARTING + || (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW))) { + // Asking for layout as if the nav bar is hidden, lets the + // application extend into the unrestricted screen area. We + // only do this for application windows (or toasts) to ensure no window that + // can be above the nav bar can do this. + // XXX This assumes that an app asking for this will also + // ask for layout in only content. We can't currently figure out + // what the screen would be if only laying out to hide the nav bar. + cf.set(displayFrames.mUnrestricted); + of.set(displayFrames.mUnrestricted); + df.set(displayFrames.mUnrestricted); + pf.set(displayFrames.mUnrestricted); + } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) { + of.set(displayFrames.mRestricted); + df.set(displayFrames.mRestricted); + pf.set(displayFrames.mRestricted); + if (adjust != SOFT_INPUT_ADJUST_RESIZE) { + cf.set(displayFrames.mDock); + } else { + cf.set(displayFrames.mContent); + } + } else { + cf.set(displayFrames.mRestricted); + of.set(displayFrames.mRestricted); + df.set(displayFrames.mRestricted); + pf.set(displayFrames.mRestricted); + } + + applyStableConstraints(sysUiFl, fl, cf, displayFrames); + + if (adjust != SOFT_INPUT_ADJUST_NOTHING) { + vf.set(displayFrames.mCurrent); + } else { + vf.set(cf); + } + } else if (attached != null) { + if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() + + "): attached to " + attached); + // A child window should be placed inside of the same visible + // frame that its parent had. + setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf, + displayFrames); + } else { + if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() + + "): normal window"); + // Otherwise, a normal window must be placed inside the content + // of all screen decorations. + if (type == TYPE_STATUS_BAR_PANEL) { + // Status bar panels can go on + // top of the status bar. They are protected by the STATUS_BAR_SERVICE + // permission, so they have the same privileges as the status bar itself. + cf.set(displayFrames.mRestricted); + of.set(displayFrames.mRestricted); + df.set(displayFrames.mRestricted); + pf.set(displayFrames.mRestricted); + } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) { + // These dialogs are stable to interim decor changes. + cf.set(displayFrames.mStable); + of.set(displayFrames.mStable); + df.set(displayFrames.mStable); + pf.set(displayFrames.mStable); + } else { + pf.set(displayFrames.mContent); + if (win.isVoiceInteraction()) { + cf.set(displayFrames.mVoiceContent); + of.set(displayFrames.mVoiceContent); + df.set(displayFrames.mVoiceContent); + } else if (adjust != SOFT_INPUT_ADJUST_RESIZE) { + cf.set(displayFrames.mDock); + of.set(displayFrames.mDock); + df.set(displayFrames.mDock); + } else { + cf.set(displayFrames.mContent); + of.set(displayFrames.mContent); + df.set(displayFrames.mContent); + } + if (adjust != SOFT_INPUT_ADJUST_NOTHING) { + vf.set(displayFrames.mCurrent); + } else { + vf.set(cf); + } + } + } + } + + final int cutoutMode = attrs.layoutInDisplayCutoutMode; + final boolean attachedInParent = attached != null && !layoutInScreen; + final boolean requestedHideNavigation = + (requestedSysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0; + + // TYPE_BASE_APPLICATION windows are never considered floating here because they don't get + // cropped / shifted to the displayFrame in WindowState. + final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen + && type != TYPE_BASE_APPLICATION; + + // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in + // the cutout safe zone. + if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) { + final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect; + displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe); + if (layoutInScreen && layoutInsetDecor && !requestedFullscreen + && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) { + // At the top we have the status bar, so apps that are + // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN + // already expect that there's an inset there and we don't need to exclude + // the window from that area. + displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE; + } + if (layoutInScreen && layoutInsetDecor && !requestedHideNavigation + && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) { + // Same for the navigation bar. + switch (mNavigationBarPosition) { + case NAV_BAR_BOTTOM: + displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE; + break; + case NAV_BAR_RIGHT: + displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE; + break; + case NAV_BAR_LEFT: + displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE; + break; + } + } + if (type == TYPE_INPUT_METHOD && mNavigationBarPosition == NAV_BAR_BOTTOM) { + // The IME can always extend under the bottom cutout if the navbar is there. + displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE; + } + // Windows that are attached to a parent and laid out in said parent already avoid + // the cutout according to that parent and don't need to be further constrained. + // Floating IN_SCREEN windows get what they ask for and lay out in the full screen. + // They will later be cropped or shifted using the displayFrame in WindowState, + // which prevents overlap with the DisplayCutout. + if (!attachedInParent && !floatingInScreenWindow) { + sTmpRect.set(pf); + pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars); + windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf)); + } + // Make sure that NO_LIMITS windows clipped to the display don't extend under the + // cutout. + df.intersectUnchecked(displayCutoutSafeExceptMaybeBars); + } + + // Content should never appear in the cutout. + cf.intersectUnchecked(displayFrames.mDisplayCutoutSafe); + + // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it. + // Also, we don't allow windows in multi-window mode to extend out of the screen. + if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR + && !win.isInMultiWindowMode()) { + df.left = df.top = -10000; + df.right = df.bottom = 10000; + if (type != TYPE_WALLPAPER) { + of.left = of.top = cf.left = cf.top = vf.left = vf.top = -10000; + of.right = of.bottom = cf.right = cf.bottom = vf.right = vf.bottom = 10000; + } + } + + // If the device has a chin (e.g. some watches), a dead area at the bottom of the screen we + // need to provide information to the clients that want to pretend that you can draw there. + // We only want to apply outsets to certain types of windows. For example, we never want to + // apply the outsets to floating dialogs, because they wouldn't make sense there. + final boolean useOutsets = shouldUseOutsets(attrs, fl); + if (isDefaultDisplay && useOutsets) { + final Rect osf = windowFrames.mOutsetFrame; + osf.set(cf.left, cf.top, cf.right, cf.bottom); + windowFrames.setHasOutsets(true); + int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources()); + if (outset > 0) { + int rotation = displayFrames.mRotation; + if (rotation == Surface.ROTATION_0) { + osf.bottom += outset; + } else if (rotation == Surface.ROTATION_90) { + osf.right += outset; + } else if (rotation == Surface.ROTATION_180) { + osf.top -= outset; + } else if (rotation == Surface.ROTATION_270) { + osf.left -= outset; + } + if (DEBUG_LAYOUT) Slog.v(TAG, "applying bottom outset of " + outset + + " with rotation " + rotation + ", result: " + osf); + } + } + + if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle() + + ": sim=#" + Integer.toHexString(sim) + + " attach=" + attached + " type=" + type + + String.format(" flags=0x%08x", fl) + + " pf=" + pf.toShortString() + " df=" + df.toShortString() + + " of=" + of.toShortString() + + " cf=" + cf.toShortString() + " vf=" + vf.toShortString() + + " dcf=" + dcf.toShortString() + + " sf=" + sf.toShortString() + + " osf=" + windowFrames.mOutsetFrame.toShortString() + " " + win); + + if (!sTmpLastParentFrame.equals(pf)) { + windowFrames.setContentChanged(true); + } + + win.computeFrameLw(); + // Dock windows carve out the bottom of the screen, so normal windows + // can't appear underneath them. + if (type == TYPE_INPUT_METHOD && win.isVisibleLw() + && !win.getGivenInsetsPendingLw()) { + offsetInputMethodWindowLw(win, displayFrames); + } + if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw() + && !win.getGivenInsetsPendingLw()) { + offsetVoiceInputWindowLw(win, displayFrames); + } + } + + private void layoutWallpaper(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect cf) { + // The wallpaper has Real Ultimate Power, but we want to tell it about the overscan area. + df.set(displayFrames.mOverscan); + pf.set(displayFrames.mOverscan); + cf.set(displayFrames.mUnrestricted); + of.set(displayFrames.mUnrestricted); + } + + private void offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) { + int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top); + top += win.getGivenContentInsetsLw().top; + displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top); + displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top); + top = win.getVisibleFrameLw().top; + top += win.getGivenVisibleInsetsLw().top; + displayFrames.mCurrent.bottom = Math.min(displayFrames.mCurrent.bottom, top); + if (DEBUG_LAYOUT) Slog.v(TAG, "Input method: mDockBottom=" + + displayFrames.mDock.bottom + " mContentBottom=" + + displayFrames.mContent.bottom + " mCurBottom=" + displayFrames.mCurrent.bottom); + } + + private void offsetVoiceInputWindowLw(WindowState win, DisplayFrames displayFrames) { + int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top); + top += win.getGivenContentInsetsLw().top; + displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top); + } + + /** + * Called following layout of all windows before each window has policy applied. + */ + public void beginPostLayoutPolicyLw() { + mTopFullscreenOpaqueWindowState = null; + mTopFullscreenOpaqueOrDimmingWindowState = null; + mTopDockedOpaqueWindowState = null; + mTopDockedOpaqueOrDimmingWindowState = null; + mForceStatusBar = false; + mForceStatusBarFromKeyguard = false; + mForceStatusBarTransparent = false; + mForcingShowNavBar = false; + mForcingShowNavBarLayer = -1; + + mAllowLockscreenWhenOn = false; + mShowingDream = false; + mWindowSleepTokenNeeded = false; + } + + /** + * Called following layout of all window to apply policy to each window. + * + * @param win The window being positioned. + * @param attrs The LayoutParams of the window. + * @param attached For sub-windows, the window it is attached to. Otherwise null. + */ + public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs, + WindowState attached, WindowState imeTarget) { + final boolean affectsSystemUi = win.canAffectSystemUiFlags(); + if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi); + mService.mPolicy.applyKeyguardPolicyLw(win, imeTarget); + final int fl = PolicyControl.getWindowFlags(win, attrs); + if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi + && attrs.type == TYPE_INPUT_METHOD) { + mForcingShowNavBar = true; + mForcingShowNavBarLayer = win.getSurfaceLayer(); + } + if (attrs.type == TYPE_STATUS_BAR) { + if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { + mForceStatusBarFromKeyguard = true; + } + if ((attrs.privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) { + mForceStatusBarTransparent = true; + } + } + + boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW + && attrs.type < FIRST_SYSTEM_WINDOW; + final int windowingMode = win.getWindowingMode(); + final boolean inFullScreenOrSplitScreenSecondaryWindowingMode = + windowingMode == WINDOWING_MODE_FULLSCREEN + || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; + if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi) { + if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) { + mForceStatusBar = true; + } + if (attrs.type == TYPE_DREAM) { + // If the lockscreen was showing when the dream started then wait + // for the dream to draw before hiding the lockscreen. + if (!mDreamingLockscreen + || (win.isVisibleLw() && win.hasDrawnLw())) { + mShowingDream = true; + appWindow = true; + } + } + + // For app windows that are not attached, we decide if all windows in the app they + // represent should be hidden or if we should hide the lockscreen. For attached app + // windows we defer the decision to the window it is attached to. + if (appWindow && attached == null) { + if (attrs.isFullscreen() && inFullScreenOrSplitScreenSecondaryWindowingMode) { + if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win); + mTopFullscreenOpaqueWindowState = win; + if (mTopFullscreenOpaqueOrDimmingWindowState == null) { + mTopFullscreenOpaqueOrDimmingWindowState = win; + } + if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) { + mAllowLockscreenWhenOn = true; + } + } + } + } + + // Voice interaction overrides both top fullscreen and top docked. + if (affectsSystemUi && win.getAttrs().type == TYPE_VOICE_INTERACTION) { + if (mTopFullscreenOpaqueWindowState == null) { + mTopFullscreenOpaqueWindowState = win; + if (mTopFullscreenOpaqueOrDimmingWindowState == null) { + mTopFullscreenOpaqueOrDimmingWindowState = win; + } + } + if (mTopDockedOpaqueWindowState == null) { + mTopDockedOpaqueWindowState = win; + if (mTopDockedOpaqueOrDimmingWindowState == null) { + mTopDockedOpaqueOrDimmingWindowState = win; + } + } + } + + // Keep track of the window if it's dimming but not necessarily fullscreen. + if (mTopFullscreenOpaqueOrDimmingWindowState == null && affectsSystemUi + && win.isDimming() && inFullScreenOrSplitScreenSecondaryWindowingMode) { + mTopFullscreenOpaqueOrDimmingWindowState = win; + } + + // We need to keep track of the top "fullscreen" opaque window for the docked stack + // separately, because both the "real fullscreen" opaque window and the one for the docked + // stack can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR. + if (mTopDockedOpaqueWindowState == null && affectsSystemUi && appWindow && attached == null + && attrs.isFullscreen() && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + mTopDockedOpaqueWindowState = win; + if (mTopDockedOpaqueOrDimmingWindowState == null) { + mTopDockedOpaqueOrDimmingWindowState = win; + } + } + + // Also keep track of any windows that are dimming but not necessarily fullscreen in the + // docked stack. + if (mTopDockedOpaqueOrDimmingWindowState == null && affectsSystemUi && win.isDimming() + && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + mTopDockedOpaqueOrDimmingWindowState = win; + } + + // Take note if a window wants to acquire a sleep token. + if ((attrs.privateFlags & PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN) != 0 + && win.canAcquireSleepToken()) { + mWindowSleepTokenNeeded = true; + } + } + + /** + * Called following layout of all windows and after policy has been applied + * to each window. If in this function you do + * something that may have modified the animation state of another window, + * be sure to return non-zero in order to perform another pass through layout. + * + * @return Return any bit set of + * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_LAYOUT}, + * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_CONFIG}, + * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_WALLPAPER}, or + * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_ANIM}. + */ + public int finishPostLayoutPolicyLw() { + int changes = 0; + boolean topIsFullscreen = false; + + // If we are not currently showing a dream then remember the current + // lockscreen state. We will use this to determine whether the dream + // started while the lockscreen was showing and remember this state + // while the dream is showing. + if (!mShowingDream) { + mDreamingLockscreen = mService.mPolicy.isKeyguardShowingAndNotOccluded(); + if (mDreamingSleepTokenNeeded) { + mDreamingSleepTokenNeeded = false; + mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 0, 1).sendToTarget(); + } + } else { + if (!mDreamingSleepTokenNeeded) { + mDreamingSleepTokenNeeded = true; + mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 1, 1).sendToTarget(); + } + } + + if (mStatusBar != null) { + if (DEBUG_LAYOUT) Slog.i(TAG, "force=" + mForceStatusBar + + " forcefkg=" + mForceStatusBarFromKeyguard + + " top=" + mTopFullscreenOpaqueWindowState); + boolean shouldBeTransparent = mForceStatusBarTransparent + && !mForceStatusBar + && !mForceStatusBarFromKeyguard; + if (!shouldBeTransparent) { + mStatusBarController.setShowTransparent(false /* transparent */); + } else if (!mStatusBar.isVisibleLw()) { + mStatusBarController.setShowTransparent(true /* transparent */); + } + + boolean statusBarForcesShowingNavigation = + (mStatusBar.getAttrs().privateFlags + & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0; + boolean topAppHidesStatusBar = topAppHidesStatusBar(); + if (mForceStatusBar || mForceStatusBarFromKeyguard || mForceStatusBarTransparent + || statusBarForcesShowingNavigation) { + if (DEBUG_LAYOUT) Slog.v(TAG, "Showing status bar: forced"); + if (mStatusBarController.setBarShowingLw(true)) { + changes |= FINISH_LAYOUT_REDO_LAYOUT; + } + // Maintain fullscreen layout until incoming animation is complete. + topIsFullscreen = mTopIsFullscreen && mStatusBar.isAnimatingLw(); + // Transient status bar is not allowed if status bar is on lockscreen or status bar + // is expecting the navigation keys from the user. + if ((mForceStatusBarFromKeyguard || statusBarForcesShowingNavigation) + && mStatusBarController.isTransientShowing()) { + mStatusBarController.updateVisibilityLw(false /*transientAllowed*/, + mLastSystemUiFlags, mLastSystemUiFlags); + } + } else if (mTopFullscreenOpaqueWindowState != null) { + topIsFullscreen = topAppHidesStatusBar; + // The subtle difference between the window for mTopFullscreenOpaqueWindowState + // and mTopIsFullscreen is that mTopIsFullscreen is set only if the window + // has the FLAG_FULLSCREEN set. Not sure if there is another way that to be the + // case though. + if (mStatusBarController.isTransientShowing()) { + if (mStatusBarController.setBarShowingLw(true)) { + changes |= FINISH_LAYOUT_REDO_LAYOUT; + } + } else if (topIsFullscreen + && !mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM) + && !mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) { + if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar"); + if (mStatusBarController.setBarShowingLw(false)) { + changes |= FINISH_LAYOUT_REDO_LAYOUT; + } else { + if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar already hiding"); + } + } else { + if (DEBUG_LAYOUT) Slog.v(TAG, "** SHOWING status bar: top is not fullscreen"); + if (mStatusBarController.setBarShowingLw(true)) { + changes |= FINISH_LAYOUT_REDO_LAYOUT; + } + topAppHidesStatusBar = false; + } + } + mStatusBarController.setTopAppHidesStatusBar(topAppHidesStatusBar); + } + + if (mTopIsFullscreen != topIsFullscreen) { + if (!topIsFullscreen) { + // Force another layout when status bar becomes fully shown. + changes |= FINISH_LAYOUT_REDO_LAYOUT; + } + mTopIsFullscreen = topIsFullscreen; + } + + if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) { + // If the navigation bar has been hidden or shown, we need to do another + // layout pass to update that window. + changes |= FINISH_LAYOUT_REDO_LAYOUT; + } + + if (mShowingDream != mLastShowingDream) { + mLastShowingDream = mShowingDream; + mService.notifyShowingDreamChanged(); + } + + updateWindowSleepToken(); + + mService.mPolicy.setAllowLockscreenWhenOn(getDisplayId(), mAllowLockscreenWhenOn); + return changes; + } + + private void updateWindowSleepToken() { + if (mWindowSleepTokenNeeded && !mLastWindowSleepTokenNeeded) { + mHandler.removeCallbacks(mReleaseSleepTokenRunnable); + mHandler.post(mAcquireSleepTokenRunnable); + } else if (!mWindowSleepTokenNeeded && mLastWindowSleepTokenNeeded) { + mHandler.removeCallbacks(mAcquireSleepTokenRunnable); + mHandler.post(mReleaseSleepTokenRunnable); + } + mLastWindowSleepTokenNeeded = mWindowSleepTokenNeeded; + } + + /** + * @return Whether the top app should hide the statusbar based on the top fullscreen opaque + * window. + */ + private boolean topAppHidesStatusBar() { + if (mTopFullscreenOpaqueWindowState == null) { + return false; + } + final int fl = PolicyControl.getWindowFlags(null, + mTopFullscreenOpaqueWindowState.getAttrs()); + if (localLOGV) { + Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()); + Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs() + + " lp.flags=0x" + Integer.toHexString(fl)); + } + return (fl & LayoutParams.FLAG_FULLSCREEN) != 0 + || (mLastSystemUiFlags & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; + } + + /** + * Called when the resource overlays change. + */ + public void onOverlayChangedLw() { + onConfigurationChanged(); + } + + /** + * Called when the configuration has changed, and it's safe to load new values from resources. + */ + public void onConfigurationChanged() { + final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation(); + + final Context uiContext = getSystemUiContext(); + final Resources res = uiContext.getResources(); + final int portraitRotation = displayRotation.getPortraitRotation(); + final int upsideDownRotation = displayRotation.getUpsideDownRotation(); + final int landscapeRotation = displayRotation.getLandscapeRotation(); + final int seascapeRotation = displayRotation.getSeascapeRotation(); + + mStatusBarHeightForRotation[portraitRotation] = + mStatusBarHeightForRotation[upsideDownRotation] = + res.getDimensionPixelSize(R.dimen.status_bar_height_portrait); + mStatusBarHeightForRotation[landscapeRotation] = + mStatusBarHeightForRotation[seascapeRotation] = + res.getDimensionPixelSize(R.dimen.status_bar_height_landscape); + + // Height of the navigation bar when presented horizontally at bottom + mNavigationBarHeightForRotationDefault[portraitRotation] = + mNavigationBarHeightForRotationDefault[upsideDownRotation] = + res.getDimensionPixelSize(R.dimen.navigation_bar_height); + mNavigationBarHeightForRotationDefault[landscapeRotation] = + mNavigationBarHeightForRotationDefault[seascapeRotation] = + res.getDimensionPixelSize(R.dimen.navigation_bar_height_landscape); + + // Width of the navigation bar when presented vertically along one side + mNavigationBarWidthForRotationDefault[portraitRotation] = + mNavigationBarWidthForRotationDefault[upsideDownRotation] = + mNavigationBarWidthForRotationDefault[landscapeRotation] = + mNavigationBarWidthForRotationDefault[seascapeRotation] = + res.getDimensionPixelSize(R.dimen.navigation_bar_width); + + if (ALTERNATE_CAR_MODE_NAV_SIZE) { + // Height of the navigation bar when presented horizontally at bottom + mNavigationBarHeightForRotationInCarMode[portraitRotation] = + mNavigationBarHeightForRotationInCarMode[upsideDownRotation] = + res.getDimensionPixelSize(R.dimen.navigation_bar_height_car_mode); + mNavigationBarHeightForRotationInCarMode[landscapeRotation] = + mNavigationBarHeightForRotationInCarMode[seascapeRotation] = + res.getDimensionPixelSize(R.dimen.navigation_bar_height_landscape_car_mode); + + // Width of the navigation bar when presented vertically along one side + mNavigationBarWidthForRotationInCarMode[portraitRotation] = + mNavigationBarWidthForRotationInCarMode[upsideDownRotation] = + mNavigationBarWidthForRotationInCarMode[landscapeRotation] = + mNavigationBarWidthForRotationInCarMode[seascapeRotation] = + res.getDimensionPixelSize(R.dimen.navigation_bar_width_car_mode); + } + + // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed + mExperiments.onConfigurationChanged(uiContext); + // EXPERIMENT END + } + + @VisibleForTesting + Context getSystemUiContext() { + final Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext(); + return mDisplayContent.isDefaultDisplay + ? uiContext : uiContext.createDisplayContext(mDisplayContent.getDisplay()); + } + + private int getNavigationBarWidth(int rotation, int uiMode) { + if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { + return mNavigationBarWidthForRotationInCarMode[rotation]; + } else { + return mNavigationBarWidthForRotationDefault[rotation]; + } + } + + /** + * Return the display width available after excluding any screen + * decorations that could never be removed in Honeycomb. That is, system bar or + * button bar. + */ + public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode, + DisplayCutout displayCutout) { + int width = fullWidth; + if (hasNavigationBar()) { + // For a basic navigation bar, when we are in landscape mode we place + // the navigation bar to the side. + if (navigationBarCanMove() && fullWidth > fullHeight) { + width -= getNavigationBarWidth(rotation, uiMode); + } + } + if (displayCutout != null) { + width -= displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight(); + } + return width; + } + + private int getNavigationBarHeight(int rotation, int uiMode) { + if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { + return mNavigationBarHeightForRotationInCarMode[rotation]; + } else { + return mNavigationBarHeightForRotationDefault[rotation]; + } + } + + /** + * Return the display height available after excluding any screen + * decorations that could never be removed in Honeycomb. That is, system bar or + * button bar. + */ + public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode, + DisplayCutout displayCutout) { + int height = fullHeight; + if (hasNavigationBar()) { + // For a basic navigation bar, when we are in portrait mode we place + // the navigation bar to the bottom. + if (!navigationBarCanMove() || fullWidth < fullHeight) { + height -= getNavigationBarHeight(rotation, uiMode); + } + } + if (displayCutout != null) { + height -= displayCutout.getSafeInsetTop() + displayCutout.getSafeInsetBottom(); + } + return height; + } + + /** + * Return the available screen width that we should report for the + * configuration. This must be no larger than + * {@link #getNonDecorDisplayWidth(int, int, int, int, DisplayCutout)}; it may be smaller + * than that to account for more transient decoration like a status bar. + */ + public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode, + DisplayCutout displayCutout) { + return getNonDecorDisplayWidth(fullWidth, fullHeight, rotation, uiMode, displayCutout); + } + + /** + * Return the available screen height that we should report for the + * configuration. This must be no larger than + * {@link #getNonDecorDisplayHeight(int, int, int, int, DisplayCutout)}; it may be smaller + * than that to account for more transient decoration like a status bar. + */ + public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode, + DisplayCutout displayCutout) { + // There is a separate status bar at the top of the display. We don't count that as part + // of the fixed decor, since it can hide; however, for purposes of configurations, + // we do want to exclude it since applications can't generally use that part + // of the screen. + int statusBarHeight = mStatusBarHeightForRotation[rotation]; + if (displayCutout != null) { + // If there is a cutout, it may already have accounted for some part of the status + // bar height. + statusBarHeight = Math.max(0, statusBarHeight - displayCutout.getSafeInsetTop()); + } + return getNonDecorDisplayHeight(fullWidth, fullHeight, rotation, uiMode, displayCutout) + - statusBarHeight; + } + + boolean isShowingDreamLw() { + return mShowingDream; + } + + /** + * Calculates the stable insets without running a layout. + * + * @param displayRotation the current display rotation + * @param displayWidth the current display width + * @param displayHeight the current display height + * @param displayCutout the current display cutout + * @param outInsets the insets to return + */ + public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight, + DisplayCutout displayCutout, Rect outInsets) { + outInsets.setEmpty(); + + // Navigation bar and status bar. + getNonDecorInsetsLw(displayRotation, displayWidth, displayHeight, displayCutout, outInsets); + outInsets.top = Math.max(outInsets.top, mStatusBarHeightForRotation[displayRotation]); + } + + /** + * Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system + * bar or button bar. See {@link #getNonDecorDisplayWidth}. + * + * @param displayRotation the current display rotation + * @param displayWidth the current display width + * @param displayHeight the current display height + * @param displayCutout the current display cutout + * @param outInsets the insets to return + */ + public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight, + DisplayCutout displayCutout, Rect outInsets) { + outInsets.setEmpty(); + + // Only navigation bar + if (hasNavigationBar()) { + final int uiMode = mService.mPolicy.getUiMode(); + int position = navigationBarPosition(displayWidth, displayHeight, displayRotation); + if (position == NAV_BAR_BOTTOM) { + outInsets.bottom = getNavigationBarHeight(displayRotation, uiMode); + } else if (position == NAV_BAR_RIGHT) { + outInsets.right = getNavigationBarWidth(displayRotation, uiMode); + } else if (position == NAV_BAR_LEFT) { + outInsets.left = getNavigationBarWidth(displayRotation, uiMode); + } + } + + if (displayCutout != null) { + outInsets.left += displayCutout.getSafeInsetLeft(); + outInsets.top += displayCutout.getSafeInsetTop(); + outInsets.right += displayCutout.getSafeInsetRight(); + outInsets.bottom += displayCutout.getSafeInsetBottom(); + } + } + + @NavigationBarPosition + int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) { + if (navigationBarCanMove() && displayWidth > displayHeight) { + if (displayRotation == Surface.ROTATION_270) { + return NAV_BAR_LEFT; + } else if (displayRotation == Surface.ROTATION_90) { + return NAV_BAR_RIGHT; + } + } + return NAV_BAR_BOTTOM; + } + + /** + * @return The side of the screen where navigation bar is positioned. + * @see WindowManagerPolicyConstants#NAV_BAR_LEFT + * @see WindowManagerPolicyConstants#NAV_BAR_RIGHT + * @see WindowManagerPolicyConstants#NAV_BAR_BOTTOM + */ + @NavigationBarPosition + public int getNavBarPosition() { + return mNavigationBarPosition; + } + + /** + * A new window has been focused. + */ + public int focusChangedLw(WindowState lastFocus, WindowState newFocus) { + mFocusedWindow = newFocus; + mLastFocusedWindow = lastFocus; + if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) { + // If the navigation bar has been hidden or shown, we need to do another + // layout pass to update that window. + return FINISH_LAYOUT_REDO_LAYOUT; + } + return 0; + } + + /** + * Return true if it is okay to perform animations for an app transition + * that is about to occur. You may return false for this if, for example, + * the dream window is currently displayed so the switch should happen + * immediately. + */ + public boolean allowAppAnimationsLw() { + return !mShowingDream; + } + + private void updateDreamingSleepToken(boolean acquire) { + if (acquire) { + final int displayId = getDisplayId(); + if (mDreamingSleepToken == null) { + mDreamingSleepToken = mService.mAtmInternal.acquireSleepToken( + "DreamOnDisplay" + displayId, displayId); + } + } else { + if (mDreamingSleepToken != null) { + mDreamingSleepToken.release(); + mDreamingSleepToken = null; + } + } + } + + private void requestTransientBars(WindowState swipeTarget) { + synchronized (mLock) { + if (!mService.mPolicy.isUserSetupComplete()) { + // Swipe-up for navigation bar is disabled during setup + return; + } + boolean sb = mStatusBarController.checkShowTransientBarLw(); + boolean nb = mNavigationBarController.checkShowTransientBarLw() + && !isNavBarEmpty(mLastSystemUiFlags); + if (sb || nb) { + // Don't show status bar when swiping on already visible navigation bar + if (!nb && swipeTarget == mNavigationBar) { + if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target"); + return; + } + if (sb) mStatusBarController.showTransient(); + if (nb) mNavigationBarController.showTransient(); + mImmersiveModeConfirmation.confirmCurrentPrompt(); + updateSystemUiVisibilityLw(); + } + } + } + + private void disposeInputConsumer(InputConsumer inputConsumer) { + if (inputConsumer != null) { + inputConsumer.dismiss(); + } + } + + private boolean isStatusBarKeyguard() { + return mStatusBar != null + && (mStatusBar.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0; + } + + private boolean isKeyguardOccluded() { + // TODO (b/113840485): Handle per display keyguard. + return mService.mPolicy.isKeyguardOccluded(); + } + + void resetSystemUiVisibilityLw() { + mLastSystemUiFlags = 0; + updateSystemUiVisibilityLw(); + } + + private int updateSystemUiVisibilityLw() { + // If there is no window focused, there will be nobody to handle the events + // anyway, so just hang on in whatever state we're in until things settle down. + WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow + : mTopFullscreenOpaqueWindowState; + if (winCandidate == null) { + return 0; + } + + // The immersive mode confirmation should never affect the system bar visibility, otherwise + // it will unhide the navigation bar and hide itself. + if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) { + + // The immersive mode confirmation took the focus from mLastFocusedWindow which was + // controlling the system ui visibility. So if mLastFocusedWindow can still receive + // keys, we let it keep controlling the visibility. + final boolean lastFocusCanReceiveKeys = + (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys()); + winCandidate = isStatusBarKeyguard() ? mStatusBar + : lastFocusCanReceiveKeys ? mLastFocusedWindow + : mTopFullscreenOpaqueWindowState; + if (winCandidate == null) { + return 0; + } + } + final WindowState win = winCandidate; + if ((win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 && isKeyguardOccluded()) { + // We are updating at a point where the keyguard has gotten + // focus, but we were last in a state where the top window is + // hiding it. This is probably because the keyguard as been + // shown while the top window was displayed, so we want to ignore + // it here because this is just a very transient change and it + // will quickly lose focus once it correctly gets hidden. + return 0; + } + + int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null) + & ~mResettingSystemUiFlags + & ~mForceClearedSystemUiFlags; + if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) { + tmpVisibility + &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS); + } + + final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */, + mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState); + final int dockedVisibility = updateLightStatusBarLw(0 /* vis */, + mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState); + mService.getStackBounds( + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds); + mService.getStackBounds( + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds); + final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility); + final int diff = visibility ^ mLastSystemUiFlags; + final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags; + final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags; + final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState); + if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu + && mFocusedApp == win.getAppToken() + && mLastNonDockedStackBounds.equals(mNonDockedStackBounds) + && mLastDockedStackBounds.equals(mDockedStackBounds)) { + return 0; + } + mLastSystemUiFlags = visibility; + mLastFullscreenStackSysUiFlags = fullscreenVisibility; + mLastDockedStackSysUiFlags = dockedVisibility; + mLastFocusNeedsMenu = needsMenu; + mFocusedApp = win.getAppToken(); + final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds); + final Rect dockedStackBounds = new Rect(mDockedStackBounds); + mHandler.post(() -> { + StatusBarManagerInternal statusBar = getStatusBarManagerInternal(); + if (statusBar != null) { + final int displayId = getDisplayId(); + statusBar.setSystemUiVisibility(displayId, visibility, fullscreenVisibility, + dockedVisibility, 0xffffffff, fullscreenStackBounds, + dockedStackBounds, win.toString()); + statusBar.topAppWindowChanged(displayId, needsMenu); + } + }); + return diff; + } + + private int updateLightStatusBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming) { + final boolean onKeyguard = isStatusBarKeyguard() && !isKeyguardOccluded(); + final WindowState statusColorWin = onKeyguard ? mStatusBar : opaqueOrDimming; + if (statusColorWin != null && (statusColorWin == opaque || onKeyguard)) { + // If the top fullscreen-or-dimming window is also the top fullscreen, respect + // its light flag. + vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + vis |= PolicyControl.getSystemUiVisibility(statusColorWin, null) + & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + } else if (statusColorWin != null && statusColorWin.isDimming()) { + // Otherwise if it's dimming, clear the light flag. + vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + } + return vis; + } + + @VisibleForTesting + @Nullable + static WindowState chooseNavigationColorWindowLw(WindowState opaque, + WindowState opaqueOrDimming, WindowState imeWindow, + @NavigationBarPosition int navBarPosition) { + // If the IME window is visible and FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is set, then IME + // window can be navigation color window. + final boolean imeWindowCanNavColorWindow = imeWindow != null + && imeWindow.isVisibleLw() + && navBarPosition == NAV_BAR_BOTTOM + && (PolicyControl.getWindowFlags(imeWindow, null) + & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; + + if (opaque != null && opaqueOrDimming == opaque) { + // If the top fullscreen-or-dimming window is also the top fullscreen, respect it + // unless IME window is also eligible, since currently the IME window is always show + // above the opaque fullscreen app window, regardless of the IME target window. + // TODO(b/31559891): Maybe we need to revisit this condition once b/31559891 is fixed. + return imeWindowCanNavColorWindow ? imeWindow : opaque; + } + + if (opaqueOrDimming == null || !opaqueOrDimming.isDimming()) { + // No dimming window is involved. Determine the result only with the IME window. + return imeWindowCanNavColorWindow ? imeWindow : null; + } + + if (!imeWindowCanNavColorWindow) { + // No IME window is involved. Determine the result only with opaqueOrDimming. + return opaqueOrDimming; + } + + // The IME window and the dimming window are competing. Check if the dimming window can be + // IME target or not. + if (LayoutParams.mayUseInputMethod(PolicyControl.getWindowFlags(opaqueOrDimming, null))) { + // The IME window is above the dimming window. + return imeWindow; + } else { + // The dimming window is above the IME window. + return opaqueOrDimming; + } + } + + @VisibleForTesting + static int updateLightNavigationBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming, + WindowState imeWindow, WindowState navColorWin) { + + if (navColorWin != null) { + if (navColorWin == imeWindow || navColorWin == opaque) { + // Respect the light flag. + vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + vis |= PolicyControl.getSystemUiVisibility(navColorWin, null) + & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) { + // Clear the light flag for dimming window. + vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + } + } + return vis; + } + + private int updateSystemBarsLw(WindowState win, int oldVis, int vis) { + final boolean dockedStackVisible = + mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + final boolean freeformStackVisible = + mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM); + final boolean resizing = mDisplayContent.getDockedDividerController().isResizing(); + + // We need to force system bars when the docked stack is visible, when the freeform stack + // is visible but also when we are resizing for the transitions when docked stack + // visibility changes. + mForceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing; + final boolean forceOpaqueStatusBar = mForceShowSystemBars && !mForceStatusBarFromKeyguard; + + // apply translucent bar vis flags + WindowState fullscreenTransWin = isStatusBarKeyguard() && !isKeyguardOccluded() + ? mStatusBar + : mTopFullscreenOpaqueWindowState; + vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis); + vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis); + final int dockedVis = mStatusBarController.applyTranslucentFlagLw( + mTopDockedOpaqueWindowState, 0, 0); + + final boolean fullscreenDrawsStatusBarBackground = + drawsStatusBarBackground(vis, mTopFullscreenOpaqueWindowState); + final boolean dockedDrawsStatusBarBackground = + drawsStatusBarBackground(dockedVis, mTopDockedOpaqueWindowState); + + // prevent status bar interaction from clearing certain flags + int type = win.getAttrs().type; + boolean statusBarHasFocus = type == TYPE_STATUS_BAR; + if (statusBarHasFocus && !isStatusBarKeyguard()) { + int flags = View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_IMMERSIVE + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + if (isKeyguardOccluded()) { + flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT; + } + vis = (vis & ~flags) | (oldVis & flags); + } + + if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) { + vis |= View.STATUS_BAR_TRANSPARENT; + vis &= ~View.STATUS_BAR_TRANSLUCENT; + } else if ((!areTranslucentBarsAllowed() && fullscreenTransWin != mStatusBar) + || forceOpaqueStatusBar) { + vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT); + } + + vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing); + + // update status bar + boolean immersiveSticky = + (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; + final boolean hideStatusBarWM = + mTopFullscreenOpaqueWindowState != null + && (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null) + & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0; + final boolean hideStatusBarSysui = + (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; + final boolean hideNavBarSysui = + (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0; + + final boolean transientStatusBarAllowed = mStatusBar != null + && (statusBarHasFocus || (!mForceShowSystemBars + && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky)))); + + final boolean transientNavBarAllowed = mNavigationBar != null + && !mForceShowSystemBars && hideNavBarSysui && immersiveSticky; + + final long now = SystemClock.uptimeMillis(); + final boolean pendingPanic = mPendingPanicGestureUptime != 0 + && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION; + final DisplayPolicy defaultDisplayPolicy = + mService.getDefaultDisplayContentLocked().getDisplayPolicy(); + if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard() + // TODO (b/111955725): Show keyguard presentation on all external displays + && defaultDisplayPolicy.isKeyguardDrawComplete()) { + // The user performed the panic gesture recently, we're about to hide the bars, + // we're no longer on the Keyguard and the screen is ready. We can now request the bars. + mPendingPanicGestureUptime = 0; + mStatusBarController.showTransient(); + if (!isNavBarEmpty(vis)) { + mNavigationBarController.showTransient(); + } + } + + final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested() + && !transientStatusBarAllowed && hideStatusBarSysui; + final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested() + && !transientNavBarAllowed; + if (denyTransientStatus || denyTransientNav || mForceShowSystemBars) { + // clear the clearable flags instead + clearClearableFlagsLw(); + vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS; + } + + final boolean immersive = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0; + immersiveSticky = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; + final boolean navAllowedHidden = immersive || immersiveSticky; + + if (hideNavBarSysui && !navAllowedHidden + && mService.mPolicy.getWindowLayerLw(win) + > mService.mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_CONSUMER)) { + // We can't hide the navbar from this window otherwise the input consumer would not get + // the input events. + vis = (vis & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + } + + vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis); + + // update navigation bar + boolean oldImmersiveMode = isImmersiveMode(oldVis); + boolean newImmersiveMode = isImmersiveMode(vis); + if (oldImmersiveMode != newImmersiveMode) { + final String pkg = win.getOwningPackage(); + mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode, + mService.mPolicy.isUserSetupComplete(), + isNavBarEmpty(win.getSystemUiVisibility())); + } + + vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis); + + final WindowState navColorWin = chooseNavigationColorWindowLw( + mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState, + mDisplayContent.mInputMethodWindow, mNavigationBarPosition); + vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState, + mTopFullscreenOpaqueOrDimmingWindowState, + mDisplayContent.mInputMethodWindow, navColorWin); + + return vis; + } + + private boolean drawsStatusBarBackground(int vis, WindowState win) { + if (!mStatusBarController.isTransparentAllowed(win)) { + return false; + } + if (win == null) { + return true; + } + + final boolean drawsSystemBars = + (win.getAttrs().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; + final boolean forceDrawsSystemBars = + (win.getAttrs().privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0; + + return forceDrawsSystemBars || drawsSystemBars && (vis & View.STATUS_BAR_TRANSLUCENT) == 0; + } + + /** + * @return the current visibility flags with the nav-bar opacity related flags toggled based + * on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}. + */ + private int configureNavBarOpacity(int visibility, boolean dockedStackVisible, + boolean freeformStackVisible, boolean isDockedDividerResizing) { + if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) { + if (dockedStackVisible || freeformStackVisible || isDockedDividerResizing) { + visibility = setNavBarOpaqueFlag(visibility); + } + } else if (mNavBarOpacityMode == NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE) { + if (isDockedDividerResizing) { + visibility = setNavBarOpaqueFlag(visibility); + } else if (freeformStackVisible) { + visibility = setNavBarTranslucentFlag(visibility); + } else { + visibility = setNavBarOpaqueFlag(visibility); + } + } + + if (!areTranslucentBarsAllowed()) { + visibility &= ~View.NAVIGATION_BAR_TRANSLUCENT; + } + return visibility; + } + + private int setNavBarOpaqueFlag(int visibility) { + return visibility & ~(View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT); + } + + private int setNavBarTranslucentFlag(int visibility) { + visibility &= ~View.NAVIGATION_BAR_TRANSPARENT; + return visibility | View.NAVIGATION_BAR_TRANSLUCENT; + } + + private void clearClearableFlagsLw() { + int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_CLEARABLE_FLAGS; + if (newVal != mResettingSystemUiFlags) { + mResettingSystemUiFlags = newVal; + mDisplayContent.reevaluateStatusBarVisibility(); + } + } + + private boolean isImmersiveMode(int vis) { + final int flags = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + return mNavigationBar != null + && (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0 + && (vis & flags) != 0 + && canHideNavigationBar(); + } + + /** + * @return whether the navigation bar can be hidden, e.g. the device has a navigation bar + */ + private boolean canHideNavigationBar() { + return hasNavigationBar(); + } + + private static boolean isNavBarEmpty(int systemUiFlags) { + final int disableNavigationBar = (View.STATUS_BAR_DISABLE_HOME + | View.STATUS_BAR_DISABLE_BACK + | View.STATUS_BAR_DISABLE_RECENT); + + return (systemUiFlags & disableNavigationBar) == disableNavigationBar; + } + + /** + * @return whether the navigation or status bar can be made translucent + * + * This should return true unless touch exploration is not enabled or + * R.boolean.config_enableTranslucentDecor is false. + */ + private boolean areTranslucentBarsAllowed() { + return mTranslucentDecorEnabled; + } + + boolean shouldRotateSeamlessly(DisplayRotation displayRotation, int oldRotation, + int newRotation) { + // For the upside down rotation we don't rotate seamlessly as the navigation + // bar moves position. + // Note most apps (using orientation:sensor or user as opposed to fullSensor) + // will not enter the reverse portrait orientation, so actually the + // orientation won't change at all. + if (oldRotation == displayRotation.getUpsideDownRotation() + || newRotation == displayRotation.getUpsideDownRotation()) { + return false; + } + // If the navigation bar can't change sides, then it will + // jump when we change orientations and we don't rotate + // seamlessly. + if (!navigationBarCanMove()) { + return false; + } + + final WindowState w = mTopFullscreenOpaqueWindowState; + if (w != mFocusedWindow) { + return false; + } + + // We only enable seamless rotation if the top window has requested + // it and is in the fullscreen opaque state. Seamless rotation + // requires freezing various Surface states and won't work well + // with animations, so we disable it in the animation case for now. + if (w != null && !w.isAnimatingLw() + && w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS) { + return true; + } + return false; + } + + private final Runnable mHiddenNavPanic = new Runnable() { + @Override + public void run() { + synchronized (mLock) { + if (!mService.mPolicy.isUserSetupComplete()) { + // Swipe-up for navigation bar is disabled during setup + return; + } + mPendingPanicGestureUptime = SystemClock.uptimeMillis(); + if (!isNavBarEmpty(mLastSystemUiFlags)) { + mNavigationBarController.showTransient(); + } + } + } + }; + + void onPowerKeyDown(boolean isScreenOn) { + // Detect user pressing the power button in panic when an application has + // taken over the whole screen. + boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(isScreenOn, + SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags), + isNavBarEmpty(mLastSystemUiFlags)); + if (panic) { + mHandler.post(mHiddenNavPanic); + } + } + + void onVrStateChangedLw(boolean enabled) { + mImmersiveModeConfirmation.onVrStateChangedLw(enabled); + } + + /** + * Called when the state of lock task mode changes. This should be used to disable immersive + * mode confirmation. + * + * @param lockTaskState the new lock task mode state. One of + * {@link ActivityManager#LOCK_TASK_MODE_NONE}, + * {@link ActivityManager#LOCK_TASK_MODE_LOCKED}, + * {@link ActivityManager#LOCK_TASK_MODE_PINNED}. + */ + public void onLockTaskStateChangedLw(int lockTaskState) { + mImmersiveModeConfirmation.onLockTaskModeChangedLw(lockTaskState); + } + + /** + * Request a screenshot be taken. + * + * @param screenshotType The type of screenshot, for example either + * {@link WindowManager#TAKE_SCREENSHOT_FULLSCREEN} or + * {@link WindowManager#TAKE_SCREENSHOT_SELECTED_REGION} + */ + public void takeScreenshot(int screenshotType) { + if (mScreenshotHelper != null) { + mScreenshotHelper.takeScreenshot(screenshotType, + mStatusBar != null && mStatusBar.isVisibleLw(), + mNavigationBar != null && mNavigationBar.isVisibleLw(), mHandler); + } + } + void dump(String prefix, PrintWriter pw) { - pw.println(prefix + "DisplayPolicy"); - pw.print(prefix + " mCarDockEnablesAccelerometer=" + mCarDockEnablesAccelerometer); - pw.println(" mDeskDockEnablesAccelerometer=" + mDeskDockEnablesAccelerometer); - pw.print(prefix + " mDockMode=" + Intent.dockStateToString(mDockMode)); - pw.println(" mLidState=" + WindowManagerFuncs.lidStateToString(mLidState)); - pw.print(prefix + " mAwake=" + mAwake); - pw.print(" mScreenOnEarly=" + mScreenOnEarly); - pw.println(" mScreenOnFully=" + mScreenOnFully); - pw.print(prefix + " mKeyguardDrawComplete=" + mKeyguardDrawComplete); - pw.println(" mWindowManagerDrawComplete=" + mWindowManagerDrawComplete); - pw.println(prefix + " mHdmiPlugged=" + mHdmiPlugged); + pw.print(prefix); pw.print("DisplayPolicy"); + prefix += " "; + pw.print(prefix); + pw.print("mCarDockEnablesAccelerometer="); pw.print(mCarDockEnablesAccelerometer); + pw.print(" mDeskDockEnablesAccelerometer="); + pw.println(mDeskDockEnablesAccelerometer); + pw.print(prefix); pw.print("mDockMode="); pw.print(Intent.dockStateToString(mDockMode)); + pw.print(" mLidState="); pw.println(WindowManagerFuncs.lidStateToString(mLidState)); + pw.print(prefix); pw.print("mAwake="); pw.print(mAwake); + pw.print(" mScreenOnEarly="); pw.print(mScreenOnEarly); + pw.print(" mScreenOnFully="); pw.println(mScreenOnFully); + pw.print(prefix); pw.print("mKeyguardDrawComplete="); pw.print(mKeyguardDrawComplete); + pw.print(" mWindowManagerDrawComplete="); pw.println(mWindowManagerDrawComplete); + pw.print(prefix); pw.print("mHdmiPlugged="); pw.println(mHdmiPlugged); + if (mLastSystemUiFlags != 0 || mResettingSystemUiFlags != 0 + || mForceClearedSystemUiFlags != 0) { + pw.print(prefix); pw.print("mLastSystemUiFlags=0x"); + pw.print(Integer.toHexString(mLastSystemUiFlags)); + pw.print(" mResettingSystemUiFlags=0x"); + pw.print(Integer.toHexString(mResettingSystemUiFlags)); + pw.print(" mForceClearedSystemUiFlags=0x"); + pw.println(Integer.toHexString(mForceClearedSystemUiFlags)); + } + if (mLastFocusNeedsMenu) { + pw.print(prefix); pw.print("mLastFocusNeedsMenu="); pw.println(mLastFocusNeedsMenu); + } + pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream); + pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen); + pw.print(" mDreamingSleepToken="); pw.println(mDreamingSleepToken); + if (mStatusBar != null) { + pw.print(prefix); pw.print("mStatusBar="); pw.print(mStatusBar); + pw.print(" isStatusBarKeyguard="); pw.println(isStatusBarKeyguard()); + } + if (mNavigationBar != null) { + pw.print(prefix); pw.print("mNavigationBar="); pw.println(mNavigationBar); + } + if (mFocusedWindow != null) { + pw.print(prefix); pw.print("mFocusedWindow="); pw.println(mFocusedWindow); + } + if (mFocusedApp != null) { + pw.print(prefix); pw.print("mFocusedApp="); pw.println(mFocusedApp); + } + if (mTopFullscreenOpaqueWindowState != null) { + pw.print(prefix); pw.print("mTopFullscreenOpaqueWindowState="); + pw.println(mTopFullscreenOpaqueWindowState); + } + if (mTopFullscreenOpaqueOrDimmingWindowState != null) { + pw.print(prefix); pw.print("mTopFullscreenOpaqueOrDimmingWindowState="); + pw.println(mTopFullscreenOpaqueOrDimmingWindowState); + } + if (mForcingShowNavBar) { + pw.print(prefix); pw.print("mForcingShowNavBar="); pw.println(mForcingShowNavBar); + pw.print(prefix); pw.print("mForcingShowNavBarLayer="); + pw.println(mForcingShowNavBarLayer); + } + pw.print(prefix); pw.print("mTopIsFullscreen="); pw.print(mTopIsFullscreen); + pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar); + pw.print(" mForceStatusBarFromKeyguard="); pw.println(mForceStatusBarFromKeyguard); + pw.print(prefix); pw.print("mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn); + mStatusBarController.dump(pw, prefix); + mNavigationBarController.dump(pw, prefix); + + pw.print(prefix); pw.println("Looper state:"); + mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + " "); } } diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 6ab7090d1aae..f1d1e49c1004 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -844,9 +844,9 @@ public class DisplayRotation { pw.print(prefix + " mPortraitRotation=" + Surface.rotationToString(mPortraitRotation)); pw.println(" mUpsideDownRotation=" + Surface.rotationToString(mUpsideDownRotation)); - pw.print(prefix + " mSupportAutoRotation=" + mSupportAutoRotation); + pw.println(prefix + " mSupportAutoRotation=" + mSupportAutoRotation); if (mOrientationListener != null) { - pw.print(" mOrientationSensorEnabled=" + mOrientationListener.mEnabled); + mOrientationListener.dump(pw, prefix + " "); } pw.println(); diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index c37707291aed..7ea88bbdaec8 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -28,6 +28,8 @@ 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; @@ -52,6 +54,7 @@ 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; @@ -184,8 +187,8 @@ public class DockedStackDividerController { .calculateNonDismissingSnapTarget(position).position; DockedDividerUtils.calculateBoundsForPosition(snappedPosition, dockSide, mTmpRect, mTmpRect2.width(), mTmpRect2.height(), getContentWidth()); - mService.mPolicy.getStableInsetsLw(rotation, mTmpRect2.width(), mTmpRect2.height(), - displayCutout, mTmpRect3); + mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, mTmpRect2.width(), + mTmpRect2.height(), displayCutout, mTmpRect3); mService.intersectDisplayInsetBounds(mTmpRect2, mTmpRect3, mTmpRect); minWidth = Math.min(mTmpRect.width(), minWidth); } @@ -231,8 +234,9 @@ public class DockedStackDividerController { final DisplayCutout displayCutout = mDisplayContent.getDisplayInfo().displayCutout; final int displayWidth = parentConfig.windowConfiguration.getBounds().width(); final int displayHeight = parentConfig.windowConfiguration.getBounds().height(); - mService.mPolicy.getStableInsetsLw(parentConfig.windowConfiguration.getRotation(), - displayWidth, displayHeight, displayCutout, mTmpRect); + 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. @@ -240,7 +244,7 @@ public class DockedStackDividerController { outBounds.set(0, mTaskHeightInMinimizedMode + dividerSize + mTmpRect.top, displayWidth, displayHeight); } else { - // In landscape also inset the left/right side with the statusbar height to match the + // 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; @@ -278,16 +282,16 @@ public class DockedStackDividerController { : mDisplayContent.mBaseDisplayHeight; final DisplayCutout displayCutout = mDisplayContent.calculateDisplayCutoutForRotation(rotation).getDisplayCutout(); - mService.mPolicy.getStableInsetsLw(rotation, dw, dh, displayCutout, mTmpRect); + final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); + displayPolicy.getStableInsetsLw(rotation, dw, dh, displayCutout, mTmpRect); config.unset(); config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; - final int displayId = mDisplayContent.getDisplayId(); - final int appWidth = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, rotation, - baseConfig.uiMode, displayId, displayCutout); - final int appHeight = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, rotation, - baseConfig.uiMode, displayId, displayCutout); - mService.mPolicy.getNonDecorInsetsLw(rotation, dw, dh, displayCutout, mTmpRect); + 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; @@ -295,10 +299,10 @@ public class DockedStackDividerController { leftInset + appWidth /*right*/, topInset + appHeight /*bottom*/); final float density = mDisplayContent.getDisplayMetrics().density; - config.screenWidthDp = (int) (mService.mPolicy.getConfigDisplayWidth(dw, dh, - rotation, baseConfig.uiMode, displayId, displayCutout) / density); - config.screenHeightDp = (int) (mService.mPolicy.getConfigDisplayHeight(dw, dh, - rotation, baseConfig.uiMode, displayId, displayCutout) / 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(), @@ -464,8 +468,32 @@ public class DockedStackDividerController { * @return true if the side provided is valid */ boolean canPrimaryStackDockTo(int dockSide, Rect parentRect, int rotation) { - return mService.mPolicy.isDockSideAllowed(dockSide, mOriginalDockedSide, - parentRect.width(), parentRect.height(), 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) { diff --git a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java index 4aa24465215b..3d20501222b6 100644 --- a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java +++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * 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. @@ -14,10 +14,11 @@ * limitations under the License. */ -package com.android.server.policy; +package com.android.server.wm; import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; +import static android.view.Display.DEFAULT_DISPLAY; import android.animation.ArgbEvaluator; import android.animation.ValueAnimator; @@ -32,16 +33,14 @@ import android.graphics.drawable.ColorDrawable; import android.os.Binder; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.Message; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; -import android.service.vr.IVrManager; -import android.service.vr.IVrStateCallbacks; import android.util.DisplayMetrics; import android.util.Slog; +import android.view.Display; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; @@ -55,7 +54,6 @@ import android.widget.Button; import android.widget.FrameLayout; import com.android.internal.R; -import com.android.server.vr.VrManagerService; /** * Helper to manage showing/hiding a confirmation prompt when the navigation bar is hidden @@ -67,30 +65,34 @@ public class ImmersiveModeConfirmation { private static final boolean DEBUG_SHOW_EVERY_TIME = false; // super annoying, use with caution private static final String CONFIRMED = "confirmed"; + private static boolean sConfirmed; + private final Context mContext; private final H mHandler; private final long mShowDelayMs; private final long mPanicThresholdMs; private final IBinder mWindowToken = new Binder(); - private boolean mConfirmed; private ClingWindowView mClingWindow; private long mPanicTime; private WindowManager mWindowManager; - private int mCurrentUserId; // Local copy of vr mode enabled state, to avoid calling into VrManager with // the lock held. - boolean mVrModeEnabled = false; + private boolean mVrModeEnabled; private int mLockTaskState = LOCK_TASK_MODE_NONE; - public ImmersiveModeConfirmation(Context context) { - mContext = ActivityThread.currentActivityThread().getSystemUiContext(); - mHandler = new H(); + ImmersiveModeConfirmation(Context context, Looper looper, boolean vrModeEnabled) { + final Display display = context.getDisplay(); + final Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext(); + mContext = display.getDisplayId() == DEFAULT_DISPLAY + ? uiContext : uiContext.createDisplayContext(display); + mHandler = new H(looper); mShowDelayMs = getNavBarExitDuration() * 3; mPanicThresholdMs = context.getResources() .getInteger(R.integer.config_immersive_mode_confirmation_panic); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + mVrModeEnabled = vrModeEnabled; } private long getNavBarExitDuration() { @@ -98,57 +100,46 @@ public class ImmersiveModeConfirmation { return exit != null ? exit.getDuration() : 0; } - public void loadSetting(int currentUserId) { - mConfirmed = false; - mCurrentUserId = currentUserId; - if (DEBUG) Slog.d(TAG, String.format("loadSetting() mCurrentUserId=%d", mCurrentUserId)); + static boolean loadSetting(int currentUserId, Context context) { + final boolean wasConfirmed = sConfirmed; + sConfirmed = false; + if (DEBUG) Slog.d(TAG, String.format("loadSetting() currentUserId=%d", currentUserId)); String value = null; try { - value = Settings.Secure.getStringForUser(mContext.getContentResolver(), + value = Settings.Secure.getStringForUser(context.getContentResolver(), Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS, UserHandle.USER_CURRENT); - mConfirmed = CONFIRMED.equals(value); - if (DEBUG) Slog.d(TAG, "Loaded mConfirmed=" + mConfirmed); + sConfirmed = CONFIRMED.equals(value); + if (DEBUG) Slog.d(TAG, "Loaded sConfirmed=" + sConfirmed); } catch (Throwable t) { Slog.w(TAG, "Error loading confirmations, value=" + value, t); } + return sConfirmed != wasConfirmed; } - private void saveSetting() { + private static void saveSetting(Context context) { if (DEBUG) Slog.d(TAG, "saveSetting()"); try { - final String value = mConfirmed ? CONFIRMED : null; - Settings.Secure.putStringForUser(mContext.getContentResolver(), + final String value = sConfirmed ? CONFIRMED : null; + Settings.Secure.putStringForUser(context.getContentResolver(), Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS, value, UserHandle.USER_CURRENT); if (DEBUG) Slog.d(TAG, "Saved value=" + value); } catch (Throwable t) { - Slog.w(TAG, "Error saving confirmations, mConfirmed=" + mConfirmed, t); - } - } - - void systemReady() { - IVrManager vrManager = IVrManager.Stub.asInterface( - ServiceManager.getService(Context.VR_SERVICE)); - if (vrManager != null) { - try { - vrManager.registerListener(mVrStateCallbacks); - mVrModeEnabled = vrManager.getVrModeState(); - } catch (RemoteException re) { - } + Slog.w(TAG, "Error saving confirmations, sConfirmed=" + sConfirmed, t); } } - public void immersiveModeChangedLw(String pkg, boolean isImmersiveMode, + void immersiveModeChangedLw(String pkg, boolean isImmersiveMode, boolean userSetupComplete, boolean navBarEmpty) { mHandler.removeMessages(H.SHOW); if (isImmersiveMode) { final boolean disabled = PolicyControl.disableImmersiveConfirmation(pkg); - if (DEBUG) Slog.d(TAG, String.format("immersiveModeChanged() disabled=%s mConfirmed=%s", - disabled, mConfirmed)); + if (DEBUG) Slog.d(TAG, String.format("immersiveModeChanged() disabled=%s sConfirmed=%s", + disabled, sConfirmed)); if (!disabled - && (DEBUG_SHOW_EVERY_TIME || !mConfirmed) + && (DEBUG_SHOW_EVERY_TIME || !sConfirmed) && userSetupComplete && !mVrModeEnabled && !navBarEmpty @@ -161,7 +152,7 @@ public class ImmersiveModeConfirmation { } } - public boolean onPowerKeyDown(boolean isScreenOn, long time, boolean inImmersiveMode, + boolean onPowerKeyDown(boolean isScreenOn, long time, boolean inImmersiveMode, boolean navBarEmpty) { if (!isScreenOn && (time - mPanicTime < mPanicThresholdMs)) { // turning the screen back on within the panic threshold @@ -176,7 +167,7 @@ public class ImmersiveModeConfirmation { return false; } - public void confirmCurrentPrompt() { + void confirmCurrentPrompt() { if (mClingWindow != null) { if (DEBUG) Slog.d(TAG, "confirmCurrentPrompt()"); mHandler.post(mConfirm); @@ -191,16 +182,14 @@ public class ImmersiveModeConfirmation { } } - public WindowManager.LayoutParams getClingWindowLayoutParams() { + private WindowManager.LayoutParams getClingWindowLayoutParams() { final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, - 0 - | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - , + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT); lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; lp.setTitle("ImmersiveModeConfirmation"); @@ -209,7 +198,7 @@ public class ImmersiveModeConfirmation { return lp; } - public FrameLayout.LayoutParams getBubbleLayoutParams() { + private FrameLayout.LayoutParams getBubbleLayoutParams() { return new FrameLayout.LayoutParams( mContext.getResources().getDimensionPixelSize( R.dimen.immersive_mode_cling_width), @@ -220,7 +209,7 @@ public class ImmersiveModeConfirmation { /** * @return the window token that's used by all ImmersiveModeConfirmation windows. */ - public IBinder getWindowToken() { + IBinder getWindowToken() { return mWindowToken; } @@ -272,7 +261,7 @@ public class ImmersiveModeConfirmation { } }; - public ClingWindowView(Context context, Runnable confirm) { + ClingWindowView(Context context, Runnable confirm) { super(context); mConfirm = confirm; setBackground(mColor); @@ -295,7 +284,7 @@ public class ImmersiveModeConfirmation { mClingLayout = (ViewGroup) View.inflate(getContext(), R.layout.immersive_mode_cling, null); - final Button ok = (Button) mClingLayout.findViewById(R.id.ok); + final Button ok = mClingLayout.findViewById(R.id.ok); ok.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { @@ -359,8 +348,7 @@ public class ImmersiveModeConfirmation { // we will be hiding the nav bar, so layout as if it's already hidden mClingWindow.setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); // show the confirmation WindowManager.LayoutParams lp = getClingWindowLayoutParams(); @@ -371,9 +359,9 @@ public class ImmersiveModeConfirmation { @Override public void run() { if (DEBUG) Slog.d(TAG, "mConfirm.run()"); - if (!mConfirmed) { - mConfirmed = true; - saveSetting(); + if (!sConfirmed) { + sConfirmed = true; + saveSetting(mContext); } handleHide(); } @@ -383,6 +371,10 @@ public class ImmersiveModeConfirmation { private static final int SHOW = 1; private static final int HIDE = 2; + H(Looper looper) { + super(looper); + } + @Override public void handleMessage(Message msg) { switch(msg.what) { @@ -396,16 +388,13 @@ public class ImmersiveModeConfirmation { } } - private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { - @Override - public void onVrStateChanged(boolean enabled) throws RemoteException { - mVrModeEnabled = enabled; - if (mVrModeEnabled) { - mHandler.removeMessages(H.SHOW); - mHandler.sendEmptyMessage(H.HIDE); - } + void onVrStateChangedLw(boolean enabled) { + mVrModeEnabled = enabled; + if (mVrModeEnabled) { + mHandler.removeMessages(H.SHOW); + mHandler.sendEmptyMessage(H.HIDE); } - }; + } void onLockTaskModeChangedLw(int lockTaskState) { mLockTaskState = lockTaskState; diff --git a/services/core/java/com/android/server/policy/NavigationBarExperiments.java b/services/core/java/com/android/server/wm/NavigationBarExperiments.java index 06772e3bff5a..b74fb4579cf0 100644 --- a/services/core/java/com/android/server/policy/NavigationBarExperiments.java +++ b/services/core/java/com/android/server/wm/NavigationBarExperiments.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.policy; +package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; @@ -25,9 +25,6 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; import android.content.Context; import android.graphics.Rect; -import com.android.server.policy.WindowManagerPolicy.WindowState; -import com.android.server.wm.WindowFrames; - /** * This class acts as a proxy for Navigation Bar experiments enabled with custom overlays * {@see OverlayManagerService}. By default with no overlays, this class will essentially do nothing diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java index d21f67d908d5..ba2325877075 100644 --- a/services/core/java/com/android/server/wm/PinnedStackController.java +++ b/services/core/java/com/android/server/wm/PinnedStackController.java @@ -514,8 +514,9 @@ class PinnedStackController { */ private void getInsetBounds(Rect outRect) { synchronized (mService.mGlobalLock) { - mService.mPolicy.getStableInsetsLw(mDisplayInfo.rotation, mDisplayInfo.logicalWidth, - mDisplayInfo.logicalHeight, mDisplayInfo.displayCutout, mTmpInsets); + mDisplayContent.getDisplayPolicy().getStableInsetsLw(mDisplayInfo.rotation, + mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight, + mDisplayInfo.displayCutout, mTmpInsets); outRect.set(mTmpInsets.left + mScreenEdgeInsets.x, mTmpInsets.top + mScreenEdgeInsets.y, mDisplayInfo.logicalWidth - mTmpInsets.right - mScreenEdgeInsets.x, mDisplayInfo.logicalHeight - mTmpInsets.bottom - mScreenEdgeInsets.y); diff --git a/services/core/java/com/android/server/policy/PolicyControl.java b/services/core/java/com/android/server/wm/PolicyControl.java index 48e72bc5036d..4c8ce9ebb72c 100644 --- a/services/core/java/com/android/server/policy/PolicyControl.java +++ b/services/core/java/com/android/server/wm/PolicyControl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.policy; +package com.android.server.wm; import android.app.ActivityManager; import android.content.Context; @@ -26,8 +26,6 @@ import android.view.View; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; -import com.android.server.policy.WindowManagerPolicy.WindowState; - import java.io.PrintWriter; import java.io.StringWriter; @@ -49,9 +47,9 @@ import java.io.StringWriter; * Separate multiple name-value pairs with ':' * e.g. "immersive.status=apps:immersive.preconfirms=*" */ -public class PolicyControl { - private static String TAG = "PolicyControl"; - private static boolean DEBUG = false; +class PolicyControl { + private static final String TAG = "PolicyControl"; + private static final boolean DEBUG = false; private static final String NAME_IMMERSIVE_FULL = "immersive.full"; private static final String NAME_IMMERSIVE_STATUS = "immersive.status"; @@ -63,7 +61,7 @@ public class PolicyControl { private static Filter sImmersiveStatusFilter; private static Filter sImmersiveNavigationFilter; - public static int getSystemUiVisibility(WindowState win, LayoutParams attrs) { + static int getSystemUiVisibility(WindowState win, LayoutParams attrs) { attrs = attrs != null ? attrs : win.getAttrs(); int vis = win != null ? win.getSystemUiVisibility() : (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility); @@ -84,7 +82,7 @@ public class PolicyControl { return vis; } - public static int getWindowFlags(WindowState win, LayoutParams attrs) { + static int getWindowFlags(WindowState win, LayoutParams attrs) { attrs = attrs != null ? attrs : win.getAttrs(); int flags = attrs.flags; if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) { @@ -98,7 +96,7 @@ public class PolicyControl { return flags; } - public static int adjustClearableFlags(WindowState win, int clearableFlags) { + static int adjustClearableFlags(WindowState win, int clearableFlags) { final LayoutParams attrs = win != null ? win.getAttrs() : null; if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) { clearableFlags &= ~View.SYSTEM_UI_FLAG_FULLSCREEN; @@ -106,28 +104,32 @@ public class PolicyControl { return clearableFlags; } - public static boolean disableImmersiveConfirmation(String pkg) { + static boolean disableImmersiveConfirmation(String pkg) { return (sImmersivePreconfirmationsFilter != null && sImmersivePreconfirmationsFilter.matches(pkg)) || ActivityManager.isRunningInTestHarness(); } - public static void reloadFromSetting(Context context) { + static boolean reloadFromSetting(Context context) { if (DEBUG) Slog.d(TAG, "reloadFromSetting()"); String value = null; try { value = Settings.Global.getStringForUser(context.getContentResolver(), Settings.Global.POLICY_CONTROL, UserHandle.USER_CURRENT); - if (sSettingValue != null && sSettingValue.equals(value)) return; + if (sSettingValue == value || sSettingValue != null && sSettingValue.equals(value)) { + return false; + } setFilters(value); sSettingValue = value; } catch (Throwable t) { Slog.w(TAG, "Error loading policy control, value=" + value, t); + return false; } + return true; } - public static void dump(String prefix, PrintWriter pw) { + static void dump(String prefix, PrintWriter pw) { dump("sImmersiveStatusFilter", sImmersiveStatusFilter, prefix, pw); dump("sImmersiveNavigationFilter", sImmersiveNavigationFilter, prefix, pw); dump("sImmersivePreconfirmationsFilter", sImmersivePreconfirmationsFilter, prefix, pw); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 72e3562ee4fa..b483fd3bc642 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -120,13 +120,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { // events. int mTopFocusedDisplayId = INVALID_DISPLAY; - // Only a seperate transaction until we seperate the apply surface changes + // Only a separate transaction until we separate the apply surface changes // transaction from the global transaction. private final SurfaceControl.Transaction mDisplayTransaction = new SurfaceControl.Transaction(); - private final Consumer<DisplayContent> mDisplayContentConfigChangesConsumer = - mService.mPolicy::onConfigurationChanged; - private final Consumer<WindowState> mCloseSystemDialogsConsumer = w -> { if (w.mHasSurface) { try { @@ -362,8 +359,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { public void onConfigurationChanged(Configuration newParentConfig) { prepareFreezingTaskBounds(); super.onConfigurationChanged(newParentConfig); - - forAllDisplays(mDisplayContentConfigChangesConsumer); } private void prepareFreezingTaskBounds() { @@ -1052,6 +1047,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { } } + void forAllDisplayPolicies(Consumer<DisplayPolicy> callback) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + callback.accept(mChildren.get(i).getDisplayPolicy()); + } + } + /** * Get current topmost focused IME window in system. * Will look on all displays in current Z-order. diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java index baeedbc1d28e..35264a22bce5 100644 --- a/services/core/java/com/android/server/wm/StackWindowController.java +++ b/services/core/java/com/android/server/wm/StackWindowController.java @@ -261,11 +261,12 @@ public class StackWindowController final DisplayContent displayContent = stack.getDisplayContent(); final DisplayInfo di = displayContent.getDisplayInfo(); final DisplayCutout displayCutout = di.displayCutout; + final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); // Get the insets and display bounds - mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, + displayPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, displayCutout, mTmpStableInsets); - mService.mPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, + displayPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, displayCutout, mTmpNonDecorInsets); mTmpDisplayBounds.set(0, 0, di.logicalWidth, di.logicalHeight); diff --git a/services/core/java/com/android/server/wm/StatusBarController.java b/services/core/java/com/android/server/wm/StatusBarController.java new file mode 100644 index 000000000000..b4de75b287fa --- /dev/null +++ b/services/core/java/com/android/server/wm/StatusBarController.java @@ -0,0 +1,113 @@ +/* + * 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.LayoutParams.FLAG_TRANSLUCENT_STATUS; +import static android.view.WindowManager.LayoutParams.MATCH_PARENT; + +import static com.android.server.wm.WindowManagerInternal.AppTransitionListener; + +import android.app.StatusBarManager; +import android.os.IBinder; +import android.view.View; + +import com.android.server.statusbar.StatusBarManagerInternal; + +/** + * Implements status bar specific behavior. + */ +public class StatusBarController extends BarController { + + private final AppTransitionListener mAppTransitionListener = new AppTransitionListener() { + + private Runnable mAppTransitionPending = () -> { + StatusBarManagerInternal statusBar = getStatusBarInternal(); + if (statusBar != null && mWin != null) { + statusBar.appTransitionPending(mWin.getDisplayId()); + } + }; + + private Runnable mAppTransitionCancelled = () -> { + StatusBarManagerInternal statusBar = getStatusBarInternal(); + if (statusBar != null && mWin != null) { + statusBar.appTransitionCancelled(mWin.getDisplayId()); + } + }; + + private Runnable mAppTransitionFinished = () -> { + StatusBarManagerInternal statusBar = getStatusBarInternal(); + if (statusBar != null && mWin != null) { + statusBar.appTransitionFinished(mWin.getDisplayId()); + } + }; + + @Override + public void onAppTransitionPendingLocked() { + mHandler.post(mAppTransitionPending); + } + + @Override + public int onAppTransitionStartingLocked(int transit, IBinder openToken, + IBinder closeToken, long duration, long statusBarAnimationStartTime, + long statusBarAnimationDuration) { + mHandler.post(() -> { + StatusBarManagerInternal statusBar = getStatusBarInternal(); + if (statusBar != null && mWin != null) { + statusBar.appTransitionStarting(mWin.getDisplayId(), + statusBarAnimationStartTime, statusBarAnimationDuration); + } + }); + return 0; + } + + @Override + public void onAppTransitionCancelledLocked(int transit) { + mHandler.post(mAppTransitionCancelled); + } + + @Override + public void onAppTransitionFinishedLocked(IBinder token) { + mHandler.post(mAppTransitionFinished); + } + }; + + StatusBarController() { + super("StatusBar", + View.STATUS_BAR_TRANSIENT, + View.STATUS_BAR_UNHIDE, + View.STATUS_BAR_TRANSLUCENT, + StatusBarManager.WINDOW_STATUS_BAR, + FLAG_TRANSLUCENT_STATUS, + View.STATUS_BAR_TRANSPARENT); + } + + void setTopAppHidesStatusBar(boolean hidesStatusBar) { + StatusBarManagerInternal statusBar = getStatusBarInternal(); + if (statusBar != null) { + statusBar.setTopAppHidesStatusBar(hidesStatusBar); + } + } + + @Override + protected boolean skipAnimation() { + return mWin.getAttrs().height == MATCH_PARENT; + } + + AppTransitionListener getAppTransitionListener() { + return mAppTransitionListener; + } +} diff --git a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java index d3cc8effb8ad..bdb76c27c668 100644 --- a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java +++ b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * 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. @@ -14,11 +14,10 @@ * limitations under the License. */ -package com.android.server.policy; +package com.android.server.wm; import android.content.Context; import android.os.Handler; -import android.os.Looper; import android.os.SystemClock; import android.util.Slog; import android.view.GestureDetector; @@ -27,11 +26,11 @@ import android.view.MotionEvent; import android.view.WindowManagerPolicyConstants.PointerEventListener; import android.widget.OverScroller; -/* +/** * Listens for system-wide input gestures, firing callbacks when detected. * @hide */ -public class SystemGesturesPointerEventListener implements PointerEventListener { +class SystemGesturesPointerEventListener implements PointerEventListener { private static final String TAG = "SystemGestures"; private static final boolean DEBUG = false; private static final long SWIPE_TIMEOUT_MS = 500; @@ -46,6 +45,7 @@ public class SystemGesturesPointerEventListener implements PointerEventListener private static final int SWIPE_FROM_LEFT = 4; private final Context mContext; + private final Handler mHandler; private final int mSwipeStartThreshold; private final int mSwipeDistanceThreshold; private final Callbacks mCallbacks; @@ -55,7 +55,6 @@ public class SystemGesturesPointerEventListener implements PointerEventListener private final long[] mDownTime = new long[MAX_TRACKED_POINTERS]; private GestureDetector mGestureDetector; - private OverScroller mOverscroller; int screenHeight; int screenWidth; @@ -65,8 +64,9 @@ public class SystemGesturesPointerEventListener implements PointerEventListener private boolean mMouseHoveringAtEdge; private long mLastFlingTime; - public SystemGesturesPointerEventListener(Context context, Callbacks callbacks) { + SystemGesturesPointerEventListener(Context context, Handler handler, Callbacks callbacks) { mContext = context; + mHandler = handler; mCallbacks = checkNull("callbacks", callbacks); mSwipeStartThreshold = checkNull("context", context).getResources() .getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); @@ -83,9 +83,7 @@ public class SystemGesturesPointerEventListener implements PointerEventListener } public void systemReady() { - Handler h = new Handler(Looper.myLooper()); - mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), h); - mOverscroller = new OverScroller(mContext); + mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), mHandler); } @Override @@ -163,14 +161,14 @@ public class SystemGesturesPointerEventListener implements PointerEventListener private void captureDown(MotionEvent event, int pointerIndex) { final int pointerId = event.getPointerId(pointerIndex); final int i = findIndex(pointerId); - if (DEBUG) Slog.d(TAG, "pointer " + pointerId + - " down pointerIndex=" + pointerIndex + " trackingIndex=" + i); + if (DEBUG) Slog.d(TAG, "pointer " + pointerId + + " down pointerIndex=" + pointerIndex + " trackingIndex=" + i); if (i != UNTRACKED_POINTER) { mDownX[i] = event.getX(pointerIndex); mDownY[i] = event.getY(pointerIndex); mDownTime[i] = event.getEventTime(); - if (DEBUG) Slog.d(TAG, "pointer " + pointerId + - " down x=" + mDownX[i] + " y=" + mDownY[i]); + if (DEBUG) Slog.d(TAG, "pointer " + pointerId + + " down x=" + mDownX[i] + " y=" + mDownY[i]); } } @@ -242,6 +240,13 @@ public class SystemGesturesPointerEventListener implements PointerEventListener } private final class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener { + + private OverScroller mOverscroller; + + FlingGestureDetector() { + mOverscroller = new OverScroller(mContext); + } + @Override public boolean onSingleTapUp(MotionEvent e) { if (!mOverscroller.isFinished()) { diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 15de1ecec970..64f4ba5e24ab 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -521,7 +521,7 @@ public class TaskStack extends WindowContainer<Task> implements // Snap the position to a target. final int rotation = parentConfig.windowConfiguration.getRotation(); final int orientation = parentConfig.orientation; - mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight, + mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, displayWidth, displayHeight, displayCutout, outBounds); final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm( mService.mContext.getResources(), displayWidth, displayHeight, @@ -867,7 +867,8 @@ public class TaskStack extends WindowContainer<Task> implements // 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; - mService.mPolicy.getStableInsetsLw(parentConfig.windowConfiguration.getRotation(), + mDisplayContent.getDisplayPolicy().getStableInsetsLw( + parentConfig.windowConfiguration.getRotation(), displayRect.width(), displayRect.height(), displayCutout, mTmpRect2); final int position = new DividerSnapAlgorithm(mService.mContext.getResources(), displayRect.width(), diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index b096bf2d0738..e83b8634925e 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -392,11 +392,6 @@ public abstract class WindowManagerInternal { public abstract boolean isStackVisible(int windowingMode); /** - * @return True if and only if the docked divider is currently in resize mode. - */ - public abstract boolean isDockedDividerResizing(); - - /** * Requests the window manager to resend the windows for accessibility. */ public abstract void computeWindowsForAccessibility(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 8d5cd780e657..39a8465a31b3 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -171,6 +171,8 @@ import android.os.Trace; import android.os.UserHandle; import android.os.WorkSource; import android.provider.Settings; +import android.service.vr.IVrManager; +import android.service.vr.IVrStateCallbacks; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -231,6 +233,7 @@ import com.android.internal.policy.IShortcutService; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.LatencyTracker; +import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.view.WindowManagerPolicyThread; import com.android.server.AnimationThread; import com.android.server.DisplayThread; @@ -373,6 +376,18 @@ public class WindowManagerService extends IWindowManager.Stub boolean mKeyguardOrAodShowingOnDefaultDisplay; // VR Vr2d Display Id. int mVr2dDisplayId = INVALID_DISPLAY; + boolean mVrModeEnabled = false; + + private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { + @Override + public void onVrStateChanged(boolean enabled) { + synchronized (mGlobalLock) { + mVrModeEnabled = enabled; + mRoot.forAllDisplayPolicies(PooledLambda.obtainConsumer( + DisplayPolicy::onVrStateChangedLw, PooledLambda.__(), enabled)); + } + } + }; private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -528,6 +543,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean mForceDisplayEnabled = false; boolean mShowingBootMessages = false; boolean mBootAnimationStopped = false; + boolean mSystemReady = false; // Following variables are for debugging screen wakelock only. WindowState mLastWakeLockHoldingWindow = null; @@ -579,9 +595,6 @@ public class WindowManagerService extends IWindowManager.Stub final WallpaperVisibilityListeners mWallpaperVisibilityListeners = new WallpaperVisibilityListeners(); - int mSystemDecorLayer = 0; - final Rect mScreenRect = new Rect(); - boolean mDisplayFrozen = false; long mDisplayFreezeTime = 0; int mLastDisplayFreezeDuration = 0; @@ -597,11 +610,6 @@ public class WindowManagerService extends IWindowManager.Stub boolean mClientFreezingScreen = false; int mAppsFreezingScreen = 0; - // Last systemUiVisibility we received from status bar. - int mLastStatusBarVisibility = 0; - // Last systemUiVisibility we dispatched to windows. - int mLastDispatchedSystemUiVisibility = 0; - // State while inside of layoutAndPlaceSurfacesLocked(). boolean mFocusMayChange; @@ -649,6 +657,10 @@ public class WindowManagerService extends IWindowManager.Stub Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE); private final Uri mAnimationDurationScaleUri = Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE); + private final Uri mImmersiveModeConfirmationsUri = + Settings.Secure.getUriFor(Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS); + private final Uri mPolicyControlUri = + Settings.Global.getUriFor(Settings.Global.POLICY_CONTROL); public SettingsObserver() { super(new Handler()); @@ -661,6 +673,10 @@ public class WindowManagerService extends IWindowManager.Stub UserHandle.USER_ALL); resolver.registerContentObserver(mAnimationDurationScaleUri, false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(mImmersiveModeConfirmationsUri, false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(mPolicyControlUri, false, this, + UserHandle.USER_ALL); } @Override @@ -669,23 +685,40 @@ public class WindowManagerService extends IWindowManager.Stub return; } + if (mImmersiveModeConfirmationsUri.equals(uri) || mPolicyControlUri.equals(uri)) { + updateSystemUiSettings(); + return; + } + if (mDisplayInversionEnabledUri.equals(uri)) { updateCircularDisplayMaskIfNeeded(); + return; + } + + @UpdateAnimationScaleMode + final int mode; + if (mWindowAnimationScaleUri.equals(uri)) { + mode = WINDOW_ANIMATION_SCALE; + } else if (mTransitionAnimationScaleUri.equals(uri)) { + mode = TRANSITION_ANIMATION_SCALE; + } else if (mAnimationDurationScaleUri.equals(uri)) { + mode = ANIMATION_DURATION_SCALE; } else { - @UpdateAnimationScaleMode - final int mode; - if (mWindowAnimationScaleUri.equals(uri)) { - mode = WINDOW_ANIMATION_SCALE; - } else if (mTransitionAnimationScaleUri.equals(uri)) { - mode = TRANSITION_ANIMATION_SCALE; - } else if (mAnimationDurationScaleUri.equals(uri)) { - mode = ANIMATION_DURATION_SCALE; - } else { - // Ignoring unrecognized content changes - return; - } - Message m = mH.obtainMessage(H.UPDATE_ANIMATION_SCALE, mode, 0); - mH.sendMessage(m); + // Ignoring unrecognized content changes + return; + } + Message m = mH.obtainMessage(H.UPDATE_ANIMATION_SCALE, mode, 0); + mH.sendMessage(m); + } + + void updateSystemUiSettings() { + boolean changed; + synchronized (mWindowMap) { + changed = ImmersiveModeConfirmation.loadSetting(mCurrentUserId, mContext) + || PolicyControl.reloadFromSetting(mContext); + } + if (changed) { + updateRotation(false /* alwaysSendConfiguration */, false /* forceRelayout */); } } } @@ -1286,10 +1319,11 @@ public class WindowManagerService extends IWindowManager.Stub final boolean hasStatusBarServicePermission = mContext.checkCallingOrSelfPermission(permission.STATUS_BAR_SERVICE) == PackageManager.PERMISSION_GRANTED; - mPolicy.adjustWindowParamsLw(win, win.mAttrs, hasStatusBarServicePermission); + final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); + displayPolicy.adjustWindowParamsLw(win, win.mAttrs, hasStatusBarServicePermission); win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs)); - res = mPolicy.prepareAddWindowLw(win, attrs); + res = displayPolicy.prepareAddWindowLw(win, attrs); if (res != WindowManagerGlobal.ADD_OKAY) { return res; } @@ -1422,9 +1456,8 @@ public class WindowManagerService extends IWindowManager.Stub taskBounds = null; floatingStack = false; } - if (mPolicy.getLayoutHintLw(win.mAttrs, taskBounds, displayFrames, floatingStack, - outFrame, outContentInsets, outStableInsets, outOutsets, - outDisplayCutout)) { + if (displayPolicy.getLayoutHintLw(win.mAttrs, taskBounds, displayFrames, floatingStack, + outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) { res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR; } @@ -1842,6 +1875,8 @@ public class WindowManagerService extends IWindowManager.Stub return 0; } displayId = win.getDisplayId(); + final DisplayContent displayContent = win.getDisplayContent(); + final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); WindowStateAnimator winAnimator = win.mWinAnimator; if (viewVisibility != View.GONE) { @@ -1857,7 +1892,7 @@ public class WindowManagerService extends IWindowManager.Stub int attrChanges = 0; int flagChanges = 0; if (attrs != null) { - mPolicy.adjustWindowParamsLw(win, attrs, hasStatusBarServicePermission); + displayPolicy.adjustWindowParamsLw(win, attrs, hasStatusBarServicePermission); // if they don't have the permission, mask out the status bar bits if (seq == win.mSeq) { int systemUiVisibility = attrs.systemUiVisibility @@ -1996,7 +2031,7 @@ public class WindowManagerService extends IWindowManager.Stub try { result = createSurfaceControl(outSurface, result, win, winAnimator); } catch (Exception e) { - win.getDisplayContent().getInputMonitor().updateInputWindowsLw(true /*force*/); + displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/); Slog.w(TAG_WM, "Exception thrown when creating surface for client " + client + " (" + win.mAttrs.getTitle() + ")", @@ -2007,7 +2042,6 @@ public class WindowManagerService extends IWindowManager.Stub if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { focusMayChange = true; } - final DisplayContent displayContent = win.getDisplayContent(); if (win.mAttrs.type == TYPE_INPUT_METHOD && displayContent.mInputMethodWindow == null) { displayContent.setInputMethodWindowLocked(win); @@ -2052,35 +2086,34 @@ public class WindowManagerService extends IWindowManager.Stub // updateFocusedWindowLocked() already assigned layers so we only need to // reassign them at this point if the IM window state gets shuffled boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0; - final DisplayContent dc = win.getDisplayContent(); if (imMayMove) { - dc.computeImeTarget(true /* updateImeTarget */); + displayContent.computeImeTarget(true /* updateImeTarget */); if (toBeDisplayed) { // Little hack here -- we -should- be able to rely on the function to return // true if the IME has moved and needs its layer recomputed. However, if the IME // was hidden and isn't actually moved in the list, its layer may be out of data // so we make sure to recompute it. - dc.assignWindowLayers(false /* setLayoutNeeded */); + displayContent.assignWindowLayers(false /* setLayoutNeeded */); } } if (wallpaperMayMove) { - win.getDisplayContent().pendingLayoutChanges |= + displayContent.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; } if (win.mAppToken != null) { - dc.mUnknownAppVisibilityController.notifyRelayouted(win.mAppToken); + displayContent.mUnknownAppVisibilityController.notifyRelayouted(win.mAppToken); } Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: updateOrientationFromAppTokens"); - configChanged = dc.updateOrientationFromAppTokens(); + configChanged = displayContent.updateOrientationFromAppTokens(); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); if (toBeDisplayed && win.mIsWallpaper) { - DisplayInfo displayInfo = win.getDisplayContent().getDisplayInfo(); - dc.mWallpaperController.updateWallpaperOffset( + DisplayInfo displayInfo = displayContent.getDisplayInfo(); + displayContent.mWallpaperController.updateWallpaperOffset( win, displayInfo.logicalWidth, displayInfo.logicalHeight, false); } if (win.mAppToken != null) { @@ -2090,7 +2123,7 @@ public class WindowManagerService extends IWindowManager.Stub winAnimator.mReportSurfaceResized = false; result |= WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED; } - if (mPolicy.isNavBarForcedShownLw(win)) { + if (displayPolicy.isNavBarForcedShownLw(win)) { result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR; } if (!win.isGoneForLayoutLw()) { @@ -2408,7 +2441,7 @@ public class WindowManagerService extends IWindowManager.Stub mWaitingForConfig = true; displayContent.setLayoutNeeded(); int anim[] = new int[2]; - mPolicy.selectRotationAnimationLw(anim); + displayContent.getDisplayPolicy().selectRotationAnimationLw(anim); startFreezingDisplayLocked(anim[0], anim[1], displayContent); config = new Configuration(mTempConfiguration); @@ -2603,7 +2636,9 @@ public class WindowManagerService extends IWindowManager.Stub } } - @Override + /** + * Notifies window manager that {@link DisplayPolicy#isShowingDreamLw} has changed. + */ public void notifyShowingDreamChanged() { // TODO(multi-display): support show dream in multi-display. notifyKeyguardFlagsChanged(null /* callback */, DEFAULT_DISPLAY); @@ -2634,6 +2669,21 @@ public class WindowManagerService extends IWindowManager.Stub mH.sendEmptyMessage(H.RECOMPUTE_FOCUS); } + @Override + public void onPowerKeyDown(boolean isScreenOn) { + mRoot.forAllDisplayPolicies(PooledLambda.obtainConsumer( + DisplayPolicy::onPowerKeyDown, PooledLambda.__(), isScreenOn)); + } + + @Override + public void onUserSwitched() { + mSettingsObserver.updateSystemUiSettings(); + synchronized (mWindowMap) { + // force a re-application of focused window sysui visibility on each display. + mRoot.forAllDisplayPolicies(DisplayPolicy::resetSystemUiVisibilityLw); + } + } + /** * Starts deferring layout passes. Useful when doing multiple changes but to optimize * performance, only one layout pass should be done. This can be called multiple times, and @@ -2839,7 +2889,8 @@ public class WindowManagerService extends IWindowManager.Stub public boolean isShowingDream() { synchronized (mGlobalLock) { - return mPolicy.isShowingDreamLw(); + // TODO: fix this when dream can be shown on non-default display. + return getDefaultDisplayContentLocked().getDisplayPolicy().isShowingDreamLw(); } } @@ -3672,13 +3723,6 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public WindowManagerPolicy.DisplayContentInfo getDefaultDisplayContentInfo() { - synchronized (mGlobalLock) { - return getDefaultDisplayContentLocked(); - } - } - - @Override public int getDefaultDisplayRotation() { synchronized (mGlobalLock) { return getDefaultDisplayContentLocked().getRotation(); @@ -4291,10 +4335,29 @@ public class WindowManagerService extends IWindowManager.Stub } public void systemReady() { + mSystemReady = true; mPolicy.systemReady(); + mRoot.forAllDisplayPolicies(DisplayPolicy::systemReady); mTaskSnapshotController.systemReady(); mHasWideColorGamutSupport = queryWideColorGamutSupport(); mHasHdrSupport = queryHdrSupport(); + UiThread.getHandler().post(mSettingsObserver::updateSystemUiSettings); + IVrManager vrManager = IVrManager.Stub.asInterface( + ServiceManager.getService(Context.VR_SERVICE)); + if (vrManager != null) { + try { + final boolean vrModeEnabled = vrManager.getVrModeState(); + synchronized (mGlobalLock) { + vrManager.registerListener(mVrStateCallbacks); + if (vrModeEnabled) { + mVrModeEnabled = vrModeEnabled; + mVrStateCallbacks.onVrStateChanged(vrModeEnabled); + } + } + } catch (RemoteException e) { + // Ignore, we cannot do anything if we failed to register VR mode listener + } + } } private static boolean queryWideColorGamutSupport() { @@ -5382,7 +5445,8 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_ORIENTATION) Slog.i(TAG_WM, "**** Dismissing screen rotation animation"); DisplayInfo displayInfo = displayContent.getDisplayInfo(); // Get rotation animation again, with new top window - if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, false)) { + if (!displayContent.getDisplayPolicy() + .validateRotationAnimationLw(mExitAnimId, mEnterAnimId, false)) { mExitAnimId = mEnterAnimId = 0; } if (screenRotationAnimation.dismiss(mTransaction, MAX_ANIMATION_DURATION, @@ -5522,7 +5586,7 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void statusBarVisibilityChanged(int visibility) { + public void statusBarVisibilityChanged(int displayId, int visibility) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Caller does not hold permission " @@ -5530,9 +5594,12 @@ public class WindowManagerService extends IWindowManager.Stub } synchronized (mGlobalLock) { - mLastStatusBarVisibility = visibility; - visibility = mPolicy.adjustSystemUiVisibilityLw(visibility); - updateStatusBarVisibilityLocked(visibility); + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null) { + displayContent.statusBarVisibilityChanged(visibility); + } else { + Slog.w(TAG, "statusBarVisibilityChanged with invalid displayId=" + displayId); + } } } @@ -5548,49 +5615,36 @@ public class WindowManagerService extends IWindowManager.Stub } } - // TODO(multidisplay): StatusBar on multiple screens? - private boolean updateStatusBarVisibilityLocked(int visibility) { - if (mLastDispatchedSystemUiVisibility == visibility) { - return false; - } - final int globalDiff = (visibility ^ mLastDispatchedSystemUiVisibility) - // We are only interested in differences of one of the - // clearable flags... - & View.SYSTEM_UI_CLEARABLE_FLAGS - // ...if it has actually been cleared. - & ~visibility; - - mLastDispatchedSystemUiVisibility = visibility; - mInputManager.setSystemUiVisibility(visibility); - getDefaultDisplayContentLocked().updateSystemUiVisibility(visibility, globalDiff); - return true; - } - + // TODO: Make the callers use getNavBarPosition(int) only. + /** + * Used by SystemUI and shared SystemUI libraries. + * @see DisplayPolicy#getNavBarPosition() + */ @Override - public void reevaluateStatusBarVisibility() { - synchronized (mGlobalLock) { - int visibility = mPolicy.adjustSystemUiVisibilityLw(mLastStatusBarVisibility); - if (updateStatusBarVisibilityLocked(visibility)) { - mWindowPlacerLocked.requestTraversal(); - } - } + @WindowManagerPolicy.NavigationBarPosition + public int getNavBarPosition() { + return getNavBarPosition(Display.DEFAULT_DISPLAY); } /** * Used by ActivityManager to determine where to position an app with aspect ratio shorter then * the screen is. - * @see WindowManagerPolicy#getNavBarPosition() + * @see DisplayPolicy#getNavBarPosition() */ - @Override @WindowManagerPolicy.NavigationBarPosition - public int getNavBarPosition() { + public int getNavBarPosition(int displayId) { synchronized (mGlobalLock) { // Perform layout if it was scheduled before to make sure that we get correct nav bar // position when doing rotations. - final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked(); - defaultDisplayContent.performLayout(false /* initial */, + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + Slog.w(TAG, "getNavBarPosition with invalid displayId=" + displayId + + " callers=" + Debug.getCallers(3)); + return -1; + } + displayContent.performLayout(false /* initial */, false /* updateInputWindows */); - return mPolicy.getNavBarPosition(); + return displayContent.getDisplayPolicy().getNavBarPosition(); } } @@ -5776,11 +5830,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - @Override - public int getDockedDividerInsetsLw() { - return getDefaultDisplayContentLocked().getDockedDividerController().getContentInsets(); - } - private void dumpPolicyLocked(PrintWriter pw, String[] args, boolean dumpAll) { pw.println("WINDOW MANAGER POLICY STATE (dumpsys window policy)"); mPolicy.dump(" ", pw, args); @@ -5969,12 +6018,6 @@ public class WindowManagerService extends IWindowManager.Stub mTaskSnapshotController.dump(pw, " "); if (dumpAll) { - pw.print(" mSystemDecorLayer="); pw.print(mSystemDecorLayer); - pw.print(" mScreenRect="); pw.println(mScreenRect.toShortString()); - if (mLastStatusBarVisibility != 0) { - pw.print(" mLastStatusBarVisibility=0x"); - pw.println(Integer.toHexString(mLastStatusBarVisibility)); - } final WindowState imeWindow = mRoot.getCurrentInputMethodWindow(); if (imeWindow != null) { pw.print(" mInputMethodWindow="); pw.println(imeWindow); @@ -6007,6 +6050,7 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(" mRecentsAnimationController="); pw.println(mRecentsAnimationController); mRecentsAnimationController.dump(pw, " "); } + PolicyControl.dump(" ", pw); } } @@ -6270,7 +6314,7 @@ public class WindowManagerService extends IWindowManager.Stub public void onOverlayChanged() { synchronized (mGlobalLock) { mRoot.forAllDisplays(displayContent -> { - mPolicy.onOverlayChangedLw(displayContent); + displayContent.getDisplayPolicy().onOverlayChangedLw(); displayContent.updateDisplayInfo(); }); requestTraversal(); @@ -6500,7 +6544,7 @@ public class WindowManagerService extends IWindowManager.Stub final DisplayContent dc = mRoot.getDisplayContent(displayId); if (dc != null) { final DisplayInfo di = dc.getDisplayInfo(); - mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, + dc.getDisplayPolicy().getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, di.displayCutout, outInsets); } } @@ -7102,13 +7146,6 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public boolean isDockedDividerResizing() { - synchronized (mGlobalLock) { - return getDefaultDisplayContentLocked().getDockedDividerController().isResizing(); - } - } - - @Override public void computeWindowsForAccessibility() { final AccessibilityController accessibilityController; synchronized (mGlobalLock) { @@ -7367,9 +7404,11 @@ public class WindowManagerService extends IWindowManager.Stub * {@link ActivityManager#LOCK_TASK_MODE_LOCKED}, * {@link ActivityManager#LOCK_TASK_MODE_PINNED}. */ - public void onLockTaskStateChanged(int lockTaskState) { + void onLockTaskStateChanged(int lockTaskState) { + // TODO: pass in displayId to determine which display the lock task state changed synchronized (mGlobalLock) { - mPolicy.onLockTaskStateChangedLw(lockTaskState); + mRoot.forAllDisplayPolicies(PooledLambda.obtainConsumer( + DisplayPolicy::onLockTaskStateChangedLw, PooledLambda.__(), lockTaskState)); } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index a117cf33e596..567b5836bb9e 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -179,6 +179,7 @@ import android.view.IWindowId; import android.view.InputChannel; import android.view.InputEvent; import android.view.InputEventReceiver; +import android.view.InputWindowHandle; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.View; @@ -191,7 +192,6 @@ import android.view.animation.Interpolator; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ToBooleanFunction; -import android.view.InputWindowHandle; import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.LocalAnimationAdapter.AnimationSpec; import com.android.server.wm.utils.InsetUtils; @@ -1800,7 +1800,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // also been registered in display. dc.mTapExcludeProvidingWindows.remove(this); } - mPolicy.removeWindowLw(this); + dc.getDisplayPolicy().removeWindowLw(this); disposeInputChannel(); @@ -2982,7 +2982,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets, reportDraw, mergedConfiguration, getBackdropFrame(frame), forceRelayout, - mPolicy.isNavBarForcedShownLw(this), displayId, + getDisplayContent().getDisplayPolicy().isNavBarForcedShownLw(this), displayId, new DisplayCutout.ParcelableWrapper(displayCutout)); mDragResizingChangeReported = true; } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 15ea5b5dea18..e090cc5177ba 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1343,7 +1343,7 @@ class WindowStateAnimator { // is running. Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#applyAnimationLocked"); if (mWin.mToken.okToAnimate()) { - int anim = mPolicy.selectAnimationLw(mWin, transit); + int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimationLw(mWin, transit); int attr = -1; Animation a = null; if (anim != 0) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java new file mode 100644 index 000000000000..05912a5e3776 --- /dev/null +++ b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java @@ -0,0 +1,268 @@ +/* + * 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.devicepolicy; + +import android.app.admin.DevicePolicyManager.InstallUpdateCallback; +import android.app.admin.StartInstallingUpdateCallback; +import android.content.Context; +import android.os.ParcelFileDescriptor; +import android.os.UpdateEngine; +import android.os.UpdateEngineCallback; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +/** + * Used for installing an update on <a href="https://source.android.com/devices/tech/ota/ab">AB + * devices.</a> + * <p>This logic is specific to GOTA and should be modified by OEMs using a different AB update + * system.</p> + */ +class AbUpdateInstaller extends UpdateInstaller { + private static final String PAYLOAD_BIN = "payload.bin"; + private static final String PAYLOAD_PROPERTIES_TXT = "payload_properties.txt"; + //https://en.wikipedia.org/wiki/Zip_(file_format)#Local_file_header + private static final int OFFSET_TO_FILE_NAME = 30; + // kDownloadStateInitializationError constant from system/update_engine/common/error_code.h. + private static final int DOWNLOAD_STATE_INITIALIZATION_ERROR = 20; + private long mSizeForUpdate; + private long mOffsetForUpdate; + private List<String> mProperties; + private Enumeration<? extends ZipEntry> mEntries; + private ZipFile mPackedUpdateFile; + private static final Map<Integer, Integer> errorCodesMap = buildErrorCodesMap(); + private static final Map<Integer, String> errorStringsMap = buildErrorStringsMap(); + public static final String UNKNOWN_ERROR = "Unknown error with error code = "; + private boolean mUpdateInstalled; + + private static Map<Integer, Integer> buildErrorCodesMap() { + Map<Integer, Integer> map = new HashMap<>(); + map.put(UpdateEngine.ErrorCodeConstants.ERROR, InstallUpdateCallback.UPDATE_ERROR_UNKNOWN); + map.put( + DOWNLOAD_STATE_INITIALIZATION_ERROR, + InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION); + + // Error constants corresponding to errors related to bad update file. + map.put( + UpdateEngine.ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR, + InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID); + map.put( + UpdateEngine.ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR, + InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID); + map.put( + UpdateEngine.ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR, + InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID); + map.put( + UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR, + InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID); + + // Error constants corresponding to errors related to devices bad state. + map.put( + UpdateEngine.ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR, + InstallUpdateCallback.UPDATE_ERROR_UNKNOWN); + map.put( + UpdateEngine.ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR, + InstallUpdateCallback.UPDATE_ERROR_UNKNOWN); + map.put( + UpdateEngine.ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR, + InstallUpdateCallback.UPDATE_ERROR_UNKNOWN); + map.put( + UpdateEngine.ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE, + InstallUpdateCallback.UPDATE_ERROR_UNKNOWN); + + return map; + } + + private static Map<Integer, String> buildErrorStringsMap() { + Map<Integer, String> map = new HashMap<>(); + map.put(UpdateEngine.ErrorCodeConstants.ERROR, UNKNOWN_ERROR); + map.put( + DOWNLOAD_STATE_INITIALIZATION_ERROR, + "The delta update payload was targeted for another version or the source partition" + + "was modified after it was installed"); + map.put( + UpdateEngine.ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR, + "Failed to finish the configured postinstall works."); + map.put( + UpdateEngine.ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR, + "Failed to open one of the partitions it tried to write to or read data from."); + map.put( + UpdateEngine.ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR, + "Payload mismatch error."); + map.put( + UpdateEngine.ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR, + "Failed to read the payload data from the given URL."); + map.put( + UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR, "Payload hash error."); + map.put( + UpdateEngine.ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR, + "Payload size mismatch error."); + map.put( + UpdateEngine.ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR, + "Failed to verify the signature of the payload."); + map.put( + UpdateEngine.ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE, + "The payload has been successfully installed," + + "but the active slot was not flipped."); + return map; + } + + AbUpdateInstaller(Context context, ParcelFileDescriptor updateFileDescriptor, + StartInstallingUpdateCallback callback, DevicePolicyManagerService.Injector injector, + DevicePolicyConstants constants) { + super(context, updateFileDescriptor, callback, injector, constants); + mUpdateInstalled = false; + } + + @Override + public void installUpdateInThread() { + if (mUpdateInstalled) { + throw new IllegalStateException("installUpdateInThread can be called only once."); + } + try { + setState(); + applyPayload(Paths.get(mCopiedUpdateFile.getAbsolutePath()).toUri().toString()); + } catch (ZipException e) { + Log.w(UpdateInstaller.TAG, e); + notifyCallbackOnError( + InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID, + Log.getStackTraceString(e)); + } catch (IOException e) { + Log.w(UpdateInstaller.TAG, e); + notifyCallbackOnError( + InstallUpdateCallback.UPDATE_ERROR_UNKNOWN, Log.getStackTraceString(e)); + } + } + + private void setState() throws IOException { + mUpdateInstalled = true; + mPackedUpdateFile = new ZipFile(mCopiedUpdateFile); + mProperties = new ArrayList<>(); + mSizeForUpdate = -1; + mOffsetForUpdate = 0; + mEntries = mPackedUpdateFile.entries(); + } + + private UpdateEngine buildBoundUpdateEngine() { + UpdateEngine updateEngine = new UpdateEngine(); + updateEngine.bind(new DelegatingUpdateEngineCallback(this, updateEngine)); + return updateEngine; + } + + private void applyPayload(String updatePath) throws IOException { + if (!updateStateForPayload()) { + return; + } + String[] headerKeyValuePairs = mProperties.stream().toArray(String[]::new); + if (mSizeForUpdate == -1) { + Log.w(UpdateInstaller.TAG, "Failed to find payload entry in the given package."); + notifyCallbackOnError( + InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID, + "Failed to find payload entry in the given package."); + return; + } + + UpdateEngine updateEngine = buildBoundUpdateEngine(); + updateEngine.applyPayload( + updatePath, mOffsetForUpdate, mSizeForUpdate, headerKeyValuePairs); + } + + private boolean updateStateForPayload() throws IOException { + long offset = 0; + while (mEntries.hasMoreElements()) { + ZipEntry entry = mEntries.nextElement(); + + String name = entry.getName(); + offset += buildOffsetForEntry(entry, name); + if (entry.isDirectory()) { + offset -= entry.getCompressedSize(); + continue; + } + if (PAYLOAD_BIN.equals(name)) { + if (entry.getMethod() != ZipEntry.STORED) { + Log.w(UpdateInstaller.TAG, "Invalid compression method."); + notifyCallbackOnError( + InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID, + "Invalid compression method."); + return false; + } + mSizeForUpdate = entry.getCompressedSize(); + mOffsetForUpdate = offset - entry.getCompressedSize(); + } else if (PAYLOAD_PROPERTIES_TXT.equals(name)) { + updatePropertiesForEntry(entry); + } + } + return true; + } + + private long buildOffsetForEntry(ZipEntry entry, String name) { + return OFFSET_TO_FILE_NAME + name.length() + entry.getCompressedSize() + + (entry.getExtra() == null ? 0 : entry.getExtra().length); + } + + private void updatePropertiesForEntry(ZipEntry entry) throws IOException { + try (BufferedReader bufferedReader = new BufferedReader( + new InputStreamReader(mPackedUpdateFile.getInputStream(entry)))) { + String line; + /* Neither @line nor @mProperties are size constraint since there is a few properties + with limited size. */ + while ((line = bufferedReader.readLine()) != null) { + mProperties.add(line); + } + } + } + + private static class DelegatingUpdateEngineCallback extends UpdateEngineCallback { + private UpdateInstaller mUpdateInstaller; + private UpdateEngine mUpdateEngine; + + DelegatingUpdateEngineCallback( + UpdateInstaller updateInstaller, UpdateEngine updateEngine) { + mUpdateInstaller = updateInstaller; + mUpdateEngine = updateEngine; + } + + @Override + public void onStatusUpdate(int statusCode, float percentage) { + return; + } + + @Override + public void onPayloadApplicationComplete(int errorCode) { + mUpdateEngine.unbind(); + if (errorCode == UpdateEngine.ErrorCodeConstants.SUCCESS) { + mUpdateInstaller.notifyCallbackOnSuccess(); + } else { + mUpdateInstaller.notifyCallbackOnError( + errorCodesMap.getOrDefault( + errorCode, InstallUpdateCallback.UPDATE_ERROR_UNKNOWN), + errorStringsMap.getOrDefault(errorCode, UNKNOWN_ERROR + errorCode)); + } + } + } +} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 5926bddbf847..6462d163cc9d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -17,7 +17,9 @@ package com.android.server.devicepolicy; import android.app.admin.DevicePolicyManager; import android.app.admin.IDevicePolicyManager; +import android.app.admin.StartInstallingUpdateCallback; import android.content.ComponentName; +import android.os.ParcelFileDescriptor; import com.android.server.SystemService; @@ -91,4 +93,8 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { @Override public void grantDeviceIdsAccessToProfileOwner(ComponentName who, int userId) { } + + @Override + public void installUpdateFromFile(ComponentName admin, + ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback listener) {} } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java index 71fea02c282f..fd59b4328f86 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java @@ -42,6 +42,12 @@ public class DevicePolicyConstants { private static final String DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY = "das_died_service_stable_connection_threshold_sec"; + private static final String BATTERY_THRESHOLD_NOT_CHARGING_KEY = + "battery_threshold_not_charging"; + + private static final String BATTERY_THRESHOLD_CHARGING_KEY = + "battery_threshold_charging"; + /** * The back-off before re-connecting, when a service binding died, due to the owner * crashing repeatedly. @@ -63,6 +69,17 @@ public class DevicePolicyConstants { */ public final long DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC; + /** + * Battery threshold for installing system update while the device is not charging. + */ + public final int BATTERY_THRESHOLD_NOT_CHARGING; + + /** + * Battery threshold for installing system update while the device is charging. + */ + public final int BATTERY_THRESHOLD_CHARGING; + + private DevicePolicyConstants(String settings) { final KeyValueListParser parser = new KeyValueListParser(','); @@ -87,6 +104,12 @@ public class DevicePolicyConstants { DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY, TimeUnit.MINUTES.toSeconds(2)); + int batteryThresholdNotCharging = parser.getInt( + BATTERY_THRESHOLD_NOT_CHARGING_KEY, 40); + + int batteryThresholdCharging = parser.getInt( + BATTERY_THRESHOLD_CHARGING_KEY, 20); + // Set minimum: 5 seconds. dasDiedServiceReconnectBackoffSec = Math.max(5, dasDiedServiceReconnectBackoffSec); @@ -103,6 +126,8 @@ public class DevicePolicyConstants { DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC = dasDiedServiceReconnectMaxBackoffSec; DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC = dasDiedServiceStableConnectionThresholdSec; + BATTERY_THRESHOLD_NOT_CHARGING = batteryThresholdNotCharging; + BATTERY_THRESHOLD_CHARGING = batteryThresholdCharging; } public static DevicePolicyConstants loadFromString(String settings) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index bbbc40cedd3e..7751b4a44b88 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -113,6 +113,7 @@ import android.app.admin.NetworkEvent; import android.app.admin.PasswordMetrics; import android.app.admin.SecurityLog; import android.app.admin.SecurityLog.SecurityEvent; +import android.app.admin.StartInstallingUpdateCallback; import android.app.admin.SystemUpdateInfo; import android.app.admin.SystemUpdatePolicy; import android.app.backup.IBackupManager; @@ -379,6 +380,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final Set<String> GLOBAL_SETTINGS_DEPRECATED; private static final Set<String> SYSTEM_SETTINGS_WHITELIST; private static final Set<Integer> DA_DISALLOWED_POLICIES; + private static final String AB_DEVICE_KEY = "ro.build.ab_update"; + static { SECURE_SETTINGS_WHITELIST = new ArraySet<>(); SECURE_SETTINGS_WHITELIST.add(Settings.Secure.DEFAULT_INPUT_METHOD); @@ -13315,4 +13318,30 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return mInjector.settingsGlobalGetString(PRIVATE_DNS_SPECIFIER); } + + @Override + public void installUpdateFromFile(ComponentName admin, + ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback callback) { + enforceDeviceOwner(admin); + final long id = mInjector.binderClearCallingIdentity(); + try { + UpdateInstaller updateInstaller; + if (isDeviceAB()) { + updateInstaller = new AbUpdateInstaller( + mContext, updateFileDescriptor, callback, mInjector, mConstants); + } else { + updateInstaller = new NonAbUpdateInstaller( + mContext, updateFileDescriptor, callback, mInjector, mConstants); + } + updateInstaller.startInstallUpdate(); + } finally { + mInjector.binderRestoreCallingIdentity(id); + } + } + + + private boolean isDeviceAB() { + return "true".equalsIgnoreCase(android.os.SystemProperties + .get(AB_DEVICE_KEY, "")); + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java new file mode 100644 index 000000000000..5f1e92682ac1 --- /dev/null +++ b/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java @@ -0,0 +1,52 @@ +/* + * 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.devicepolicy; + +import android.app.admin.DevicePolicyManager; +import android.app.admin.StartInstallingUpdateCallback; +import android.content.Context; +import android.os.ParcelFileDescriptor; +import android.os.RecoverySystem; +import android.util.Log; + +import java.io.IOException; + +/** + * Used for installing an update for <a href="https://source.android.com/devices/tech/ota/nonab">non + * AB</a> devices. + */ +class NonAbUpdateInstaller extends UpdateInstaller { + NonAbUpdateInstaller(Context context, + ParcelFileDescriptor updateFileDescriptor, + StartInstallingUpdateCallback callback, DevicePolicyManagerService.Injector injector, + DevicePolicyConstants constants) { + super(context, updateFileDescriptor, callback, injector, constants); + } + + @Override + public void installUpdateInThread() { + try { + RecoverySystem.installPackage(mContext, mCopiedUpdateFile); + notifyCallbackOnSuccess(); + } catch (IOException e) { + Log.w(TAG, "IO error while trying to install non AB update.", e); + notifyCallbackOnError( + DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN, + Log.getStackTraceString(e)); + } + } +} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java new file mode 100644 index 000000000000..7910598d8429 --- /dev/null +++ b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java @@ -0,0 +1,146 @@ +/* + * 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.devicepolicy; + +import android.app.admin.DevicePolicyManager; +import android.app.admin.StartInstallingUpdateCallback; +import android.content.Context; +import android.os.BatteryManager; +import android.os.Environment; +import android.os.FileUtils; +import android.os.ParcelFileDescriptor; +import android.os.PowerManager; +import android.os.Process; +import android.os.RemoteException; +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +abstract class UpdateInstaller { + private StartInstallingUpdateCallback mCallback; + private ParcelFileDescriptor mUpdateFileDescriptor; + private DevicePolicyConstants mConstants; + protected Context mContext; + protected File mCopiedUpdateFile; + + static final String TAG = "UpdateInstaller"; + private DevicePolicyManagerService.Injector mInjector; + + protected UpdateInstaller(Context context, ParcelFileDescriptor updateFileDescriptor, + StartInstallingUpdateCallback callback, DevicePolicyManagerService.Injector injector, + DevicePolicyConstants constants) { + mContext = context; + mCallback = callback; + mUpdateFileDescriptor = updateFileDescriptor; + mInjector = injector; + mConstants = constants; + } + + public abstract void installUpdateInThread(); + + public void startInstallUpdate() { + if (!checkIfBatteryIsSufficient()) { + notifyCallbackOnError( + DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_BATTERY_LOW, + "The battery level must be above " + + mConstants.BATTERY_THRESHOLD_NOT_CHARGING + " while not charging or" + + "above " + mConstants.BATTERY_THRESHOLD_CHARGING + " while charging"); + return; + } + Thread thread = new Thread(() -> { + mCopiedUpdateFile = copyUpdateFileToDataOtaPackageDir(); + if (mCopiedUpdateFile == null) { + notifyCallbackOnError( + DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN, + "Error while copying file."); + return; + } + installUpdateInThread(); + }); + thread.setPriority(Process.THREAD_PRIORITY_BACKGROUND); + thread.start(); + } + + private boolean checkIfBatteryIsSufficient() { + BatteryManager batteryManager = + (BatteryManager) mContext.getSystemService(Context.BATTERY_SERVICE); + if (batteryManager != null) { + int chargePercentage = batteryManager + .getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY); + return batteryManager.isCharging() + ? chargePercentage >= mConstants.BATTERY_THRESHOLD_CHARGING + : chargePercentage >= mConstants.BATTERY_THRESHOLD_NOT_CHARGING; + } + return false; + } + + private File copyUpdateFileToDataOtaPackageDir() { + try { + File destination = createNewFileWithPermissions(); + copyToFile(destination); + return destination; + } catch (IOException e) { + Log.w(TAG, "Failed to copy update file to OTA directory", e); + notifyCallbackOnError( + DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN, + Log.getStackTraceString(e)); + return null; + } + } + + private File createNewFileWithPermissions() throws IOException { + File destination = File.createTempFile( + "update", ".zip", new File(Environment.getDataDirectory() + "/ota_package")); + FileUtils.setPermissions( + /* path= */ destination, + /* mode= */ FileUtils.S_IRWXU | FileUtils.S_IRGRP | FileUtils.S_IROTH, + /* uid= */ -1, /* gid= */ -1); + return destination; + } + + private void copyToFile(File destination) throws IOException { + try (OutputStream out = new FileOutputStream(destination); + InputStream in = new ParcelFileDescriptor.AutoCloseInputStream( + mUpdateFileDescriptor)) { + FileUtils.copy(in, out); + } + } + + void cleanupUpdateFile() { + if (mCopiedUpdateFile.exists()) { + mCopiedUpdateFile.delete(); + } + } + + protected void notifyCallbackOnError(int errorCode, String errorMessage) { + cleanupUpdateFile(); + try { + mCallback.onStartInstallingUpdateError(errorCode, errorMessage); + } catch (RemoteException e) { + Log.d(TAG, "Error while calling callback", e); + } + } + + protected void notifyCallbackOnSuccess() { + cleanupUpdateFile(); + mInjector.powerManagerReboot(PowerManager.REBOOT_REQUESTED_BY_DEVICE_OWNER); + } +} diff --git a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java index 57e954f10fa7..08fbf5549a87 100644 --- a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java +++ b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java @@ -24,11 +24,14 @@ import android.service.intelligence.InteractionContext; import android.service.intelligence.InteractionSessionId; import android.service.intelligence.SnapshotData; import android.util.Slog; +import android.view.autofill.AutofillId; +import android.view.autofill.IAutoFillManagerClient; import android.view.intelligence.ContentCaptureEvent; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import com.android.server.AbstractRemoteService; +import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback; import com.android.server.intelligence.RemoteIntelligenceService.RemoteIntelligenceServiceCallbacks; import java.io.PrintWriter; @@ -39,12 +42,12 @@ final class ContentCaptureSession implements RemoteIntelligenceServiceCallbacks private static final String TAG = "ContentCaptureSession"; private final Object mLock; - private final IBinder mActivityToken; - + final IBinder mActivityToken; private final IntelligencePerUserService mService; private final RemoteIntelligenceService mRemoteService; private final InteractionContext mInterationContext; private final InteractionSessionId mId; + private AugmentedAutofillCallback mAutofillCallback; ContentCaptureSession(@NonNull Context context, int userId, @NonNull Object lock, @NonNull IBinder activityToken, @NonNull IntelligencePerUserService service, @@ -92,6 +95,18 @@ final class ContentCaptureSession implements RemoteIntelligenceServiceCallbacks } /** + * Requests the service to autofill the given field. + */ + public AugmentedAutofillCallback requestAutofillLocked(@NonNull IAutoFillManagerClient client, + int autofillSessionId, @NonNull AutofillId focusedId) { + mRemoteService.onRequestAutofillLocked(mId, client, autofillSessionId, focusedId); + if (mAutofillCallback == null) { + mAutofillCallback = () -> mRemoteService.onDestroyAutofillWindowsRequest(mId); + } + return mAutofillCallback; + } + + /** * Cleans up the session and removes it from the service. * * @param notifyRemoteService whether it should trigger a {@link @@ -119,6 +134,11 @@ final class ContentCaptureSession implements RemoteIntelligenceServiceCallbacks if (mService.isVerbose()) { Slog.v(TAG, "destroyLocked(notifyRemoteService=" + notifyRemoteService + ")"); } + if (mAutofillCallback != null) { + mAutofillCallback.destroy(); + mAutofillCallback = null; + } + // TODO(b/111276913): must call client to set session as FINISHED_BY_SERVER if (notifyRemoteService) { mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId); @@ -152,6 +172,8 @@ final class ContentCaptureSession implements RemoteIntelligenceServiceCallbacks pw.print(prefix); pw.print("id: "); mId.dump(pw); pw.println(); pw.print(prefix); pw.print("context: "); mInterationContext.dump(pw); pw.println(); pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken); + pw.print(prefix); pw.print("has autofill callback: "); + pw.println(mAutofillCallback != null); } @Override diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java index a7f45ee4c9bf..38810dd8d8d8 100644 --- a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java +++ b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java @@ -27,6 +27,8 @@ import android.os.Bundle; import android.os.IBinder; import android.os.UserManager; import android.service.intelligence.InteractionSessionId; +import android.view.autofill.AutofillId; +import android.view.autofill.IAutoFillManagerClient; import android.view.intelligence.ContentCaptureEvent; import android.view.intelligence.IIntelligenceManager; @@ -86,20 +88,6 @@ public final class IntelligenceManagerService extends service.destroyLocked(); } - /** - * Notifies the intelligence service of new assist data for the given activity. - * - * @return {@code false} if there was no service set for the given user - */ - private boolean sendActivityAssistDataLocked(@UserIdInt int userId, - @NonNull IBinder activityToken, @NonNull Bundle data) { - final IntelligencePerUserService service = peekServiceForUserLocked(userId); - if (service != null) { - return service.sendActivityAssistDataLocked(activityToken, data); - } - return false; - } - private ActivityManagerInternal getAmInternal() { synchronized (mLock) { if (mAm == null) { @@ -112,7 +100,7 @@ public final class IntelligenceManagerService extends final class IntelligenceManagerServiceStub extends IIntelligenceManager.Stub { @Override - public void startSession(int userId, @NonNull IBinder activityToken, + public void startSession(@UserIdInt int userId, @NonNull IBinder activityToken, @NonNull ComponentName componentName, @NonNull InteractionSessionId sessionId, int flags, @NonNull IResultReceiver result) { Preconditions.checkNotNull(activityToken); @@ -134,7 +122,7 @@ public final class IntelligenceManagerService extends } @Override - public void sendEvents(int userId, @NonNull InteractionSessionId sessionId, + public void sendEvents(@UserIdInt int userId, @NonNull InteractionSessionId sessionId, @NonNull List<ContentCaptureEvent> events) { Preconditions.checkNotNull(sessionId); Preconditions.checkNotNull(events); @@ -146,7 +134,7 @@ public final class IntelligenceManagerService extends } @Override - public void finishSession(int userId, @NonNull InteractionSessionId sessionId) { + public void finishSession(@UserIdInt int userId, @NonNull InteractionSessionId sessionId) { Preconditions.checkNotNull(sessionId); synchronized (mLock) { @@ -168,14 +156,13 @@ public final class IntelligenceManagerService extends private final class LocalService extends IntelligenceManagerInternal { @Override - public boolean isIntelligenceServiceForUser(int uid, int userId) { + public boolean isIntelligenceServiceForUser(int uid, @UserIdInt int userId) { synchronized (mLock) { final IntelligencePerUserService service = peekServiceForUserLocked(userId); if (service != null) { return service.isIntelligenceServiceForUserLocked(uid); } } - return false; } @@ -183,8 +170,26 @@ public final class IntelligenceManagerService extends public boolean sendActivityAssistData(@UserIdInt int userId, @NonNull IBinder activityToken, @NonNull Bundle data) { synchronized (mLock) { - return sendActivityAssistDataLocked(userId, activityToken, data); + final IntelligencePerUserService service = peekServiceForUserLocked(userId); + if (service != null) { + return service.sendActivityAssistDataLocked(activityToken, data); + } + } + return false; + } + + @Override + public AugmentedAutofillCallback requestAutofill(@UserIdInt int userId, + @NonNull IAutoFillManagerClient client, @NonNull IBinder activityToken, + int autofillSessionId, @NonNull AutofillId focusedId) { + synchronized (mLock) { + final IntelligencePerUserService service = peekServiceForUserLocked(userId); + if (service != null) { + return service.requestAutofill(client, activityToken, autofillSessionId, + focusedId); + } } + return null; } } } diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java index 9694ab968e71..051f0d695fcf 100644 --- a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java +++ b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java @@ -22,6 +22,7 @@ import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUC import android.Manifest; import android.annotation.NonNull; +import android.annotation.UserIdInt; import android.app.AppGlobals; import android.app.assist.AssistContent; import android.app.assist.AssistStructure; @@ -36,12 +37,15 @@ import android.service.intelligence.InteractionSessionId; import android.service.intelligence.SnapshotData; import android.util.ArrayMap; import android.util.Slog; +import android.view.autofill.AutofillId; +import android.view.autofill.IAutoFillManagerClient; import android.view.intelligence.ContentCaptureEvent; import android.view.intelligence.IntelligenceManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.IResultReceiver; import com.android.server.AbstractPerUserSystemService; +import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback; import java.io.PrintWriter; import java.util.List; @@ -62,7 +66,7 @@ final class IntelligencePerUserService // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's protected IntelligencePerUserService( - IntelligenceManagerService master, Object lock, int userId) { + IntelligenceManagerService master, Object lock, @UserIdInt int userId) { super(master, lock, userId); } @@ -210,6 +214,17 @@ final class IntelligencePerUserService return uid == getServiceUidLocked(); } + @GuardedBy("mLock") + private ContentCaptureSession getSession(@NonNull IBinder activityToken) { + for (int i = 0; i < mSessions.size(); i++) { + final ContentCaptureSession session = mSessions.valueAt(i); + if (session.mActivityToken.equals(activityToken)) { + return session; + } + } + return null; + } + /** * Destroys the service and all state associated with it. * @@ -226,6 +241,22 @@ final class IntelligencePerUserService mSessions.clear(); } + public AugmentedAutofillCallback requestAutofill(@NonNull IAutoFillManagerClient client, + @NonNull IBinder activityToken, int autofillSessionId, @NonNull AutofillId focusedId) { + synchronized (mLock) { + final ContentCaptureSession session = getSession(activityToken); + if (session != null) { + // TODO(b/111330312): log metrics + if (mMaster.verbose) Slog.v(TAG, "requestAugmentedAutofill()"); + return session.requestAutofillLocked(client, autofillSessionId, focusedId); + } + if (mMaster.debug) { + Slog.d(TAG, "requestAutofill(): no session for " + activityToken); + } + return null; + } + } + @Override protected void dumpLocked(String prefix, PrintWriter pw) { super.dumpLocked(prefix, pw); diff --git a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java index a27c1cf98a9a..00c5b6a1d67b 100644 --- a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java +++ b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java @@ -19,6 +19,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; +import android.os.Bundle; import android.os.IBinder; import android.os.IInterface; import android.os.RemoteException; @@ -28,8 +29,12 @@ import android.service.intelligence.InteractionSessionId; import android.service.intelligence.SnapshotData; import android.text.format.DateUtils; import android.util.Slog; +import android.view.autofill.AutofillId; +import android.view.autofill.AutofillManager; +import android.view.autofill.IAutoFillManagerClient; import android.view.intelligence.ContentCaptureEvent; +import com.android.internal.os.IResultReceiver; import com.android.server.AbstractRemoteService; import java.util.List; @@ -39,7 +44,7 @@ final class RemoteIntelligenceService extends AbstractRemoteService { private static final String TAG = "RemoteIntelligenceService"; private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS; - private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS; + private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS; private final RemoteIntelligenceServiceCallbacks mCallbacks; private IIntelligenceService mService; @@ -101,6 +106,25 @@ final class RemoteIntelligenceService extends AbstractRemoteService { scheduleRequest(new PendingOnActivitySnapshotRequest(this, sessionId, snapshotData)); } + /** + * Called by {@link ContentCaptureSession} to request augmented autofill. + */ + public void onRequestAutofillLocked(@NonNull InteractionSessionId sessionId, + @NonNull IAutoFillManagerClient client, int autofillSessionId, + @NonNull AutofillId focusedId) { + cancelScheduledUnbind(); + scheduleRequest(new PendingAutofillRequest(this, sessionId, client, autofillSessionId, + focusedId)); + } + + /** + * Called by {@link ContentCaptureSession} when it's time to destroy all augmented autofill + * requests. + */ + public void onDestroyAutofillWindowsRequest(@NonNull InteractionSessionId sessionId) { + cancelScheduledUnbind(); + scheduleRequest(new PendingDestroyAutofillWindowsRequest(this, sessionId)); + } private abstract static class MyPendingRequest extends PendingRequest<RemoteIntelligenceService> { @@ -124,8 +148,9 @@ final class RemoteIntelligenceService extends AbstractRemoteService { final RemoteIntelligenceService remoteService = getService(); if (remoteService != null) { try { - myRun(remoteService); // We don't expect the service to call us back, so we finish right away. + myRun(remoteService); + // TODO(b/111330312): not true anymore!! finish(); } catch (RemoteException e) { Slog.w(TAG, "exception handling " + getClass().getSimpleName() + " for " @@ -191,6 +216,53 @@ final class RemoteIntelligenceService extends AbstractRemoteService { } } + private static final class PendingAutofillRequest extends MyPendingRequest { + private final @NonNull AutofillId mFocusedId; + private final @NonNull IAutoFillManagerClient mClient; + private final int mAutofillSessionId; + + protected PendingAutofillRequest(@NonNull RemoteIntelligenceService service, + @NonNull InteractionSessionId sessionId, @NonNull IAutoFillManagerClient client, + int autofillSessionId, @NonNull AutofillId focusedId) { + super(service, sessionId); + mClient = client; + mAutofillSessionId = autofillSessionId; + mFocusedId = focusedId; + } + + @Override // from MyPendingRequest + public void myRun(@NonNull RemoteIntelligenceService remoteService) throws RemoteException { + final IResultReceiver receiver = new IResultReceiver.Stub() { + + @Override + public void send(int resultCode, Bundle resultData) throws RemoteException { + final IBinder realClient = resultData + .getBinder(AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT); + remoteService.mService.onAutofillRequest(mSessionId, realClient, + mAutofillSessionId, mFocusedId); + } + }; + + // TODO(b/111330312): set cancellation signal, timeout (from both mClient and service), + // cache IAugmentedAutofillManagerClient reference, etc... + mClient.getAugmentedAutofillClient(receiver); + } + } + + private static final class PendingDestroyAutofillWindowsRequest extends MyPendingRequest { + + protected PendingDestroyAutofillWindowsRequest(@NonNull RemoteIntelligenceService service, + @NonNull InteractionSessionId sessionId) { + super(service, sessionId); + } + + @Override + protected void myRun(@NonNull RemoteIntelligenceService service) throws RemoteException { + service.mService.onDestroyAutofillWindowsRequest(mSessionId); + // TODO(b/111330312): implement timeout + } + } + public interface RemoteIntelligenceServiceCallbacks extends VultureCallback { // To keep it simple, we use the same callback for all failures / timeouts. void onFailureOrTimeout(boolean timedOut); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index f8ac41f3c40c..56f7cff565af 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -36,7 +36,6 @@ import android.content.res.Resources.Theme; import android.database.sqlite.SQLiteCompatibilityWalFlags; import android.database.sqlite.SQLiteGlobal; import android.hardware.display.ColorDisplayManager; -import android.hardware.display.DisplayManagerInternal; import android.os.BaseBundle; import android.os.Binder; import android.os.Build; @@ -717,17 +716,9 @@ public final class SystemServer { // Manages Overlay packages traceBeginAndSlog("StartOverlayManagerService"); - OverlayManagerService overlayManagerService = new OverlayManagerService( - mSystemContext, installer); - mSystemServiceManager.startService(overlayManagerService); + mSystemServiceManager.startService(new OverlayManagerService(mSystemContext, installer)); traceEnd(); - if (SystemProperties.getInt("persist.sys.displayinset.top", 0) > 0) { - // DisplayManager needs the overlay immediately. - overlayManagerService.updateSystemUiContext(); - LocalServices.getService(DisplayManagerInternal.class).onOverlayChanged(); - } - // The sensor service needs access to package manager service, app ops // service, and permissions service, therefore we start it after them. // Start sensor service in a separate thread. Completion should be checked diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk index 565152c31448..6f10ed5f4f5c 100644 --- a/services/robotests/Android.mk +++ b/services/robotests/Android.mk @@ -26,6 +26,7 @@ LOCAL_MODULE_TAGS := optional LOCAL_PRIVILEGED_MODULE := true LOCAL_STATIC_JAVA_LIBRARIES := \ + bmgrlib \ services.backup \ services.core \ services.net diff --git a/services/robotests/src/com/android/commands/bmgr/BmgrTest.java b/services/robotests/src/com/android/commands/bmgr/BmgrTest.java new file mode 100644 index 000000000000..1705f5b8c704 --- /dev/null +++ b/services/robotests/src/com/android/commands/bmgr/BmgrTest.java @@ -0,0 +1,63 @@ +/* + * 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.commands.bmgr; + +import static org.mockito.Mockito.verify; + +import android.app.backup.IBackupManager; +import android.os.UserHandle; +import android.platform.test.annotations.Presubmit; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; + +/** Unit tests for {@link com.android.commands.bmgr.Bmgr}. */ +@RunWith(RobolectricTestRunner.class) +@Presubmit +public class BmgrTest { + @Mock private IBackupManager mBackupManager; + private Bmgr mBmgr; + + /** Common setup run before each test method. */ + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mBmgr = new Bmgr(mBackupManager); + } + + /** + * Test that bmgr uses the default user {@link UserHandle.USER_SYSTEM} if no user is specified. + */ + @Test + public void testRun_whenUserNotSpecified_callsBackupManagerAsSystemUser() throws Exception { + mBmgr.run(new String[] {"run"}); + + verify(mBackupManager).isBackupServiceActive(UserHandle.USER_SYSTEM); + } + + /** Test that bmgr uses the specified user if an user is specified. */ + @Test + public void testRun_whenUserSpecified_callsBackupManagerAsSpecifiedUser() throws Exception { + mBmgr.run(new String[] {"--user", "10"}); + + verify(mBackupManager).isBackupServiceActive(10); + } +} diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp new file mode 100644 index 000000000000..ebc816dc45da --- /dev/null +++ b/services/tests/mockingservicestests/Android.bp @@ -0,0 +1,46 @@ +// 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. + +android_test { + name: "FrameworksMockingServicesTests", + + srcs: ["src/**/*.java"], + + static_libs: [ + "services.core", + "services.net", + "androidx.test.runner", + "mockito-target-extended-minus-junit4", + "platform-test-annotations", + ], + + libs: [ + "android.test.mock", + "android.test.base", + "android.test.runner", + ], + + jni_libs: [ + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + ], + + certificate: "platform", + platform_apis: true, + test_suites: ["device-tests"], + + optimize: { + enabled: false, + }, +} diff --git a/services/tests/mockingservicestests/Android.mk b/services/tests/mockingservicestests/Android.mk deleted file mode 100644 index b21b3e42adc3..000000000000 --- a/services/tests/mockingservicestests/Android.mk +++ /dev/null @@ -1,42 +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. - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_STATIC_JAVA_LIBRARIES := \ - services.core \ - services.net \ - androidx.test.runner \ - mockito-target-extended-minus-junit4 \ - platform-test-annotations \ - -LOCAL_JAVA_LIBRARIES := android.test.mock android.test.base android.test.runner - -LOCAL_JNI_SHARED_LIBRARIES := \ - libdexmakerjvmtiagent \ - libstaticjvmtiagent \ - -LOCAL_CERTIFICATE := platform -LOCAL_PACKAGE_NAME := FrameworksMockingServicesTests -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_COMPATIBILITY_SUITE := device-tests - -LOCAL_PROGUARD_ENABLED := disabled - -include $(BUILD_PACKAGE) diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp new file mode 100644 index 000000000000..e80434224633 --- /dev/null +++ b/services/tests/servicestests/Android.bp @@ -0,0 +1,104 @@ +//######################################################################## +// Build FrameworksServicesTests package +//######################################################################## + +android_test { + name: "FrameworksServicesTests", + + // Include all test java files. + srcs: [ + "src/**/*.java", + + "aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl", + "aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl", + + "test-apps/JobTestApp/src/**/*.java", + + "test-apps/SuspendTestApp/src/**/*.java", + ], + static_libs: [ + "frameworks-base-testutils", + "services.accessibility", + "services.appwidget", + "services.autofill", + "services.backup", + "services.core", + "services.devicepolicy", + "services.net", + "services.usage", + "guava", + "androidx.test.runner", + "androidx.test.rules", + "mockito-target-minus-junit4", + "platform-test-annotations", + "ShortcutManagerTestUtils", + "truth-prebuilt", + "testables", + "testng", + "ub-uiautomator", + "platformprotosnano", + "hamcrest-library", + "servicestests-utils", + ], + + aidl: { + local_include_dirs: ["aidl"], + }, + + libs: [ + "android.hidl.manager-V1.0-java", + "android.hardware.tv.cec-V1.0-java", + "android.test.mock", + "android.test.base", + "android.test.runner", + ], + + platform_apis: true, + test_suites: ["device-tests"], + + certificate: "platform", + + // These are not normally accessible from apps so they must be explicitly included. + jni_libs: [ + "libbacktrace", + "libbase", + "libbinder", + "libbinderthreadstate", + "libc++", + "libcutils", + "liblog", + "liblzma", + "libnativehelper", + "libnetdaidl", + "libui", + "libunwind", + "libutils", + ], + + dxflags: ["--multi-dex"], + + optimize: { + enabled: false, + }, +} + +java_library { + name: "servicestests-utils", + srcs: [ + "utils/**/*.java", + ], + static_libs: [ + "android-support-test", + "mockito-target-minus-junit4", + ], + libs: [ + "android.test.runner", + ], +} + +filegroup { + name: "servicestests-SuspendTestApp-files", + srcs: [ + "src/com/android/server/pm/SuspendPackagesTest.java", + ], +} diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk deleted file mode 100644 index e2f899555aca..000000000000 --- a/services/tests/servicestests/Android.mk +++ /dev/null @@ -1,83 +0,0 @@ -######################################################################### -# Build FrameworksServicesTests package -######################################################################### - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# We only want this apk build for tests. -LOCAL_MODULE_TAGS := tests - -# Include all test java files. -LOCAL_SRC_FILES := \ - $(call all-java-files-under, src) \ - $(call all-java-files-under, utils) \ - -LOCAL_STATIC_JAVA_LIBRARIES := \ - frameworks-base-testutils \ - services.accessibility \ - services.appwidget \ - services.autofill \ - services.backup \ - services.core \ - services.devicepolicy \ - services.net \ - services.usage \ - guava \ - androidx.test.runner \ - androidx.test.rules \ - mockito-target-minus-junit4 \ - platform-test-annotations \ - ShortcutManagerTestUtils \ - truth-prebuilt \ - testables \ - testng \ - ub-uiautomator\ - platformprotosnano \ - hamcrest-library - -LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl - -LOCAL_SRC_FILES += aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl \ - aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl -LOCAL_SRC_FILES += $(call all-java-files-under, test-apps/JobTestApp/src) -LOCAL_SRC_FILES += $(call all-java-files-under, test-apps/SuspendTestApp/src) - -LOCAL_JAVA_LIBRARIES := \ - android.hidl.manager-V1.0-java \ - android.hardware.tv.cec-V1.0-java \ - android.test.mock \ - android.test.base android.test.runner \ - -LOCAL_PACKAGE_NAME := FrameworksServicesTests -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_COMPATIBILITY_SUITE := device-tests - -LOCAL_CERTIFICATE := platform - -# These are not normally accessible from apps so they must be explicitly included. -LOCAL_JNI_SHARED_LIBRARIES := \ - libbacktrace \ - libbase \ - libbinder \ - libbinderthreadstate \ - libc++ \ - libcutils \ - liblog \ - liblzma \ - libnativehelper \ - libnetdaidl \ - libui \ - libunwind \ - libutils - -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk - -LOCAL_JACK_FLAGS := --multi-dex native -LOCAL_DX_FLAGS := --multi-dex - -LOCAL_PROGUARD_ENABLED := disabled - -include $(BUILD_PACKAGE) - -include $(call all-makefiles-under, $(LOCAL_PATH)) diff --git a/services/tests/servicestests/aidl/Android.bp b/services/tests/servicestests/aidl/Android.bp new file mode 100644 index 000000000000..d4e53dddf4a7 --- /dev/null +++ b/services/tests/servicestests/aidl/Android.bp @@ -0,0 +1,22 @@ +// 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. + +java_library { + name: "servicestests-aidl", + sdk_version: "current", + srcs: [ + "com/android/servicestests/aidl/INetworkStateObserver.aidl", + "com/android/servicestests/aidl/ICmdReceiverService.aidl", + ], +} diff --git a/services/tests/servicestests/aidl/Android.mk b/services/tests/servicestests/aidl/Android.mk deleted file mode 100644 index 166da1dea7e3..000000000000 --- a/services/tests/servicestests/aidl/Android.mk +++ /dev/null @@ -1,24 +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. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE_TAGS := tests -LOCAL_SDK_VERSION := current -LOCAL_SRC_FILES := \ - com/android/servicestests/aidl/INetworkStateObserver.aidl \ - com/android/servicestests/aidl/ICmdReceiverService.aidl -LOCAL_MODULE := servicestests-aidl -include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java index f024fe77aaf7..18bd2e45acd4 100644 --- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.policy; +package com.android.server.wm; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_180; @@ -25,36 +25,27 @@ import static org.hamcrest.Matchers.equalTo; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; -import android.view.Display; import android.view.DisplayInfo; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ErrorCollector; +import org.junit.runner.RunWith; -/** - * Build/Install/Run: - * atest WmTests:PhoneWindowManagerInsetsTest - */ +@RunWith(AndroidJUnit4.class) @SmallTest @Presubmit -public class PhoneWindowManagerInsetsTest extends PhoneWindowManagerTestBase { +public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase { @Rule public final ErrorCollector mErrorCollector = new ErrorCollector(); - @Before - public void setUp() throws Exception { - addStatusBar(); - addNavigationBar(); - } - @Test public void portrait() { - DisplayInfo di = displayInfoForRotation(ROTATION_0, false /* withCutout */); + final DisplayInfo di = displayInfoForRotation(ROTATION_0, false /* withCutout */); verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT); @@ -63,7 +54,7 @@ public class PhoneWindowManagerInsetsTest extends PhoneWindowManagerTestBase { @Test public void portrait_withCutout() { - DisplayInfo di = displayInfoForRotation(ROTATION_0, true /* withCutout */); + final DisplayInfo di = displayInfoForRotation(ROTATION_0, true /* withCutout */); verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); verifyNonDecorInsets(di, 0, DISPLAY_CUTOUT_HEIGHT, 0, NAV_BAR_HEIGHT); @@ -72,7 +63,7 @@ public class PhoneWindowManagerInsetsTest extends PhoneWindowManagerTestBase { @Test public void landscape() { - DisplayInfo di = displayInfoForRotation(ROTATION_90, false /* withCutout */); + final DisplayInfo di = displayInfoForRotation(ROTATION_90, false /* withCutout */); verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); verifyNonDecorInsets(di, 0, 0, NAV_BAR_HEIGHT, 0); @@ -81,7 +72,7 @@ public class PhoneWindowManagerInsetsTest extends PhoneWindowManagerTestBase { @Test public void landscape_withCutout() { - DisplayInfo di = displayInfoForRotation(ROTATION_90, true /* withCutout */); + final DisplayInfo di = displayInfoForRotation(ROTATION_90, true /* withCutout */); verifyStableInsets(di, DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); verifyNonDecorInsets(di, DISPLAY_CUTOUT_HEIGHT, 0, NAV_BAR_HEIGHT, 0); @@ -90,7 +81,7 @@ public class PhoneWindowManagerInsetsTest extends PhoneWindowManagerTestBase { @Test public void seascape() { - DisplayInfo di = displayInfoForRotation(ROTATION_270, false /* withCutout */); + final DisplayInfo di = displayInfoForRotation(ROTATION_270, false /* withCutout */); verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0); verifyNonDecorInsets(di, NAV_BAR_HEIGHT, 0, 0, 0); @@ -99,7 +90,7 @@ public class PhoneWindowManagerInsetsTest extends PhoneWindowManagerTestBase { @Test public void seascape_withCutout() { - DisplayInfo di = displayInfoForRotation(ROTATION_270, true /* withCutout */); + final DisplayInfo di = displayInfoForRotation(ROTATION_270, true /* withCutout */); verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0); verifyNonDecorInsets(di, NAV_BAR_HEIGHT, 0, DISPLAY_CUTOUT_HEIGHT, 0); @@ -108,7 +99,7 @@ public class PhoneWindowManagerInsetsTest extends PhoneWindowManagerTestBase { @Test public void upsideDown() { - DisplayInfo di = displayInfoForRotation(ROTATION_180, false /* withCutout */); + final DisplayInfo di = displayInfoForRotation(ROTATION_180, false /* withCutout */); verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT); @@ -117,7 +108,7 @@ public class PhoneWindowManagerInsetsTest extends PhoneWindowManagerTestBase { @Test public void upsideDown_withCutout() { - DisplayInfo di = displayInfoForRotation(ROTATION_180, true /* withCutout */); + final DisplayInfo di = displayInfoForRotation(ROTATION_180, true /* withCutout */); verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT); verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT); @@ -151,35 +142,39 @@ public class PhoneWindowManagerInsetsTest extends PhoneWindowManagerTestBase { private Rect getStableInsetsLw(DisplayInfo di) { Rect result = new Rect(); - mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, + mDisplayPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, di.displayCutout, result); return result; } private Rect getNonDecorInsetsLw(DisplayInfo di) { Rect result = new Rect(); - mPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, + mDisplayPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, di.displayCutout, result); return result; } private int getNonDecorDisplayWidth(DisplayInfo di) { - return mPolicy.getNonDecorDisplayWidth(di.logicalWidth, di.logicalHeight, di.rotation, - 0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout); + return mDisplayPolicy.getNonDecorDisplayWidth(di.logicalWidth, di.logicalHeight, + di.rotation, 0 /* ui */, di.displayCutout); } private int getNonDecorDisplayHeight(DisplayInfo di) { - return mPolicy.getNonDecorDisplayHeight(di.logicalWidth, di.logicalHeight, di.rotation, - 0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout); + return mDisplayPolicy.getNonDecorDisplayHeight(di.logicalWidth, di.logicalHeight, + di.rotation, 0 /* ui */, di.displayCutout); } private int getConfigDisplayWidth(DisplayInfo di) { - return mPolicy.getConfigDisplayWidth(di.logicalWidth, di.logicalHeight, di.rotation, - 0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout); + return mDisplayPolicy.getConfigDisplayWidth(di.logicalWidth, di.logicalHeight, + di.rotation, 0 /* ui */, di.displayCutout); } private int getConfigDisplayHeight(DisplayInfo di) { - return mPolicy.getConfigDisplayHeight(di.logicalWidth, di.logicalHeight, di.rotation, - 0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout); + return mDisplayPolicy.getConfigDisplayHeight(di.logicalWidth, di.logicalHeight, + di.rotation, 0 /* ui */, di.displayCutout); + } + + private static DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) { + return displayInfoAndCutoutForRotation(rotation, withDisplayCutout).first; } } diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java new file mode 100644 index 000000000000..a91c5e79ccfc --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -0,0 +1,422 @@ +/* + * 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.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; +import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; +import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.spy; + +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.util.Pair; +import android.view.DisplayCutout; +import android.view.DisplayInfo; +import android.view.WindowManager; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.wm.utils.WmDisplayCutout; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +@Presubmit +public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { + + private DisplayFrames mFrames; + private WindowState mWindow; + private int mRotation = ROTATION_0; + private boolean mHasDisplayCutout; + + @Before + public void setUp() throws Exception { + updateDisplayFrames(); + + mWindow = spy(createWindow(null, TYPE_APPLICATION, "window")); + // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from + // changing those frames. + doNothing().when(mWindow).computeFrameLw(); + + final WindowManager.LayoutParams attrs = mWindow.mAttrs; + attrs.width = MATCH_PARENT; + attrs.height = MATCH_PARENT; + attrs.flags = + FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + attrs.format = PixelFormat.TRANSLUCENT; + } + + public void setRotation(int rotation) { + mRotation = rotation; + updateDisplayFrames(); + } + + public void addDisplayCutout() { + mHasDisplayCutout = true; + updateDisplayFrames(); + } + + private void updateDisplayFrames() { + final Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation, + mHasDisplayCutout); + mFrames = new DisplayFrames(mDisplayContent.getDisplayId(), info.first, info.second); + } + + @Test + public void layoutWindowLw_appDrawsBars() { + mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); + assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0); + } + + @Test + public void layoutWindowLw_appWontDrawBars() { + mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT); + } + + @Test + public void layoutWindowLw_appWontDrawBars_forceStatus() throws Exception { + mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDecorFrame(), 0, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT); + } + + @Test + public void addingWindow_doesNotTamperWithSysuiFlags() { + mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + addWindow(mWindow); + + assertEquals(0, mWindow.mAttrs.systemUiVisibility); + assertEquals(0, mWindow.mAttrs.subtreeSystemUiVisibility); + } + + @Test + public void layoutWindowLw_withDisplayCutout() { + addDisplayCutout(); + + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_never() { + addDisplayCutout(); + + mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_layoutFullscreen() { + addDisplayCutout(); + + mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); + assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_fullscreen() { + addDisplayCutout(); + + mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() { + addDisplayCutout(); + + mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN; + mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0); + } + + + @Test + public void layoutWindowLw_withDisplayCutout_landscape() { + addDisplayCutout(); + setRotation(ROTATION_90); + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mWindow.getContentFrameLw(), + DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_seascape() { + addDisplayCutout(); + setRotation(ROTATION_270); + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetBy(mWindow.getParentFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0); + assertInsetBy(mWindow.getStableFrameLw(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0); + assertInsetBy(mWindow.getContentFrameLw(), + NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0); + assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() { + addDisplayCutout(); + setRotation(ROTATION_90); + + mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mWindow.getContentFrameLw(), + DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_floatingInScreen() { + addDisplayCutout(); + + mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN; + mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY; + mWindow.mAttrs.width = DISPLAY_WIDTH; + mWindow.mAttrs.height = DISPLAY_HEIGHT; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + } + + @Test + public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() { + addDisplayCutout(); + setRotation(ROTATION_90); + + mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mWindow.getContentFrameLw(), + DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); + } + + @Test + public void layoutHint_appWindow() { + // Initialize DisplayFrames + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + + final Rect outFrame = new Rect(); + final Rect outContentInsets = new Rect(); + final Rect outStableInsets = new Rect(); + final Rect outOutsets = new Rect(); + final DisplayCutout.ParcelableWrapper outDisplayCutout = + new DisplayCutout.ParcelableWrapper(); + + mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, null, mFrames, + false /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets, + outDisplayCutout); + + assertThat(outFrame, is(mFrames.mUnrestricted)); + assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT))); + assertThat(outStableInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT))); + assertThat(outOutsets, is(new Rect())); + assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper())); + } + + @Test + public void layoutHint_appWindowInTask() { + // Initialize DisplayFrames + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + + final Rect taskBounds = new Rect(100, 100, 200, 200); + + final Rect outFrame = new Rect(); + final Rect outContentInsets = new Rect(); + final Rect outStableInsets = new Rect(); + final Rect outOutsets = new Rect(); + final DisplayCutout.ParcelableWrapper outDisplayCutout = + new DisplayCutout.ParcelableWrapper(); + + mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames, + false /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets, + outDisplayCutout); + + assertThat(outFrame, is(taskBounds)); + assertThat(outContentInsets, is(new Rect())); + assertThat(outStableInsets, is(new Rect())); + assertThat(outOutsets, is(new Rect())); + assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper())); + } + + @Test + public void layoutHint_appWindowInTask_outsideContentFrame() { + // Initialize DisplayFrames + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + + // Task is in the nav bar area (usually does not happen, but this is similar enough to the + // possible overlap with the IME) + final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1, + 200, mFrames.mContent.bottom + 10); + + final Rect outFrame = new Rect(); + final Rect outContentInsets = new Rect(); + final Rect outStableInsets = new Rect(); + final Rect outOutsets = new Rect(); + final DisplayCutout.ParcelableWrapper outDisplayCutout = + new DisplayCutout.ParcelableWrapper(); + + mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames, + true /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets, + outDisplayCutout); + + assertThat(outFrame, is(taskBounds)); + assertThat(outContentInsets, is(new Rect())); + assertThat(outStableInsets, is(new Rect())); + assertThat(outOutsets, is(new Rect())); + assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper())); + } + + /** + * Asserts that {@code actual} is inset by the given amounts from the full display rect. + * + * Convenience wrapper for when only the top and bottom inset are non-zero. + */ + private void assertInsetByTopBottom(Rect actual, int expectedInsetTop, + int expectedInsetBottom) { + assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom); + } + + /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */ + private void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop, + int expectedInsetRight, int expectedInsetBottom) { + assertEquals(new Rect(expectedInsetLeft, expectedInsetTop, + mFrames.mDisplayWidth - expectedInsetRight, + mFrames.mDisplayHeight - expectedInsetBottom), actual); + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java new file mode 100644 index 000000000000..07d5fea334cd --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java @@ -0,0 +1,202 @@ +/* + * 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.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; +import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; +import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; + +import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM; +import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.graphics.PixelFormat; +import android.platform.test.annotations.Presubmit; +import android.view.WindowManager; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +@Presubmit +public class DisplayPolicyTests extends WindowTestsBase { + + private WindowState createOpaqueFullscreen(boolean hasLightNavBar) { + final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "opaqueFullscreen"); + final WindowManager.LayoutParams attrs = win.mAttrs; + attrs.width = MATCH_PARENT; + attrs.height = MATCH_PARENT; + attrs.flags = + FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + attrs.format = PixelFormat.OPAQUE; + attrs.systemUiVisibility = attrs.subtreeSystemUiVisibility = win.mSystemUiVisibility = + hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0; + return win; + } + + private WindowState createDimmingDialogWindow(boolean canBeImTarget) { + final WindowState win = spy(createWindow(null, TYPE_APPLICATION, "dimmingDialog")); + final WindowManager.LayoutParams attrs = win.mAttrs; + attrs.width = WRAP_CONTENT; + attrs.height = WRAP_CONTENT; + attrs.flags = FLAG_DIM_BEHIND | (canBeImTarget ? 0 : FLAG_ALT_FOCUSABLE_IM); + attrs.format = PixelFormat.TRANSLUCENT; + when(win.isDimming()).thenReturn(true); + return win; + } + + private WindowState createInputMethodWindow(boolean visible, boolean drawNavBar, + boolean hasLightNavBar) { + final WindowState win = createWindow(null, TYPE_INPUT_METHOD, "inputMethod"); + final WindowManager.LayoutParams attrs = win.mAttrs; + attrs.width = MATCH_PARENT; + attrs.height = MATCH_PARENT; + attrs.flags = FLAG_NOT_FOCUSABLE | FLAG_LAYOUT_IN_SCREEN + | (drawNavBar ? FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS : 0); + attrs.format = PixelFormat.TRANSPARENT; + attrs.systemUiVisibility = attrs.subtreeSystemUiVisibility = win.mSystemUiVisibility = + hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0; + win.mHasSurface = visible; + return win; + } + + @Test + public void testChooseNavigationColorWindowLw() { + final WindowState opaque = createOpaqueFullscreen(false); + + final WindowState dimmingImTarget = createDimmingDialogWindow(true); + final WindowState dimmingNonImTarget = createDimmingDialogWindow(false); + + final WindowState visibleIme = createInputMethodWindow(true, true, false); + final WindowState invisibleIme = createInputMethodWindow(false, true, false); + final WindowState imeNonDrawNavBar = createInputMethodWindow(true, false, false); + + // If everything is null, return null + assertNull(null, DisplayPolicy.chooseNavigationColorWindowLw( + null, null, null, NAV_BAR_BOTTOM)); + + assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, opaque, null, NAV_BAR_BOTTOM)); + assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, dimmingImTarget, null, NAV_BAR_BOTTOM)); + assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, dimmingNonImTarget, null, NAV_BAR_BOTTOM)); + + assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( + null, null, visibleIme, NAV_BAR_BOTTOM)); + assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( + null, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM)); + assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( + null, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM)); + assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, opaque, visibleIme, NAV_BAR_BOTTOM)); + assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM)); + assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM)); + + assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, opaque, invisibleIme, NAV_BAR_BOTTOM)); + assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, opaque, invisibleIme, NAV_BAR_BOTTOM)); + assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, opaque, visibleIme, NAV_BAR_RIGHT)); + + // Only IME windows that have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS should be navigation color + // window. + assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, opaque, imeNonDrawNavBar, NAV_BAR_BOTTOM)); + assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM)); + assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM)); + } + + @Test + public void testUpdateLightNavigationBarLw() { + final WindowState opaqueDarkNavBar = createOpaqueFullscreen(false); + final WindowState opaqueLightNavBar = createOpaqueFullscreen(true); + + final WindowState dimming = createDimmingDialogWindow(false); + + final WindowState imeDrawDarkNavBar = createInputMethodWindow(true, true, false); + final WindowState imeDrawLightNavBar = createInputMethodWindow(true, true, true); + + assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, + DisplayPolicy.updateLightNavigationBarLw( + SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null, + null, null)); + + // Opaque top fullscreen window overrides SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR flag. + assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( + 0, opaqueDarkNavBar, opaqueDarkNavBar, null, opaqueDarkNavBar)); + assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( + SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, opaqueDarkNavBar, null, + opaqueDarkNavBar)); + assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, + DisplayPolicy.updateLightNavigationBarLw(0, opaqueLightNavBar, + opaqueLightNavBar, null, opaqueLightNavBar)); + assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, + DisplayPolicy.updateLightNavigationBarLw(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, + opaqueLightNavBar, opaqueLightNavBar, null, opaqueLightNavBar)); + + // Dimming window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR. + assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( + 0, opaqueDarkNavBar, dimming, null, dimming)); + assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( + 0, opaqueLightNavBar, dimming, null, dimming)); + assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( + SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, dimming, null, dimming)); + assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( + SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, null, dimming)); + assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( + SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, imeDrawLightNavBar, + dimming)); + + // IME window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR + assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( + SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null, imeDrawDarkNavBar, + imeDrawDarkNavBar)); + + // Even if the top fullscreen has SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, IME window wins. + assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( + SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, opaqueLightNavBar, + imeDrawDarkNavBar, imeDrawDarkNavBar)); + + // IME window should be able to use SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR. + assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, + DisplayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar, + opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java new file mode 100644 index 000000000000..1d63c57e6cfe --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java @@ -0,0 +1,184 @@ +/* + * 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.util.DisplayMetrics.DENSITY_DEFAULT; +import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM; +import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT; +import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT; +import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; + +import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.ContextWrapper; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.os.IBinder; +import android.testing.TestableResources; +import android.util.Pair; +import android.view.DisplayCutout; +import android.view.DisplayInfo; +import android.view.Gravity; +import android.view.View; +import android.view.WindowManagerGlobal; + +import com.android.internal.R; +import com.android.server.wm.utils.WmDisplayCutout; + +import org.junit.Before; + +public class DisplayPolicyTestsBase extends WindowTestsBase { + + static final int DISPLAY_WIDTH = 500; + static final int DISPLAY_HEIGHT = 1000; + static final int DISPLAY_DENSITY = 320; + + static final int STATUS_BAR_HEIGHT = 10; + static final int NAV_BAR_HEIGHT = 15; + static final int DISPLAY_CUTOUT_HEIGHT = 8; + + DisplayPolicy mDisplayPolicy; + + @Before + public void setUpBase() { + super.setUpBase(); + mDisplayPolicy = spy(mDisplayContent.getDisplayPolicy()); + + final TestContextWrapper context = + new TestContextWrapper(mDisplayPolicy.getSystemUiContext()); + final TestableResources resources = context.getResourceMocker(); + resources.addOverride(R.dimen.status_bar_height_portrait, STATUS_BAR_HEIGHT); + resources.addOverride(R.dimen.status_bar_height_landscape, STATUS_BAR_HEIGHT); + resources.addOverride(R.dimen.navigation_bar_height, NAV_BAR_HEIGHT); + resources.addOverride(R.dimen.navigation_bar_height_landscape, NAV_BAR_HEIGHT); + resources.addOverride(R.dimen.navigation_bar_width, NAV_BAR_HEIGHT); + when(mDisplayPolicy.getSystemUiContext()).thenReturn(context); + when(mDisplayPolicy.hasNavigationBar()).thenReturn(true); + + final int shortSizeDp = + Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY; + final int longSizeDp = + Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY; + mDisplayContent.getDisplayRotation().configure( + DISPLAY_WIDTH, DISPLAY_HEIGHT, shortSizeDp, longSizeDp); + mDisplayPolicy.configure(DISPLAY_WIDTH, DISPLAY_HEIGHT, shortSizeDp); + mDisplayPolicy.onConfigurationChanged(); + + mStatusBarWindow.mAttrs.gravity = Gravity.TOP; + addWindow(mStatusBarWindow); + mDisplayPolicy.mLastSystemUiFlags |= View.STATUS_BAR_TRANSPARENT; + + mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM; + addWindow(mNavBarWindow); + mDisplayPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT; + } + + void addWindow(WindowState win) { + mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs, true /* hasStatusBarPermission */); + assertEquals(WindowManagerGlobal.ADD_OKAY, + mDisplayPolicy.prepareAddWindowLw(win, win.mAttrs)); + win.mHasSurface = true; + } + + static Pair<DisplayInfo, WmDisplayCutout> displayInfoAndCutoutForRotation(int rotation, + boolean withDisplayCutout) { + final DisplayInfo info = new DisplayInfo(); + WmDisplayCutout cutout = null; + + final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270; + info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH; + info.logicalHeight = flippedDimensions ? DISPLAY_WIDTH : DISPLAY_HEIGHT; + info.rotation = rotation; + if (withDisplayCutout) { + cutout = WmDisplayCutout.computeSafeInsets( + displayCutoutForRotation(rotation), info.logicalWidth, + info.logicalHeight); + info.displayCutout = cutout.getDisplayCutout(); + } else { + info.displayCutout = null; + } + return Pair.create(info, cutout); + } + + private static DisplayCutout displayCutoutForRotation(int rotation) { + final RectF rectF = + new RectF(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT); + + final Matrix m = new Matrix(); + transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m); + m.mapRect(rectF); + + int pos = -1; + switch (rotation) { + case ROTATION_0: + pos = BOUNDS_POSITION_TOP; + break; + case ROTATION_90: + pos = BOUNDS_POSITION_LEFT; + break; + case ROTATION_180: + pos = BOUNDS_POSITION_BOTTOM; + break; + case ROTATION_270: + pos = BOUNDS_POSITION_RIGHT; + break; + } + + return DisplayCutout.fromBoundingRect((int) rectF.left, (int) rectF.top, + (int) rectF.right, (int) rectF.bottom, pos); + } + + static class TestContextWrapper extends ContextWrapper { + private final TestableResources mResourceMocker; + + TestContextWrapper(Context targetContext) { + super(targetContext); + mResourceMocker = new TestableResources(targetContext.getResources()); + } + + @Override + public int checkPermission(String permission, int pid, int uid) { + return PackageManager.PERMISSION_GRANTED; + } + + @Override + public int checkPermission(String permission, int pid, int uid, IBinder callerToken) { + return PackageManager.PERMISSION_GRANTED; + } + + @Override + public Resources getResources() { + return mResourceMocker.getResources(); + } + + TestableResources getResourceMocker() { + return mResourceMocker; + } + } + +} diff --git a/services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java new file mode 100644 index 000000000000..a04bf16eaf1c --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java @@ -0,0 +1,91 @@ +/* + * 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 androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@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/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java index fe5fc068a36b..ee3bba73cd1e 100644 --- a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -123,6 +123,8 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { final AppWindowToken hiddenAppWindow = createAppWindowToken(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); hiddenAppWindow.setHidden(true); + mDisplayContent.getConfiguration().windowConfiguration.setRotation( + mDisplayContent.getRotation()); mController.initialize(mDisplayContent, ACTIVITY_TYPE_HOME, new SparseBooleanArray()); // Ensure that we are animating the target activity as well diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java index 0165e7d4e84b..7b542cb4f2f7 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -26,12 +26,10 @@ import android.annotation.Nullable; import android.content.Context; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; -import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.proto.ProtoOutputStream; -import android.view.DisplayCutout; import android.view.IWindow; import android.view.IWindowManager; import android.view.KeyEvent; @@ -48,7 +46,6 @@ import java.util.function.Supplier; class TestWindowManagerPolicy implements WindowManagerPolicy { private final Supplier<WindowManagerService> mWmSupplier; - int rotationToReport = 0; boolean keyguardShowingAndNotOccluded = false; private Runnable mRunnableWhenAddingSplashScreen; @@ -81,11 +78,6 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs, - boolean hasStatusBarServicePermission) { - } - - @Override public void adjustConfigurationLw(Configuration config, int keyboardPresence, int navigationPresence) { } @@ -96,30 +88,6 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode, - int displayId, DisplayCutout displayCutout) { - return 0; - } - - @Override - public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode, - int displayId, DisplayCutout displayCutout) { - return 0; - } - - @Override - public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode, - int displayId, DisplayCutout displayCutout) { - return 0; - } - - @Override - public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode, - int displayId, DisplayCutout displayCutout) { - return 0; - } - - @Override public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) { return attrs.type == TYPE_STATUS_BAR; } @@ -166,28 +134,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public int prepareAddWindowLw(WindowState win, - WindowManager.LayoutParams attrs) { - return 0; - } - - @Override - public void removeWindowLw(WindowState win) { - } - - @Override - public int selectAnimationLw(WindowState win, int transit) { - return 0; - } - - @Override - public void selectRotationAnimationLw(int[] anim) { - } - - @Override - public boolean validateRotationAnimationLw(int exitAnimId, int enterAnimId, - boolean forceDefault) { - return false; + public void setKeyguardCandidateLw(WindowState win) { } @Override @@ -222,32 +169,11 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public int getSystemDecorLayerLw() { - return 0; + public void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget) { } @Override - public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) { - } - - @Override - public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs, - WindowState attached, WindowState imeTarget) { - } - - @Override - public int finishPostLayoutPolicyLw() { - return 0; - } - - @Override - public boolean allowAppAnimationsLw() { - return false; - } - - @Override - public int focusChangedLw(WindowState lastFocus, WindowState newFocus) { - return 0; + public void setAllowLockscreenWhenOn(int displayId, boolean allow) { } @Override @@ -349,11 +275,6 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public boolean isShowingDreamLw() { - return false; - } - - @Override public void onKeyguardOccludedChangedLw(boolean occluded) { } @@ -399,11 +320,6 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public int adjustSystemUiVisibilityLw(int visibility) { - return 0; - } - - @Override public boolean hasNavigationBar() { return false; } @@ -421,65 +337,38 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public void setCurrentUserLw(int newUserId) { - } - - @Override - public void setSwitchingUser(boolean switching) { - } - - @Override - public void writeToProto(ProtoOutputStream proto, long fieldId) { - } - - @Override - public void dump(String prefix, PrintWriter writer, String[] args) { - } - - @Override - public boolean isTopLevelWindow(int windowType) { + public boolean isUserSetupComplete() { return false; } @Override - public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) { + public int getUiMode() { + return 0; } @Override - public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight, - DisplayCutout cutout, Rect outInsets) { + public void setCurrentUserLw(int newUserId) { } @Override - public boolean isNavBarForcedShownLw(WindowState win) { - return false; + public void setSwitchingUser(boolean switching) { } - @NavigationBarPosition @Override - public int getNavBarPosition() { - return NAV_BAR_BOTTOM; + public void writeToProto(ProtoOutputStream proto, long fieldId) { } @Override - public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight, - DisplayCutout cutout, Rect outInsets) { + public void dump(String prefix, PrintWriter writer, String[] args) { } @Override - public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth, - int displayHeight, int displayRotation) { + public boolean isTopLevelWindow(int windowType) { return false; } @Override - public void onConfigurationChanged(DisplayContentInfo displayContentInfo) { - } - - @Override - public boolean shouldRotateSeamlessly(DisplayRotation displayRotation, int oldRotation, - int newRotation) { - return false; + public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) { } @Override @@ -508,10 +397,6 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public void onLockTaskStateChangedLw(int lockTaskState) { - } - - @Override public boolean setAodShowing(boolean aodShowing) { return false; } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java index b0c8d8bfee1b..227eb00be7f7 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java @@ -113,9 +113,6 @@ public class WindowFrameTests extends WindowTestsBase { @Before public void setUp() throws Exception { - // Just any non zero value. - mWm.mSystemDecorLayer = 10000; - mWindowToken = WindowTestUtils.createTestAppWindowToken( mWm.getDefaultDisplayContentLocked()); mStubStack = new TaskStack(mWm, 0, null); diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java index d6fea096d7ba..60c045991d69 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java @@ -28,19 +28,15 @@ import static org.mockito.Mockito.anyFloat; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.content.ComponentName; -import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; -import android.view.Display; import android.view.IApplicationToken; import android.view.IWindow; -import android.view.Surface; import android.view.SurfaceControl.Transaction; import android.view.WindowManager; @@ -53,37 +49,6 @@ import org.mockito.invocation.InvocationOnMock; public class WindowTestUtils { private static int sNextTaskId = 0; - /** An extension of {@link DisplayContent} to gain package scoped access. */ - public static class TestDisplayContent extends DisplayContent { - - private TestDisplayContent(Display display, WindowManagerService service, - DisplayWindowController controller) { - super(display, service, controller); - } - - /** Create a mocked default {@link DisplayContent}. */ - public static TestDisplayContent create(Context context) { - final TestDisplayContent displayContent = mock(TestDisplayContent.class); - displayContent.isDefaultDisplay = true; - - final DisplayPolicy displayPolicy = mock(DisplayPolicy.class); - when(displayPolicy.navigationBarCanMove()).thenReturn(true); - when(displayPolicy.hasNavigationBar()).thenReturn(true); - - final DisplayRotation displayRotation = new DisplayRotation( - mock(WindowManagerService.class), displayContent, displayPolicy, - context, new Object()); - displayRotation.mPortraitRotation = Surface.ROTATION_0; - displayRotation.mLandscapeRotation = Surface.ROTATION_90; - displayRotation.mUpsideDownRotation = Surface.ROTATION_180; - displayRotation.mSeascapeRotation = Surface.ROTATION_270; - - when(displayContent.getDisplayRotation()).thenReturn(displayRotation); - - return displayContent; - } - } - /** * Creates a mock instance of {@link StackWindowController}. */ diff --git a/services/tests/servicestests/test-apps/Android.mk b/services/tests/servicestests/test-apps/Android.mk deleted file mode 100644 index 5053e7d64389..000000000000 --- a/services/tests/servicestests/test-apps/Android.mk +++ /dev/null @@ -1 +0,0 @@ -include $(call all-subdir-makefiles) diff --git a/services/tests/servicestests/test-apps/ConnTestApp/Android.bp b/services/tests/servicestests/test-apps/ConnTestApp/Android.bp new file mode 100644 index 000000000000..13e664446418 --- /dev/null +++ b/services/tests/servicestests/test-apps/ConnTestApp/Android.bp @@ -0,0 +1,31 @@ +// 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. + +android_test_helper_app { + name: "ConnTestApp", + + test_suites: ["device-tests"], + + static_libs: ["servicestests-aidl"], + srcs: ["**/*.java"], + + platform_apis: true, + certificate: "platform", + dex_preopt: { + enabled: false, + }, + optimize: { + enabled: false, + }, +} diff --git a/services/tests/servicestests/test-apps/ConnTestApp/Android.mk b/services/tests/servicestests/test-apps/ConnTestApp/Android.mk deleted file mode 100644 index 18b8c2d63313..000000000000 --- a/services/tests/servicestests/test-apps/ConnTestApp/Android.mk +++ /dev/null @@ -1,32 +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. - -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_COMPATIBILITY_SUITE := device-tests - -LOCAL_STATIC_JAVA_LIBRARIES := servicestests-aidl -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := ConnTestApp -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform -LOCAL_DEX_PREOPT := false -LOCAL_PROGUARD_ENABLED := disabled - -include $(BUILD_PACKAGE)
\ No newline at end of file diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.bp b/services/tests/servicestests/test-apps/JobTestApp/Android.bp new file mode 100644 index 000000000000..ae1eca7ba707 --- /dev/null +++ b/services/tests/servicestests/test-apps/JobTestApp/Android.bp @@ -0,0 +1,30 @@ +// 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. + +android_test_helper_app { + name: "JobTestApp", + + sdk_version: "current", + + test_suites: ["device-tests"], + + srcs: ["**/*.java"], + + dex_preopt: { + enabled: false, + }, + optimize: { + enabled: false, + }, +} diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.mk b/services/tests/servicestests/test-apps/JobTestApp/Android.mk deleted file mode 100644 index 7893c913d19d..000000000000 --- a/services/tests/servicestests/test-apps/JobTestApp/Android.mk +++ /dev/null @@ -1,30 +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. - -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests -LOCAL_SDK_VERSION := current - -LOCAL_COMPATIBILITY_SUITE := device-tests - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := JobTestApp -LOCAL_DEX_PREOPT := false -LOCAL_PROGUARD_ENABLED := disabled - -include $(BUILD_PACKAGE)
\ No newline at end of file diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp b/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp new file mode 100644 index 000000000000..7257275971ab --- /dev/null +++ b/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp @@ -0,0 +1,39 @@ +// 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. + +android_test_helper_app { + name: "SuspendTestApp", + + test_suites: ["device-tests"], + + static_libs: [ + "androidx.test.runner", + "ub-uiautomator", + ], + + srcs: [ + "**/*.java", + ":servicestests-SuspendTestApp-files", + ], + + dex_preopt: { + enabled: false, + }, + optimize: { + enabled: false, + }, + + platform_apis: true, + +} diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk deleted file mode 100644 index ab222b941e94..000000000000 --- a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk +++ /dev/null @@ -1,34 +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. - -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_COMPATIBILITY_SUITE := device-tests - -LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.runner ub-uiautomator - -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_SRC_FILES += ../../src/com/android/server/pm/SuspendPackagesTest.java - -LOCAL_PACKAGE_NAME := SuspendTestApp -LOCAL_DEX_PREOPT := false -LOCAL_PROGUARD_ENABLED := disabled - -LOCAL_PRIVATE_PLATFORM_APIS := true - -include $(BUILD_PACKAGE)
\ No newline at end of file diff --git a/services/tests/shortcutmanagerutils/Android.bp b/services/tests/shortcutmanagerutils/Android.bp new file mode 100644 index 000000000000..c2cb688175b2 --- /dev/null +++ b/services/tests/shortcutmanagerutils/Android.bp @@ -0,0 +1,26 @@ +// 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. + +java_library { + name: "ShortcutManagerTestUtils", + + srcs: ["src/**/*.java"], + + libs: [ + "mockito-target", + "android.test.runner.stubs", + ], + + sdk_version: "test_current", +} diff --git a/services/tests/shortcutmanagerutils/Android.mk b/services/tests/shortcutmanagerutils/Android.mk deleted file mode 100644 index 019bcbd37628..000000000000 --- a/services/tests/shortcutmanagerutils/Android.mk +++ /dev/null @@ -1,32 +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. - -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - $(call all-java-files-under, src) - -LOCAL_JAVA_LIBRARIES := \ - mockito-target \ - android.test.runner.stubs - -LOCAL_MODULE_TAGS := optional - -LOCAL_MODULE := ShortcutManagerTestUtils - -LOCAL_SDK_VERSION := test_current - -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp new file mode 100644 index 000000000000..ca8cc0d89201 --- /dev/null +++ b/services/tests/uiservicestests/Android.bp @@ -0,0 +1,58 @@ +//######################################################################## +// Build FrameworksUiServicesTests package +//######################################################################## + +android_test { + name: "FrameworksUiServicesTests", + + // Include test java files + srcs: [ + "src/**/*.java", + ], + + static_libs: [ + "services.accessibility", + "services.core", + "services.devicepolicy", + "services.net", + "services.usage", + "guava", + "android-support-test", + "mockito-target-inline-minus-junit4", + "platform-test-annotations", + "testables", + ], + + libs: [ + "android.test.runner", + "android.test.base", + ], + + dxflags: ["--multi-dex"], + + platform_apis: true, + test_suites: ["device-tests"], + + certificate: "platform", + + compile_multilib: "both", + + // These are not normally accessible from apps so they must be explicitly included. + jni_libs: [ + "libdexmakerjvmtiagent", + "libmultiplejvmtiagentsinterferenceagent", + "libbacktrace", + "libbase", + "libbinder", + "libbinderthreadstate", + "libc++", + "libcutils", + "liblog", + "liblzma", + "libnativehelper", + "libnetdaidl", + "libui", + "libunwindstack", + "libutils", + ], +} diff --git a/services/tests/uiservicestests/Android.mk b/services/tests/uiservicestests/Android.mk deleted file mode 100644 index f3f43558115b..000000000000 --- a/services/tests/uiservicestests/Android.mk +++ /dev/null @@ -1,61 +0,0 @@ -######################################################################### -# Build FrameworksUiServicesTests package -######################################################################### - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# We only want this apk build for tests. -LOCAL_MODULE_TAGS := tests - -# Include test java files and source from notifications package. -LOCAL_SRC_FILES := $(call all-java-files-under, src) \ - $(call all-java-files-under, ../../core/java/com/android/server/notification) \ - $(call all-java-files-under, ../../core/java/com/android/server/slice) \ - -LOCAL_STATIC_JAVA_LIBRARIES := \ - services.accessibility \ - services.core \ - services.devicepolicy \ - services.net \ - services.usage \ - guava \ - android-support-test \ - mockito-target-inline-minus-junit4 \ - platform-test-annotations \ - testables - -LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base - -LOCAL_JACK_FLAGS := --multi-dex native -LOCAL_DX_FLAGS := --multi-dex - -LOCAL_PACKAGE_NAME := FrameworksUiServicesTests -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_COMPATIBILITY_SUITE := device-tests - -LOCAL_CERTIFICATE := platform - -LOCAL_MULTILIB := both - -# These are not normally accessible from apps so they must be explicitly included. -LOCAL_JNI_SHARED_LIBRARIES := \ - libdexmakerjvmtiagent \ - libmultiplejvmtiagentsinterferenceagent \ - libbacktrace \ - libbase \ - libbinder \ - libbinderthreadstate \ - libc++ \ - libcutils \ - liblog \ - liblzma \ - libnativehelper \ - libnetdaidl \ - libui \ - libunwindstack \ - libutils - -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk - -include $(BUILD_PACKAGE) 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 8bf14cc23907..f11492aa59e5 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -283,6 +283,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false); when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner); + when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG}); // write to a test file; the system file isn't readable from tests mFile = new File(mContext.getCacheDir(), "test.xml"); @@ -1730,7 +1731,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testGetNotificationChannelFromPrivilegedListener_assistant_noAccess() throws Exception { + public void testGetNotificationChannelFromPrivilegedListener_assistant_noAccess() + throws Exception { mService.setPreferencesHelper(mPreferencesHelper); when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(new ArrayList<>()); when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(false); @@ -3462,11 +3464,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { ApplicationInfo info = new ApplicationInfo(); info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT; when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(info); + when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{"any"}); - assertTrue(mService.isCallerInstantApp("any", 45770, 0)); + assertTrue(mService.isCallerInstantApp(45770, 0)); info.privateFlags = 0; - assertFalse(mService.isCallerInstantApp("any", 575370, 0)); + assertFalse(mService.isCallerInstantApp(575370, 0)); } @Test @@ -3475,8 +3478,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT; when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(10))).thenReturn(info); when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(null); + when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{"any"}); - assertTrue(mService.isCallerInstantApp("any", 68638450, 10)); + assertTrue(mService.isCallerInstantApp(68638450, 10)); } @Test diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp new file mode 100644 index 000000000000..cdba9a10fa82 --- /dev/null +++ b/services/tests/wmtests/Android.bp @@ -0,0 +1,52 @@ +//######################################################################## +// Build WmTests package +//######################################################################## + +android_test { + name: "WmTests", + + // We only want this apk build for tests. + + // Include all test java files. + srcs: [ + "src/**/*.java", + ], + + static_libs: [ + "frameworks-base-testutils", + "services.core", + "androidx.test.runner", + "androidx.test.rules", + "mockito-target-extended-minus-junit4", + "platform-test-annotations", + "servicestests-utils", + "truth-prebuilt", + "testables", + "ub-uiautomator", + "hamcrest-library", + ], + + libs: [ + "android.test.mock", + "android.test.base", + "android.test.runner", + ], + + // These are not normally accessible from apps so they must be explicitly included. + jni_libs: [ + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + ], + + platform_apis: true, + test_suites: ["device-tests"], + + certificate: "platform", + + dxflags: ["--multi-dex"], + + optimize: { + enabled: false, + }, + +} diff --git a/services/tests/wmtests/Android.mk b/services/tests/wmtests/Android.mk deleted file mode 100644 index 67c2860511d1..000000000000 --- a/services/tests/wmtests/Android.mk +++ /dev/null @@ -1,53 +0,0 @@ -######################################################################### -# Build WmTests package -######################################################################### - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# We only want this apk build for tests. -LOCAL_MODULE_TAGS := tests - -# Include all test java files. -LOCAL_SRC_FILES := \ - $(call all-java-files-under, src) \ - $(call all-java-files-under, ../servicestests/utils) \ - -LOCAL_STATIC_JAVA_LIBRARIES := \ - frameworks-base-testutils \ - services.core \ - androidx.test.runner \ - androidx.test.rules \ - mockito-target-extended-minus-junit4 \ - platform-test-annotations \ - truth-prebuilt \ - testables \ - ub-uiautomator \ - hamcrest-library - -LOCAL_JAVA_LIBRARIES := \ - android.test.mock \ - android.test.base \ - android.test.runner \ - -# These are not normally accessible from apps so they must be explicitly included. -LOCAL_JNI_SHARED_LIBRARIES := \ - libdexmakerjvmtiagent \ - libstaticjvmtiagent \ - -LOCAL_PACKAGE_NAME := WmTests -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_COMPATIBILITY_SUITE := device-tests - -LOCAL_CERTIFICATE := platform - -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk - -LOCAL_JACK_FLAGS := --multi-dex native -LOCAL_DX_FLAGS := --multi-dex - -LOCAL_PROGUARD_ENABLED := disabled - -include $(BUILD_PACKAGE) - -include $(call all-makefiles-under, $(LOCAL_PATH)) diff --git a/services/tests/wmtests/src/com/android/server/policy/FakeWindowState.java b/services/tests/wmtests/src/com/android/server/policy/FakeWindowState.java deleted file mode 100644 index d4f2b061d570..000000000000 --- a/services/tests/wmtests/src/com/android/server/policy/FakeWindowState.java +++ /dev/null @@ -1,260 +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.policy; - -import android.graphics.Rect; -import android.util.proto.ProtoOutputStream; -import android.view.Display; -import android.view.IApplicationToken; -import android.view.WindowManager; - -import com.android.server.wm.WindowFrames; - -public class FakeWindowState implements WindowManagerPolicy.WindowState { - - private WindowFrames mWindowFrames = new WindowFrames(); - - public WindowManager.LayoutParams attrs; - public int displayId; - public boolean isVoiceInteraction; - public boolean inMultiWindowMode; - public boolean visible = true; - public int surfaceLayer = 1; - public boolean isDimming = false; - - public boolean policyVisible = true; - - @Override - public int getOwningUid() { - throw new UnsupportedOperationException("not implemented"); - } - - @Override - public String getOwningPackage() { - throw new UnsupportedOperationException("not implemented"); - } - - @Override - public void computeFrameLw() { - } - - @Override - public Rect getFrameLw() { - return mWindowFrames.mParentFrame; - } - - @Override - public Rect getDisplayFrameLw() { - return mWindowFrames.mDisplayFrame; - } - - @Override - public Rect getOverscanFrameLw() { - return mWindowFrames.mOverscanFrame; - } - - @Override - public Rect getContentFrameLw() { - return mWindowFrames.mContentFrame; - } - - @Override - public Rect getVisibleFrameLw() { - return mWindowFrames.mVisibleFrame; - } - - public Rect getStableFrame() { - return mWindowFrames.mStableFrame; - } - - public Rect getDecorFrame() { - return mWindowFrames.mDecorFrame; - } - - @Override - public boolean getGivenInsetsPendingLw() { - throw new UnsupportedOperationException("not implemented"); - } - - @Override - public Rect getGivenContentInsetsLw() { - throw new UnsupportedOperationException("not implemented"); - } - - @Override - public Rect getGivenVisibleInsetsLw() { - throw new UnsupportedOperationException("not implemented"); - } - - @Override - public WindowManager.LayoutParams getAttrs() { - return attrs; - } - - @Override - public boolean getNeedsMenuLw(WindowManagerPolicy.WindowState bottom) { - throw new UnsupportedOperationException("not implemented"); - } - - @Override - public int getSystemUiVisibility() { - return attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility; - } - - @Override - public int getSurfaceLayer() { - return surfaceLayer; - } - - @Override - public int getBaseType() { - throw new UnsupportedOperationException("not implemented"); - } - - @Override - public IApplicationToken getAppToken() { - throw new UnsupportedOperationException("not implemented"); - } - - @Override - public boolean isVoiceInteraction() { - return isVoiceInteraction; - } - - @Override - public boolean hasAppShownWindows() { - throw new UnsupportedOperationException("not implemented"); - } - - @Override - public boolean isVisibleLw() { - return visible && policyVisible; - } - - @Override - public boolean isDisplayedLw() { - return isVisibleLw(); - } - - @Override - public boolean isAnimatingLw() { - return false; - } - - @Override - public boolean canAffectSystemUiFlags() { - throw new UnsupportedOperationException("not implemented"); - } - - @Override - public boolean isGoneForLayoutLw() { - throw new UnsupportedOperationException("not implemented"); - } - - @Override - public boolean isDrawnLw() { - return true; - } - - @Override - public boolean hasDrawnLw() { - return true; - } - - @Override - public boolean hideLw(boolean doAnimation) { - if (!policyVisible) { - return false; - } - policyVisible = false; - return true; - } - - @Override - public boolean showLw(boolean doAnimation) { - if (policyVisible) { - return false; - } - policyVisible = true; - return true; - } - - @Override - public boolean isAlive() { - return true; - } - - @Override - public boolean isDefaultDisplay() { - return displayId == Display.DEFAULT_DISPLAY; - } - - @Override - public boolean isDimming() { - return isDimming; - } - - @Override - public int getWindowingMode() { - throw new UnsupportedOperationException("not implemented"); - } - - @Override - public boolean isInMultiWindowMode() { - return inMultiWindowMode; - } - - @Override - public int getRotationAnimationHint() { - throw new UnsupportedOperationException("not implemented"); - } - - @Override - public boolean isInputMethodWindow() { - throw new UnsupportedOperationException("not implemented"); - } - - @Override - public int getDisplayId() { - return displayId; - } - - @Override - public boolean canAcquireSleepToken() { - throw new UnsupportedOperationException("not implemented"); - } - - @Override - public boolean canReceiveKeys() { - return false; - } - - @Override - public void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) { - throw new UnsupportedOperationException("not implemented"); - } - - @Override - public WindowFrames getWindowFrames() { - return mWindowFrames; - } - - @Override - public boolean isInputMethodTarget() { - return false; - } -} diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java deleted file mode 100644 index e8f767a873d3..000000000000 --- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java +++ /dev/null @@ -1,395 +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.policy; - -import static android.view.Surface.ROTATION_270; -import static android.view.Surface.ROTATION_90; -import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; -import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; -import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; -import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; - -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; - -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.platform.test.annotations.Presubmit; -import android.view.DisplayCutout; -import android.view.WindowManager; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Test; - -/** - * Build/Install/Run: - * atest WmTests:PhoneWindowManagerLayoutTest - */ -@SmallTest -@Presubmit -public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase { - - private FakeWindowState mAppWindow; - - @Before - public void setUp() throws Exception { - mAppWindow = new FakeWindowState(); - mAppWindow.attrs = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT, - TYPE_APPLICATION, - FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, - PixelFormat.TRANSLUCENT); - - addStatusBar(); - addNavigationBar(); - } - - @Test - public void layoutWindowLw_appDrawsBars() { - mAppWindow.attrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - mPolicy.addWindow(mAppWindow); - - mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mPolicy.layoutWindowLw(mAppWindow, null, mFrames); - - assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0); - assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0); - assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, 0, 0); - } - - @Test - public void layoutWindowLw_appWontDrawBars() { - mAppWindow.attrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - mPolicy.addWindow(mAppWindow); - - mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mPolicy.layoutWindowLw(mAppWindow, null, mFrames); - - assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT); - } - - @Test - public void layoutWindowLw_appWontDrawBars_forceStatus() { - mAppWindow.attrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - mAppWindow.attrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; - mPolicy.addWindow(mAppWindow); - - mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mPolicy.layoutWindowLw(mAppWindow, null, mFrames); - - assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT); - } - - @Test - public void addingWindow_doesNotTamperWithSysuiFlags() { - mAppWindow.attrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - mPolicy.addWindow(mAppWindow); - - assertEquals(0, mAppWindow.attrs.systemUiVisibility); - assertEquals(0, mAppWindow.attrs.subtreeSystemUiVisibility); - } - - @Test - public void layoutWindowLw_withDisplayCutout() { - addDisplayCutout(); - - mPolicy.addWindow(mAppWindow); - - mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mPolicy.layoutWindowLw(mAppWindow, null, mFrames); - - assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0); - assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0); - assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, 0); - } - - @Test - public void layoutWindowLw_withhDisplayCutout_never() { - addDisplayCutout(); - - mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; - mPolicy.addWindow(mAppWindow); - - mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mPolicy.layoutWindowLw(mAppWindow, null, mFrames); - - assertInsetByTopBottom(mAppWindow.getFrameLw(), STATUS_BAR_HEIGHT, 0); - assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0); - assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0); - } - - @Test - public void layoutWindowLw_withDisplayCutout_layoutFullscreen() { - addDisplayCutout(); - - mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; - mPolicy.addWindow(mAppWindow); - - mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mPolicy.layoutWindowLw(mAppWindow, null, mFrames); - - assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0); - assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0); - assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, 0, 0); - } - - @Test - public void layoutWindowLw_withDisplayCutout_fullscreen() { - addDisplayCutout(); - - mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN; - mPolicy.addWindow(mAppWindow); - - mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mPolicy.layoutWindowLw(mAppWindow, null, mFrames); - - assertInsetByTopBottom(mAppWindow.getFrameLw(), STATUS_BAR_HEIGHT, 0); - assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0); - assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0); - } - - @Test - public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() { - addDisplayCutout(); - - mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN; - mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - mPolicy.addWindow(mAppWindow); - - mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mPolicy.layoutWindowLw(mAppWindow, null, mFrames); - - assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0); - assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0); - assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, 0); - } - - - @Test - public void layoutWindowLw_withDisplayCutout_landscape() { - addDisplayCutout(); - setRotation(ROTATION_90); - mPolicy.addWindow(mAppWindow); - - mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mPolicy.layoutWindowLw(mAppWindow, null, mFrames); - - assertInsetBy(mAppWindow.getFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); - assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); - assertInsetBy(mAppWindow.getContentFrameLw(), - DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); - assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0); - assertInsetBy(mAppWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); - } - - @Test - public void layoutWindowLw_withDisplayCutout_seascape() { - addDisplayCutout(); - setRotation(ROTATION_270); - mPolicy.addWindow(mAppWindow); - - mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mPolicy.layoutWindowLw(mAppWindow, null, mFrames); - - assertInsetBy(mAppWindow.getFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0); - assertInsetBy(mAppWindow.getStableFrame(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0); - assertInsetBy(mAppWindow.getContentFrameLw(), - NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0); - assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0); - assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0); - } - - @Test - public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() { - addDisplayCutout(); - setRotation(ROTATION_90); - - mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; - mPolicy.addWindow(mAppWindow); - - mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mPolicy.layoutWindowLw(mAppWindow, null, mFrames); - - assertInsetBy(mAppWindow.getFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); - assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); - assertInsetBy(mAppWindow.getContentFrameLw(), - DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); - assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0); - } - - @Test - public void layoutWindowLw_withDisplayCutout_floatingInScreen() { - addDisplayCutout(); - - mAppWindow.attrs.flags = FLAG_LAYOUT_IN_SCREEN; - mAppWindow.attrs.type = TYPE_APPLICATION_OVERLAY; - mAppWindow.attrs.width = DISPLAY_WIDTH; - mAppWindow.attrs.height = DISPLAY_HEIGHT; - mPolicy.addWindow(mAppWindow); - - mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mPolicy.layoutWindowLw(mAppWindow, null, mFrames); - - assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - } - - @Test - public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() { - addDisplayCutout(); - setRotation(ROTATION_90); - - mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; - mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - mPolicy.addWindow(mAppWindow); - - mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mPolicy.layoutWindowLw(mAppWindow, null, mFrames); - - assertInsetBy(mAppWindow.getFrameLw(), 0, 0, 0, 0); - assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); - assertInsetBy(mAppWindow.getContentFrameLw(), - DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); - assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0); - } - - @Test - public void layoutHint_screenDecorWindow() { - addDisplayCutout(); - mAppWindow.attrs.privateFlags |= PRIVATE_FLAG_IS_SCREEN_DECOR; - - mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - - final Rect frame = new Rect(); - final Rect content = new Rect(); - final Rect stable = new Rect(); - final Rect outsets = new Rect(); - final DisplayCutout.ParcelableWrapper cutout = new DisplayCutout.ParcelableWrapper(); - mPolicy.getLayoutHintLw(mAppWindow.attrs, null /* taskBounds */, mFrames, - false /* floatingStack */, frame, content, stable, outsets, cutout); - - assertThat(frame, equalTo(mFrames.mUnrestricted)); - assertThat(content, equalTo(new Rect())); - assertThat(stable, equalTo(new Rect())); - assertThat(outsets, equalTo(new Rect())); - assertThat(cutout.get(), equalTo(DisplayCutout.NO_CUTOUT)); - } - - @Test - public void layoutHint_appWindow() { - // Initialize DisplayFrames - mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - - final Rect outFrame = new Rect(); - final Rect outContentInsets = new Rect(); - final Rect outStableInsets = new Rect(); - final Rect outOutsets = new Rect(); - final DisplayCutout.ParcelableWrapper outDisplayCutout = - new DisplayCutout.ParcelableWrapper(); - - mPolicy.getLayoutHintLw(mAppWindow.attrs, null, mFrames, false /* floatingStack */, - outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout); - - assertThat(outFrame, is(mFrames.mUnrestricted)); - assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT))); - assertThat(outStableInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT))); - assertThat(outOutsets, is(new Rect())); - assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper())); - } - - @Test - public void layoutHint_appWindowInTask() { - // Initialize DisplayFrames - mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - - final Rect taskBounds = new Rect(100, 100, 200, 200); - - final Rect outFrame = new Rect(); - final Rect outContentInsets = new Rect(); - final Rect outStableInsets = new Rect(); - final Rect outOutsets = new Rect(); - final DisplayCutout.ParcelableWrapper outDisplayCutout = - new DisplayCutout.ParcelableWrapper(); - - mPolicy.getLayoutHintLw(mAppWindow.attrs, taskBounds, mFrames, false /* floatingStack */, - outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout); - - assertThat(outFrame, is(taskBounds)); - assertThat(outContentInsets, is(new Rect())); - assertThat(outStableInsets, is(new Rect())); - assertThat(outOutsets, is(new Rect())); - assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper())); - } - - @Test - public void layoutHint_appWindowInTask_outsideContentFrame() { - // Initialize DisplayFrames - mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - - // Task is in the nav bar area (usually does not happen, but this is similar enough to the - // possible overlap with the IME) - final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1, - 200, mFrames.mContent.bottom + 10); - - final Rect outFrame = new Rect(); - final Rect outContentInsets = new Rect(); - final Rect outStableInsets = new Rect(); - final Rect outOutsets = new Rect(); - final DisplayCutout.ParcelableWrapper outDisplayCutout = - new DisplayCutout.ParcelableWrapper(); - - mPolicy.getLayoutHintLw(mAppWindow.attrs, taskBounds, mFrames, true /* floatingStack */, - outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout); - - assertThat(outFrame, is(taskBounds)); - assertThat(outContentInsets, is(new Rect())); - assertThat(outStableInsets, is(new Rect())); - assertThat(outOutsets, is(new Rect())); - assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper())); - } -} diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTest.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTest.java deleted file mode 100644 index 6c44d655459e..000000000000 --- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTest.java +++ /dev/null @@ -1,255 +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.policy; - -import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -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.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; -import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; -import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; - -import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM; -import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT; -import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import android.graphics.PixelFormat; -import android.platform.test.annotations.Presubmit; -import android.view.WindowManager; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; - -/** - * Build/Install/Run: - * atest WmTests:PhoneWindowManagerTest - */ -@SmallTest -@Presubmit -public class PhoneWindowManagerTest { - - private static FakeWindowState createOpaqueFullscreen(boolean hasLightNavBar) { - final FakeWindowState state = new FakeWindowState(); - state.attrs = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT, - TYPE_BASE_APPLICATION, - FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, - PixelFormat.OPAQUE); - state.attrs.subtreeSystemUiVisibility = - hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0; - return state; - } - - private static FakeWindowState createDimmingDialogWindow(boolean canBeImTarget) { - final FakeWindowState state = new FakeWindowState(); - state.attrs = new WindowManager.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, - TYPE_APPLICATION, - FLAG_DIM_BEHIND | (canBeImTarget ? 0 : FLAG_ALT_FOCUSABLE_IM), - PixelFormat.TRANSLUCENT); - state.isDimming = true; - return state; - } - - private static FakeWindowState createInputMethodWindow(boolean visible, boolean drawNavBar, - boolean hasLightNavBar) { - final FakeWindowState state = new FakeWindowState(); - state.attrs = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT, - TYPE_INPUT_METHOD, - FLAG_NOT_FOCUSABLE | FLAG_LAYOUT_IN_SCREEN - | (drawNavBar ? FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS : 0), - PixelFormat.TRANSPARENT); - state.attrs.subtreeSystemUiVisibility = - hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0; - state.visible = visible; - state.policyVisible = visible; - return state; - } - - - @Test - public void testChooseNavigationColorWindowLw() { - final FakeWindowState opaque = createOpaqueFullscreen(false); - - final FakeWindowState dimmingImTarget = createDimmingDialogWindow(true); - final FakeWindowState dimmingNonImTarget = createDimmingDialogWindow(false); - - final FakeWindowState visibleIme = createInputMethodWindow(true, true, false); - final FakeWindowState invisibleIme = createInputMethodWindow(false, true, false); - final FakeWindowState imeNonDrawNavBar = createInputMethodWindow(true, false, false); - - // If everything is null, return null - assertNull(null, PhoneWindowManager.chooseNavigationColorWindowLw( - null, null, null, NAV_BAR_BOTTOM)); - - assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw( - opaque, opaque, null, NAV_BAR_BOTTOM)); - assertEquals(dimmingImTarget, PhoneWindowManager.chooseNavigationColorWindowLw( - opaque, dimmingImTarget, null, NAV_BAR_BOTTOM)); - assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw( - opaque, dimmingNonImTarget, null, NAV_BAR_BOTTOM)); - - assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw( - null, null, visibleIme, NAV_BAR_BOTTOM)); - assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw( - null, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM)); - assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw( - null, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM)); - assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw( - opaque, opaque, visibleIme, NAV_BAR_BOTTOM)); - assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw( - opaque, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM)); - assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw( - opaque, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM)); - - assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw( - opaque, opaque, invisibleIme, NAV_BAR_BOTTOM)); - assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw( - opaque, opaque, invisibleIme, NAV_BAR_BOTTOM)); - assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw( - opaque, opaque, visibleIme, NAV_BAR_RIGHT)); - - // Only IME windows that have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS should be navigation color - // window. - assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw( - opaque, opaque, imeNonDrawNavBar, NAV_BAR_BOTTOM)); - assertEquals(dimmingImTarget, PhoneWindowManager.chooseNavigationColorWindowLw( - opaque, dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM)); - assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw( - opaque, dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM)); - } - - @Test - public void testUpdateLightNavigationBarLw() { - final FakeWindowState opaqueDarkNavBar = createOpaqueFullscreen(false); - final FakeWindowState opaqueLightNavBar = createOpaqueFullscreen(true); - - final FakeWindowState dimming = createDimmingDialogWindow(false); - - final FakeWindowState imeDrawDarkNavBar = createInputMethodWindow(true, true, false); - final FakeWindowState imeDrawLightNavBar = createInputMethodWindow(true, true, true); - - assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, - PhoneWindowManager.updateLightNavigationBarLw( - SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null, - null, null)); - - // Opaque top fullscreen window overrides SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR flag. - assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw( - 0, opaqueDarkNavBar, opaqueDarkNavBar, null, opaqueDarkNavBar)); - assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw( - SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, opaqueDarkNavBar, null, - opaqueDarkNavBar)); - assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, - PhoneWindowManager.updateLightNavigationBarLw(0, opaqueLightNavBar, - opaqueLightNavBar, null, opaqueLightNavBar)); - assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, - PhoneWindowManager.updateLightNavigationBarLw(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, - opaqueLightNavBar, opaqueLightNavBar, null, opaqueLightNavBar)); - - // Dimming window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR. - assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw( - 0, opaqueDarkNavBar, dimming, null, dimming)); - assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw( - 0, opaqueLightNavBar, dimming, null, dimming)); - assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw( - SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, dimming, null, dimming)); - assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw( - SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, null, dimming)); - assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw( - SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, imeDrawLightNavBar, - dimming)); - - // IME window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR - assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw( - SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null, imeDrawDarkNavBar, - imeDrawDarkNavBar)); - - // Even if the top fullscreen has SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, IME window wins. - assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw( - SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, opaqueLightNavBar, - imeDrawDarkNavBar, imeDrawDarkNavBar)); - - // IME window should be able to use SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR. - assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, - PhoneWindowManager.updateLightNavigationBarLw(0, opaqueDarkNavBar, - opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar)); - } - - @Test - public void testIsDockSideAllowedDockTop() { - // Docked top is always allowed - assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, NAV_BAR_BOTTOM, - true /* navigationBarCanMove */)); - assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, NAV_BAR_BOTTOM, - false /* navigationBarCanMove */)); - } - - @Test - public void testIsDockSideAllowedDockBottom() { - // Cannot dock bottom - assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_BOTTOM, DOCKED_LEFT, NAV_BAR_BOTTOM, - true /* navigationBarCanMove */)); - } - - @Test - public void testIsDockSideAllowedNavigationBarMovable() { - assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_BOTTOM, - true /* navigationBarCanMove */)); - assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_LEFT, - true /* navigationBarCanMove */)); - assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_RIGHT, - true /* navigationBarCanMove */)); - assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_BOTTOM, - true /* navigationBarCanMove */)); - assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_RIGHT, - true /* navigationBarCanMove */)); - assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_LEFT, - true /* navigationBarCanMove */)); - } - - @Test - public void testIsDockSideAllowedNavigationBarNotMovable() { - // Navigation bar is not movable such as tablets - assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_BOTTOM, - false /* navigationBarCanMove */)); - assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_TOP, NAV_BAR_BOTTOM, - false /* navigationBarCanMove */)); - assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_RIGHT, NAV_BAR_BOTTOM, - false /* navigationBarCanMove */)); - assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_BOTTOM, - false /* navigationBarCanMove */)); - assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_TOP, NAV_BAR_BOTTOM, - false /* navigationBarCanMove */)); - assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_RIGHT, NAV_BAR_BOTTOM, - false /* navigationBarCanMove */)); - } -} diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTestBase.java deleted file mode 100644 index 02a33e00fd91..000000000000 --- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTestBase.java +++ /dev/null @@ -1,278 +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.policy; - -import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM; -import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT; -import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT; -import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; -import static android.view.Surface.ROTATION_0; -import static android.view.Surface.ROTATION_180; -import static android.view.Surface.ROTATION_270; -import static android.view.Surface.ROTATION_90; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; -import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; -import static com.android.server.wm.utils.CoordinateTransforms - .transformPhysicalToLogicalCoordinates; - -import static org.junit.Assert.assertEquals; - -import android.content.Context; -import android.content.ContextWrapper; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.graphics.Matrix; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.graphics.RectF; -import android.os.IBinder; -import android.os.UserHandle; -import android.testing.TestableResources; -import android.util.Pair; -import android.view.Display; -import android.view.DisplayCutout; -import android.view.DisplayInfo; -import android.view.Gravity; -import android.view.View; -import android.view.WindowManager; -import android.view.WindowManagerGlobal; -import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.IAccessibilityManager; - -import com.android.server.policy.keyguard.KeyguardServiceDelegate; -import com.android.server.wm.DisplayFrames; -import com.android.server.wm.WindowTestUtils.TestDisplayContent; -import com.android.server.wm.utils.WmDisplayCutout; - -import org.junit.Before; - -class PhoneWindowManagerTestBase { - static final int DISPLAY_WIDTH = 500; - static final int DISPLAY_HEIGHT = 1000; - - static final int STATUS_BAR_HEIGHT = 10; - static final int NAV_BAR_HEIGHT = 15; - static final int DISPLAY_CUTOUT_HEIGHT = 8; - - TestablePhoneWindowManager mPolicy; - TestContextWrapper mContext; - DisplayFrames mFrames; - - FakeWindowState mStatusBar; - FakeWindowState mNavigationBar; - private boolean mHasDisplayCutout; - private int mRotation = ROTATION_0; - - @Before - public void setUpBase() { - mContext = new TestContextWrapper(getInstrumentation().getTargetContext()); - mContext.getResourceMocker().addOverride( - com.android.internal.R.dimen.status_bar_height_portrait, STATUS_BAR_HEIGHT); - mContext.getResourceMocker().addOverride( - com.android.internal.R.dimen.status_bar_height_landscape, STATUS_BAR_HEIGHT); - mContext.getResourceMocker().addOverride( - com.android.internal.R.dimen.navigation_bar_height, NAV_BAR_HEIGHT); - mContext.getResourceMocker().addOverride( - com.android.internal.R.dimen.navigation_bar_height_landscape, NAV_BAR_HEIGHT); - mContext.getResourceMocker().addOverride( - com.android.internal.R.dimen.navigation_bar_width, NAV_BAR_HEIGHT); - - mPolicy = TestablePhoneWindowManager.create(mContext); - - updateDisplayFrames(); - } - - public void setRotation(int rotation) { - mRotation = rotation; - updateDisplayFrames(); - } - - private void updateDisplayFrames() { - Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation, - mHasDisplayCutout); - mFrames = new DisplayFrames(Display.DEFAULT_DISPLAY, info.first, info.second); - } - - public void addStatusBar() { - mStatusBar = new FakeWindowState(); - mStatusBar.attrs = new WindowManager.LayoutParams(MATCH_PARENT, STATUS_BAR_HEIGHT, - TYPE_STATUS_BAR, 0 /* flags */, PixelFormat.TRANSLUCENT); - mStatusBar.attrs.gravity = Gravity.TOP; - - mPolicy.addWindow(mStatusBar); - mPolicy.mLastSystemUiFlags |= View.STATUS_BAR_TRANSPARENT; - } - - public void addNavigationBar() { - mNavigationBar = new FakeWindowState(); - mNavigationBar.attrs = new WindowManager.LayoutParams(MATCH_PARENT, NAV_BAR_HEIGHT, - TYPE_NAVIGATION_BAR, 0 /* flags */, PixelFormat.TRANSLUCENT); - mNavigationBar.attrs.gravity = Gravity.BOTTOM; - - mPolicy.addWindow(mNavigationBar); - mPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT; - } - - public void addDisplayCutout() { - mHasDisplayCutout = true; - updateDisplayFrames(); - } - - /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */ - public void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop, - int expectedInsetRight, int expectedInsetBottom) { - assertEquals(new Rect(expectedInsetLeft, expectedInsetTop, - mFrames.mDisplayWidth - expectedInsetRight, - mFrames.mDisplayHeight - expectedInsetBottom), actual); - } - - /** - * Asserts that {@code actual} is inset by the given amounts from the full display rect. - * - * Convenience wrapper for when only the top and bottom inset are non-zero. - */ - public void assertInsetByTopBottom(Rect actual, int expectedInsetTop, int expectedInsetBottom) { - assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom); - } - - public static DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) { - return displayInfoAndCutoutForRotation(rotation, withDisplayCutout).first; - } - public static Pair<DisplayInfo, WmDisplayCutout> displayInfoAndCutoutForRotation(int rotation, - boolean withDisplayCutout) { - DisplayInfo info = new DisplayInfo(); - WmDisplayCutout cutout = null; - - final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270; - info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH; - info.logicalHeight = flippedDimensions ? DISPLAY_WIDTH : DISPLAY_HEIGHT; - info.rotation = rotation; - if (withDisplayCutout) { - cutout = WmDisplayCutout.computeSafeInsets( - displayCutoutForRotation(rotation), info.logicalWidth, - info.logicalHeight); - info.displayCutout = cutout.getDisplayCutout(); - } else { - info.displayCutout = null; - } - return Pair.create(info, cutout); - } - - private static DisplayCutout displayCutoutForRotation(int rotation) { - RectF rectF = new RectF(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT); - - Matrix m = new Matrix(); - transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m); - m.mapRect(rectF); - - int pos = -1; - switch (rotation) { - case ROTATION_0: - pos = BOUNDS_POSITION_TOP; - break; - case ROTATION_90: - pos = BOUNDS_POSITION_LEFT; - break; - case ROTATION_180: - pos = BOUNDS_POSITION_BOTTOM; - break; - case ROTATION_270: - pos = BOUNDS_POSITION_RIGHT; - break; - } - - - return DisplayCutout.fromBoundingRect((int) rectF.left, (int) rectF.top, - (int) rectF.right, (int) rectF.bottom, pos); - } - - static class TestContextWrapper extends ContextWrapper { - private final TestableResources mResourceMocker; - - TestContextWrapper(Context targetContext) { - super(targetContext); - mResourceMocker = new TestableResources(targetContext.getResources()); - } - - @Override - public int checkPermission(String permission, int pid, int uid) { - return PackageManager.PERMISSION_GRANTED; - } - - @Override - public int checkPermission(String permission, int pid, int uid, IBinder callerToken) { - return PackageManager.PERMISSION_GRANTED; - } - - @Override - public Resources getResources() { - return mResourceMocker.getResources(); - } - - public TestableResources getResourceMocker() { - return mResourceMocker; - } - } - - static class TestablePhoneWindowManager extends PhoneWindowManager { - - TestablePhoneWindowManager() { - } - - @Override - void initializeHdmiState() { - // Do nothing. - } - - @Override - Context getSystemUiContext() { - return mContext; - } - - void addWindow(WindowState state) { - if (state instanceof FakeWindowState) { - ((FakeWindowState) state).surfaceLayer = - getWindowLayerFromTypeLw(state.getAttrs().type, - true /* canAddInternalSystemWindow */); - } - adjustWindowParamsLw(state, state.getAttrs(), true /* hasStatusBarPermission */); - assertEquals(WindowManagerGlobal.ADD_OKAY, prepareAddWindowLw(state, state.getAttrs())); - } - - public static TestablePhoneWindowManager create(Context context) { - TestablePhoneWindowManager[] policy = new TestablePhoneWindowManager[1]; - getInstrumentation().runOnMainSync(() -> { - policy[0] = new TestablePhoneWindowManager(); - policy[0].mContext = context; - policy[0].mKeyguardDelegate = mock(KeyguardServiceDelegate.class); - policy[0].mAccessibilityManager = new AccessibilityManager(context, - mock(IAccessibilityManager.class), UserHandle.USER_CURRENT); - policy[0].mSystemGestures = mock(SystemGesturesPointerEventListener.class); - - final TestDisplayContent displayContent = TestDisplayContent.create(context); - policy[0].setDefaultDisplay(displayContent); - policy[0].onConfigurationChanged(displayContent); - }); - return policy[0]; - } - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 6ed83de06ae3..170bd3311de2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -163,7 +163,8 @@ public class ActivityRecordTests extends ActivityTestsBase { private void verifyPositionWithLimitedAspectRatio(int navBarPosition, Rect taskBounds, float aspectRatio, Rect expectedActivityBounds) { // Verify with nav bar on the right. - when(mService.mWindowManager.getNavBarPosition()).thenReturn(navBarPosition); + when(mService.mWindowManager.getNavBarPosition(mActivity.getDisplayId())) + .thenReturn(navBarPosition); mTask.getConfiguration().windowConfiguration.setAppBounds(taskBounds); mActivity.info.maxAspectRatio = aspectRatio; mActivity.ensureActivityConfiguration( diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java index 152831f50770..4f573a475ae7 100644 --- a/services/usage/java/com/android/server/usage/AppStandbyController.java +++ b/services/usage/java/com/android/server/usage/AppStandbyController.java @@ -27,8 +27,6 @@ import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOU import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START; -import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_START; -import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN; @@ -846,8 +844,6 @@ public class AppStandbyController { // Inform listeners if necessary if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND - || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_START - || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_STOP || event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION || event.mEventType == UsageEvents.Event.USER_INTERACTION || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN @@ -900,10 +896,6 @@ public class AppStandbyController { switch (eventType) { case UsageEvents.Event.MOVE_TO_FOREGROUND: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND; case UsageEvents.Event.MOVE_TO_BACKGROUND: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND; - case UsageEvents.Event.FOREGROUND_SERVICE_START: - return REASON_SUB_USAGE_FOREGROUND_SERVICE_START; - case UsageEvents.Event.FOREGROUND_SERVICE_STOP: - return REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP; case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION; case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION; case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN; diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java index d94062002dcd..01e566cdd85f 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java +++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java @@ -20,6 +20,7 @@ import android.app.usage.UsageEvents; import android.app.usage.UsageStats; import android.content.res.Configuration; import android.util.ArrayMap; +import android.util.Log; import com.android.internal.util.XmlUtils; @@ -89,11 +90,23 @@ final class UsageStatsXmlV1 { // Apply the offset to the beginTime to find the absolute time. stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute( parser, LAST_TIME_ACTIVE_ATTR); - stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute( - parser, LAST_TIME_SERVICE_USED_ATTR); + + try { + stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute( + parser, LAST_TIME_SERVICE_USED_ATTR); + } catch (IOException e) { + Log.e(TAG, "Failed to parse mLastTimeForegroundServiceUsed", e); + } + stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR); - stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser, + + try { + stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser, TOTAL_TIME_SERVICE_USED_ATTR); + } catch (IOException e) { + Log.e(TAG, "Failed to parse mTotalTimeForegroundServiceUsed", e); + } + stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR); stats.mAppLaunchCount = XmlUtils.readIntAttribute(parser, APP_LAUNCH_COUNT_ATTR, 0); @@ -350,8 +363,17 @@ final class UsageStatsXmlV1 { } statsOut.endTime = statsOut.beginTime + XmlUtils.readLongAttribute(parser, END_TIME_ATTR); - statsOut.majorVersion = XmlUtils.readIntAttribute(parser, MAJOR_VERSION_ATTR); - statsOut.minorVersion = XmlUtils.readIntAttribute(parser, MINOR_VERSION_ATTR); + try { + statsOut.majorVersion = XmlUtils.readIntAttribute(parser, MAJOR_VERSION_ATTR); + } catch (IOException e) { + Log.e(TAG, "Failed to parse majorVersion", e); + } + + try { + statsOut.minorVersion = XmlUtils.readIntAttribute(parser, MINOR_VERSION_ATTR); + } catch (IOException e) { + Log.e(TAG, "Failed to parse minorVersion", e); + } int eventCode; int outerDepth = parser.getDepth(); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 5ad7c30564e5..185c886e4c49 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1838,6 +1838,13 @@ public class CarrierConfigManager { "notify_international_call_on_wfc_bool"; /** + * Flag to hide Preset APN details. If true, user cannot enter ApnEditor view of Preset APN, + * and cannot view details of the APN. If false, user can enter ApnEditor view of Preset APN. + * Default value is false. + */ + public static final String KEY_HIDE_PRESET_APN_DETAILS_BOOL = "hide_preset_apn_details_bool"; + + /** * Flag specifying whether to show an alert dialog for video call charges. * By default this value is {@code false}. * @hide @@ -2643,6 +2650,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_DISPLAY_VOICEMAIL_NUMBER_AS_DEFAULT_CALL_FORWARDING_NUMBER_BOOL, false); sDefaults.putBoolean(KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL, false); + sDefaults.putBoolean(KEY_HIDE_PRESET_APN_DETAILS_BOOL, false); sDefaults.putBoolean(KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL, false); sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY, null); diff --git a/telephony/java/android/telephony/MbmsGroupCallSession.java b/telephony/java/android/telephony/MbmsGroupCallSession.java index e3737976adf7..269cda13bd93 100644 --- a/telephony/java/android/telephony/MbmsGroupCallSession.java +++ b/telephony/java/android/telephony/MbmsGroupCallSession.java @@ -37,6 +37,7 @@ import android.telephony.mbms.vendor.IMbmsGroupCallService; import android.util.ArraySet; import android.util.Log; +import java.util.List; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; @@ -107,14 +108,14 @@ public class MbmsGroupCallSession implements AutoCloseable { * {@link MbmsGroupCallSession} that you received before calling this method again. * * @param context The {@link Context} to use. - * @param executor The executor on which you wish to execute callbacks. * @param subscriptionId The subscription ID to use. + * @param executor The executor on which you wish to execute callbacks. * @param callback A callback object on which you wish to receive results of asynchronous * operations. * @return An instance of {@link MbmsGroupCallSession}, or null if an error occurred. */ public static @Nullable MbmsGroupCallSession create(@NonNull Context context, - @NonNull Executor executor, int subscriptionId, + int subscriptionId, @NonNull Executor executor, final @NonNull MbmsGroupCallSessionCallback callback) { if (!sIsInitialized.compareAndSet(false, true)) { throw new IllegalStateException("Cannot create two instances of MbmsGroupCallSession"); @@ -138,11 +139,11 @@ public class MbmsGroupCallSession implements AutoCloseable { /** * Create a new {@link MbmsGroupCallSession} using the system default data subscription ID. - * See {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)}. + * See {@link #create(Context, int, Executor, MbmsGroupCallSessionCallback)}. */ public static MbmsGroupCallSession create(@NonNull Context context, @NonNull Executor executor, @NonNull MbmsGroupCallSessionCallback callback) { - return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback); + return create(context, SubscriptionManager.getDefaultSubscriptionId(), executor, callback); } /** @@ -153,7 +154,7 @@ public class MbmsGroupCallSession implements AutoCloseable { * instance of {@link MbmsGroupCallSessionCallback}, but callbacks that have already been * enqueued will still be delivered. * - * It is safe to call {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)} to + * It is safe to call {@link #create(Context, int, Executor, MbmsGroupCallSessionCallback)} to * obtain another instance of {@link MbmsGroupCallSession} immediately after this method * returns. * @@ -189,18 +190,19 @@ public class MbmsGroupCallSession implements AutoCloseable { * Asynchronous errors through the callback include any of the errors in * {@link MbmsErrors.GeneralErrors}. * - * @param executor The executor on which you wish to execute callbacks for this stream. * @param tmgi The TMGI, an identifier for the group call you want to join. - * @param saiArray An array of SAIs for the group call that should be negotiated separately with + * @param saiList A list of SAIs for the group call that should be negotiated separately with * the carrier. - * @param frequencyArray An array of frequencies for the group call that should be negotiated + * @param frequencyList A lost of frequencies for the group call that should be negotiated * separately with the carrier. + * @param executor The executor on which you wish to execute callbacks for this stream. * @param callback The callback that you want to receive information about the call on. * @return An instance of {@link GroupCall} through which the call can be controlled. * May be {@code null} if an error occurred. */ - public @Nullable GroupCall startGroupCall(@NonNull Executor executor, long tmgi, int[] saiArray, - int[] frequencyArray, @NonNull GroupCallCallback callback) { + public @Nullable GroupCall startGroupCall(long tmgi, @NonNull List<Integer> saiList, + @NonNull List<Integer> frequencyList, @NonNull Executor executor, + @NonNull GroupCallCallback callback) { IMbmsGroupCallService groupCallService = mService.get(); if (groupCallService == null) { throw new IllegalStateException("Middleware not yet bound"); @@ -215,7 +217,7 @@ public class MbmsGroupCallSession implements AutoCloseable { try { int returnCode = groupCallService.startGroupCall( - mSubscriptionId, tmgi, saiArray, frequencyArray, serviceCallback); + mSubscriptionId, tmgi, saiList, frequencyList, serviceCallback); if (returnCode == MbmsErrors.UNKNOWN) { // Unbind and throw an obvious error close(); diff --git a/telephony/java/android/telephony/mbms/GroupCall.java b/telephony/java/android/telephony/mbms/GroupCall.java index 9aca18e07812..25e274e4cb13 100644 --- a/telephony/java/android/telephony/mbms/GroupCall.java +++ b/telephony/java/android/telephony/mbms/GroupCall.java @@ -17,6 +17,7 @@ package android.telephony.mbms; import android.annotation.IntDef; +import android.annotation.NonNull; import android.os.RemoteException; import android.telephony.MbmsGroupCallSession; import android.telephony.mbms.vendor.IMbmsGroupCallService; @@ -24,6 +25,7 @@ import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.List; /** * Class used to represent a single MBMS group call. After a call has been started with @@ -41,8 +43,26 @@ public class GroupCall implements AutoCloseable { @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED}) public @interface GroupCallState {} + + /** + * Indicates that the group call is in a stopped state + * + * This can be reported after network action or after calling {@link #close}. + */ public static final int STATE_STOPPED = 1; + + /** + * Indicates that the group call is started. + * + * Data can be transmitted and received in this state. + */ public static final int STATE_STARTED = 2; + + /** + * Indicates that the group call is stalled. + * + * This may be due to a network issue or the device being temporarily out of range. + */ public static final int STATE_STALLED = 3; /** @@ -122,16 +142,17 @@ public class GroupCall implements AutoCloseable { * Send an update to the middleware when the SAI (Service Area Identifier) list and frequency * information of the group call has * changed. Callers must obtain this information from the * wireless carrier independently. - * @param saiArray New array of SAIs that the call is available on. - * @param frequencyArray New array of frequencies that the call is available on. + * @param saiList New list of SAIs that the call is available on. + * @param frequencyList New list of frequencies that the call is available on. */ - public void updateGroupCall(int[] saiArray, int[] frequencyArray) { + public void updateGroupCall(@NonNull List<Integer> saiList, + @NonNull List<Integer> frequencyList) { if (mService == null) { throw new IllegalStateException("No group call service attached"); } try { - mService.updateGroupCall(mSubscriptionId, mTmgi, saiArray, frequencyArray); + mService.updateGroupCall(mSubscriptionId, mTmgi, saiList, frequencyList); } catch (RemoteException e) { Log.w(LOG_TAG, "Remote process died"); mService = null; diff --git a/telephony/java/android/telephony/mbms/GroupCallCallback.java b/telephony/java/android/telephony/mbms/GroupCallCallback.java index 001bb02aad94..77e36bbcf2ae 100644 --- a/telephony/java/android/telephony/mbms/GroupCallCallback.java +++ b/telephony/java/android/telephony/mbms/GroupCallCallback.java @@ -17,6 +17,7 @@ package android.telephony.mbms; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.Nullable; import java.lang.annotation.Retention; @@ -26,7 +27,7 @@ import java.lang.annotation.RetentionPolicy; * A callback class for use when the application is in a group call. The middleware * will provide updates on the status of the call via this callback. */ -public class GroupCallCallback { +public interface GroupCallCallback { /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { @@ -40,7 +41,7 @@ public class GroupCallCallback { MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE, MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM, MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" }) - private @interface GroupCallError{} + @interface GroupCallError{} /** * Indicates broadcast signal strength is not available for this call. @@ -48,7 +49,7 @@ public class GroupCallCallback { * This may be due to the call no longer being available due to geography * or timing (end of service) */ - public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; + int SIGNAL_STRENGTH_UNAVAILABLE = -1; /** * Called by the middleware when it has detected an error condition in this group call. The @@ -56,9 +57,7 @@ public class GroupCallCallback { * @param errorCode The error code. * @param message A human-readable message generated by the middleware for debugging purposes. */ - public void onError(@GroupCallError int errorCode, @Nullable String message) { - // default implementation empty - } + void onError(@GroupCallError int errorCode, @Nullable String message); /** * Called to indicate this call has changed state. @@ -66,10 +65,8 @@ public class GroupCallCallback { * See {@link GroupCall#STATE_STOPPED}, {@link GroupCall#STATE_STARTED} * and {@link GroupCall#STATE_STALLED}. */ - public void onGroupCallStateChanged(@GroupCall.GroupCallState int state, - @GroupCall.GroupCallStateChangeReason int reason) { - // default implementation empty - } + void onGroupCallStateChanged(@GroupCall.GroupCallState int state, + @GroupCall.GroupCallStateChangeReason int reason); /** * Broadcast Signal Strength updated. @@ -81,7 +78,5 @@ public class GroupCallCallback { * {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available * for this call due to timing, geography or popularity. */ - public void onBroadcastSignalStrengthUpdated(int signalStrength) { - // default implementation empty - } + void onBroadcastSignalStrengthUpdated(@IntRange(from = -1, to = 4) int signalStrength); } diff --git a/telephony/java/android/telephony/mbms/MbmsErrors.java b/telephony/java/android/telephony/mbms/MbmsErrors.java index 7c4321ba75c3..52e4d333b29d 100644 --- a/telephony/java/android/telephony/mbms/MbmsErrors.java +++ b/telephony/java/android/telephony/mbms/MbmsErrors.java @@ -140,5 +140,21 @@ public class MbmsErrors { public static final int ERROR_UNKNOWN_FILE_INFO = 403; } + /** + * Indicates the errors that are applicable only to the group call use-case. + */ + public static class GroupCallErrors { + private GroupCallErrors() { } + /** Indicates that the middleware was unable to start the group call. */ + public static final int ERROR_UNABLE_TO_START_SERVICE = 501; + + /** + * Indicates that the app called + * {@link android.telephony.MbmsGroupCallSession#startGroupCall} more than once for the + * same {@code tmgi}. + */ + public static final int ERROR_DUPLICATE_START_GROUP_CALL = 502; + } + private MbmsErrors() {} } diff --git a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java index 7da734ee5837..04e7ba1af372 100644 --- a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java +++ b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java @@ -17,6 +17,7 @@ package android.telephony.mbms; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.telephony.MbmsGroupCallSession; @@ -29,9 +30,9 @@ import java.util.concurrent.Executor; /** * A callback class that is used to receive information from the middleware on MBMS group-call * services. An instance of this object should be passed into - * {@link MbmsGroupCallSession#create(Context, Executor, int, MbmsGroupCallSessionCallback)}. + * {@link MbmsGroupCallSession#create(Context, int, Executor, MbmsGroupCallSessionCallback)}. */ -public class MbmsGroupCallSessionCallback { +public interface MbmsGroupCallSessionCallback { /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { @@ -48,7 +49,7 @@ public class MbmsGroupCallSessionCallback { MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE, MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM, MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" }) - private @interface GroupCallError{} + @interface GroupCallError{} /** * Called by the middleware when it has detected an error condition. The possible error codes @@ -56,8 +57,7 @@ public class MbmsGroupCallSessionCallback { * @param errorCode The error code. * @param message A human-readable message generated by the middleware for debugging purposes. */ - public void onError(@GroupCallError int errorCode, @Nullable String message) { - } + void onError(@GroupCallError int errorCode, @Nullable String message); /** * Indicates that the list of currently available SAIs has been updated. The app may use this @@ -70,21 +70,22 @@ public class MbmsGroupCallSessionCallback { * @param availableSais A list of lists of available SAIS in neighboring cells, where each list * contains the available SAIs in an individual cell. */ - public void onAvailableSaisUpdated(List<Integer> currentSais, - List<List<Integer>> availableSais) { - } + void onAvailableSaisUpdated(@NonNull List<Integer> currentSais, + @NonNull List<List<Integer>> availableSais); /** * Called soon after the app calls {@link MbmsGroupCallSession#create}. The information supplied - * via this callback may be used to establish a data-link interface with the modem before the - * middleware is ready. - * Note that this method may be called before {@link #onMiddlewareReady()}. + * via this callback may be used to establish a data-link interface with the modem. + * + * In order to establish the data-link interface, the multicast IP and port must be obtained + * out-of-band from the carrier. A {@link java.net.MulticastSocket} may then be constructed + * using a {@link java.net.NetworkInterface} with the name and interface supplied by this + * callback. * * @param interfaceName The interface name for the data link. * @param index The index for the data link. */ - public void onServiceInterfaceAvailable(String interfaceName, int index) { - } + void onServiceInterfaceAvailable(@NonNull String interfaceName, int index); /** * Called to indicate that the middleware has been initialized and is ready. @@ -94,6 +95,5 @@ public class MbmsGroupCallSessionCallback { * delivered via {@link #onError(int, String)} with error code * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}. */ - public void onMiddlewareReady() { - } + void onMiddlewareReady(); } diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl index 721256a95396..44cc24a839f2 100755 --- a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl +++ b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl @@ -29,11 +29,11 @@ interface IMbmsGroupCallService void stopGroupCall(int subId, long tmgi); - void updateGroupCall(int subscriptionId, long tmgi, in int[] saiArray, - in int[] frequencyArray); + void updateGroupCall(int subscriptionId, long tmgi, in List saiList, + in List frequencyList); - int startGroupCall(int subscriptionId, long tmgi, in int[] saiArray, - in int[] frequencyArray, IGroupCallCallback callback); + int startGroupCall(int subscriptionId, long tmgi, in List saiList, + in List frequencyList, IGroupCallCallback callback); void dispose(int subId); } diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java index 3734ca7d6fc9..e86a47d5bfa1 100644 --- a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java +++ b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java @@ -115,15 +115,16 @@ public class MbmsGroupCallServiceBase extends Service { } @Override - public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray, - int[] frequencyArray) { + public void updateGroupCall(int subscriptionId, long tmgi, List saiList, + List frequencyList) { MbmsGroupCallServiceBase.this.updateGroupCall( - subscriptionId, tmgi, saiArray, frequencyArray); + subscriptionId, tmgi, saiList, frequencyList); } @Override - public int startGroupCall(final int subscriptionId, final long tmgi, final int[] saiArray, - final int[] frequencyArray, final IGroupCallCallback callback) + public int startGroupCall(final int subscriptionId, final long tmgi, + final List saiList, + final List frequencyList, final IGroupCallCallback callback) throws RemoteException { if (callback == null) { throw new NullPointerException("Callback must not be null"); @@ -132,7 +133,7 @@ public class MbmsGroupCallServiceBase extends Service { final int uid = Binder.getCallingUid(); int result = MbmsGroupCallServiceBase.this.startGroupCall( - subscriptionId, tmgi, saiArray, frequencyArray, new GroupCallCallback() { + subscriptionId, tmgi, saiList, frequencyList, new GroupCallCallback() { @Override public void onError(final int errorCode, final String message) { try { @@ -209,13 +210,13 @@ public class MbmsGroupCallServiceBase extends Service { * * @param subscriptionId The subscription id to use. * @param tmgi The TMGI, an identifier for the group call. - * @param saiArray An array of SAIs for the group call. - * @param frequencyArray An array of frequencies for the group call. + * @param saiList A list of SAIs for the group call. + * @param frequencyList A list of frequencies for the group call. * @param callback The callback object on which the app wishes to receive updates. * @return Any error in {@link MbmsErrors.GeneralErrors} */ - public int startGroupCall(int subscriptionId, long tmgi, int[] saiArray, int[] frequencyArray, - GroupCallCallback callback) { + public int startGroupCall(int subscriptionId, long tmgi, List<Integer> saiList, + List<Integer> frequencyList, GroupCallCallback callback) { throw new UnsupportedOperationException("Not implemented"); } @@ -237,11 +238,11 @@ public class MbmsGroupCallServiceBase extends Service { /** * Called when the app receives new SAI and frequency information for the group call identified * by {@code tmgi}. - * @param saiArray New array of SAIs that the call is available on. - * @param frequencyArray New array of frequencies that the call is available on. + * @param saiList New list of SAIs that the call is available on. + * @param frequencyList New list of frequencies that the call is available on. */ - public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray, - int[] frequencyArray) { + public void updateGroupCall(int subscriptionId, long tmgi, List<Integer> saiList, + List<Integer> frequencyList) { throw new UnsupportedOperationException("Not implemented"); } diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java index 771faaf4955d..be1a45501bb1 100644 --- a/tests/net/java/android/net/IpSecConfigTest.java +++ b/tests/net/java/android/net/IpSecConfigTest.java @@ -47,6 +47,7 @@ public class IpSecConfigTest { assertNull(c.getEncryption()); assertNull(c.getAuthentication()); assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId()); + assertEquals(0, c.getXfrmInterfaceId()); } private IpSecConfig getSampleConfig() { @@ -77,6 +78,7 @@ public class IpSecConfigTest { c.setNattKeepaliveInterval(42); c.setMarkValue(12); c.setMarkMask(23); + c.setXfrmInterfaceId(34); return c; } diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java index 9b919abfa41d..4dc0341c8e2b 100644 --- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java +++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java @@ -71,6 +71,9 @@ public class IpSecServiceParameterizedTest { private final LinkAddress mLocalInnerAddress; private final int mFamily; + private static final int[] ADDRESS_FAMILIES = + new int[] {AF_INET, AF_INET6}; + @Parameterized.Parameters public static Collection ipSecConfigs() { return Arrays.asList( @@ -196,6 +199,7 @@ public class IpSecServiceParameterizedTest { anyString(), eq(TEST_SPI), anyInt(), + anyInt(), anyInt()); // Verify quota and RefcountedResource objects cleaned up @@ -231,6 +235,7 @@ public class IpSecServiceParameterizedTest { anyString(), eq(TEST_SPI), anyInt(), + anyInt(), anyInt()); // Verify quota and RefcountedResource objects cleaned up @@ -304,7 +309,8 @@ public class IpSecServiceParameterizedTest { eq((authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0), eq(config.getEncapType()), eq(encapSocketPort), - eq(config.getEncapRemotePort())); + eq(config.getEncapRemotePort()), + eq(config.getXfrmInterfaceId())); } @Test @@ -430,6 +436,7 @@ public class IpSecServiceParameterizedTest { anyString(), eq(TEST_SPI), anyInt(), + anyInt(), anyInt()); // quota is not released until the SPI is released by the Transform assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent); @@ -452,6 +459,7 @@ public class IpSecServiceParameterizedTest { anyString(), eq(TEST_SPI), anyInt(), + anyInt(), anyInt()); // Verify quota and RefcountedResource objects cleaned up @@ -469,6 +477,7 @@ public class IpSecServiceParameterizedTest { anyString(), anyInt(), anyInt(), + anyInt(), anyInt()); assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent); @@ -504,6 +513,7 @@ public class IpSecServiceParameterizedTest { anyString(), eq(TEST_SPI), anyInt(), + anyInt(), anyInt()); // Verify quota and RefcountedResource objects cleaned up @@ -572,11 +582,12 @@ public class IpSecServiceParameterizedTest { assertEquals(1, userRecord.mTunnelQuotaTracker.mCurrent); verify(mMockNetd) - .addVirtualTunnelInterface( + .ipSecAddTunnelInterface( eq(createTunnelResp.interfaceName), eq(mSourceAddr), eq(mDestinationAddr), anyInt(), + anyInt(), anyInt()); } @@ -591,7 +602,7 @@ public class IpSecServiceParameterizedTest { // Verify quota and RefcountedResource objects cleaned up assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent); - verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName)); + verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName)); try { userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( createTunnelResp.resourceId); @@ -614,7 +625,7 @@ public class IpSecServiceParameterizedTest { // Verify quota and RefcountedResource objects cleaned up assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent); - verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName)); + verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName)); try { userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( createTunnelResp.resourceId); @@ -624,6 +635,41 @@ public class IpSecServiceParameterizedTest { } @Test + public void testApplyTunnelModeTransform() throws Exception { + IpSecConfig ipSecConfig = new IpSecConfig(); + ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL); + addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); + addAuthAndCryptToIpSecConfig(ipSecConfig); + + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage"); + IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage"); + + int transformResourceId = createTransformResp.resourceId; + int tunnelResourceId = createTunnelResp.resourceId; + mIpSecService.applyTunnelModeTransform(tunnelResourceId, IpSecManager.DIRECTION_OUT, + transformResourceId, "blessedPackage"); + + for (int selAddrFamily : ADDRESS_FAMILIES) { + verify(mMockNetd) + .ipSecUpdateSecurityPolicy( + eq(mUid), + eq(selAddrFamily), + eq(IpSecManager.DIRECTION_OUT), + anyString(), + anyString(), + eq(TEST_SPI), + anyInt(), // iKey/oKey + anyInt(), // mask + eq(tunnelResourceId)); + } + + ipSecConfig.setXfrmInterfaceId(tunnelResourceId); + verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp); + } + + @Test public void testAddRemoveAddressFromTunnelInterface() throws Exception { for (String pkgName : new String[]{"blessedPackage", "systemPackage"}) { IpSecTunnelInterfaceResponse createTunnelResp = diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java index af7123b84842..f2bd770d085a 100644 --- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java @@ -27,9 +27,17 @@ import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_VENDOR; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.os.Process.SYSTEM_UID; +import static com.android.server.connectivity.PermissionMonitor.NETWORK; +import static com.android.server.connectivity.PermissionMonitor.SYSTEM; + +import static junit.framework.Assert.fail; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.spy; @@ -40,6 +48,8 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Build; +import android.os.INetworkManagementService; +import android.os.UserHandle; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -48,12 +58,19 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; + +import java.util.HashMap; @RunWith(AndroidJUnit4.class) @SmallTest public class PermissionMonitorTest { - private static final int MOCK_UID = 10001; - private static final String[] MOCK_PACKAGE_NAMES = new String[] { "com.foo.bar" }; + private static final int MOCK_USER1 = 0; + private static final int MOCK_USER2 = 1; + private static final int MOCK_UID1 = 10001; + private static final String MOCK_PACKAGE1 = "appName1"; + private static final String SYSTEM_PACKAGE1 = "sysName1"; + private static final String SYSTEM_PACKAGE2 = "sysName2"; private static final String PARTITION_SYSTEM = "system"; private static final String PARTITION_OEM = "oem"; private static final String PARTITION_PRODUCT = "product"; @@ -63,6 +80,7 @@ public class PermissionMonitorTest { @Mock private Context mContext; @Mock private PackageManager mPackageManager; + @Mock private INetworkManagementService mNMS; private PermissionMonitor mPermissionMonitor; @@ -70,8 +88,7 @@ public class PermissionMonitorTest { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mContext.getPackageManager()).thenReturn(mPackageManager); - when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(MOCK_PACKAGE_NAMES); - mPermissionMonitor = spy(new PermissionMonitor(mContext, null)); + mPermissionMonitor = spy(new PermissionMonitor(mContext, mNMS)); } private boolean hasBgPermission(String partition, int targetSdkVersion, int uid, @@ -80,7 +97,8 @@ public class PermissionMonitorTest { packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion; packageInfo.applicationInfo.uid = uid; when(mPackageManager.getPackageInfoAsUser( - eq(MOCK_PACKAGE_NAMES[0]), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo); + eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo); + when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[] {MOCK_PACKAGE1}); return mPermissionMonitor.hasUseBackgroundNetworksPermission(uid); } @@ -143,16 +161,16 @@ public class PermissionMonitorTest { @Test public void testHasUseBackgroundNetworksPermission() throws Exception { - assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID)); - assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE)); - assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, NETWORK_STACK)); - assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL)); - assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, + assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1)); + assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE)); + assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, NETWORK_STACK)); + assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL)); + assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE)); + assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE)); - assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID)); - assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE)); + assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1)); + assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE)); } @Test @@ -172,15 +190,150 @@ public class PermissionMonitorTest { @Test public void testHasUseBackgroundNetworksPermissionVendorApp() throws Exception { - assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID)); - assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE)); - assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, NETWORK_STACK)); - assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL)); - assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, + assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1)); + assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE)); + assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, NETWORK_STACK)); + assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL)); + assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE)); + assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE)); + + assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1)); + assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE)); + } + + private class NMSMonitor { + private final HashMap<Integer, Boolean> mApps = new HashMap<>(); + + NMSMonitor(INetworkManagementService mockNMS) throws Exception { + // Add hook to verify and track result of setPermission. + doAnswer((InvocationOnMock invocation) -> { + final Object[] args = invocation.getArguments(); + final Boolean isSystem = args[0].equals("SYSTEM"); + for (final int uid : (int[]) args[1]) { + // TODO: Currently, permission monitor will send duplicate commands for each uid + // corresponding to each user. Need to fix that and uncomment below test. + // if (mApps.containsKey(uid) && mApps.get(uid) == isSystem) { + // fail("uid " + uid + " is already set to " + isSystem); + // } + mApps.put(uid, isSystem); + } + return null; + }).when(mockNMS).setPermission(anyString(), any(int[].class)); + + // Add hook to verify and track result of clearPermission. + doAnswer((InvocationOnMock invocation) -> { + final Object[] args = invocation.getArguments(); + for (final int uid : (int[]) args[0]) { + // TODO: Currently, permission monitor will send duplicate commands for each uid + // corresponding to each user. Need to fix that and uncomment below test. + // if (!mApps.containsKey(uid)) { + // fail("uid " + uid + " does not exist."); + // } + mApps.remove(uid); + } + return null; + }).when(mockNMS).clearPermission(any(int[].class)); + } + + public void expectPermission(Boolean permission, int[] users, int[] apps) { + for (final int user : users) { + for (final int app : apps) { + final int uid = UserHandle.getUid(user, app); + if (!mApps.containsKey(uid)) { + fail("uid " + uid + " does not exist."); + } + if (mApps.get(uid) != permission) { + fail("uid " + uid + " has wrong permission: " + permission); + } + } + } + } + + public void expectNoPermission(int[] users, int[] apps) { + for (final int user : users) { + for (final int app : apps) { + final int uid = UserHandle.getUid(user, app); + if (mApps.containsKey(uid)) { + fail("uid " + uid + " has listed permissions, expected none."); + } + } + } + } + } + + @Test + public void testUserAndPackageAddRemove() throws Exception { + final NMSMonitor mNMSMonitor = new NMSMonitor(mNMS); - assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID)); - assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE)); + // MOCK_UID1: MOCK_PACKAGE1 only has network permission. + // SYSTEM_UID: SYSTEM_PACKAGE1 has system permission. + // SYSTEM_UID: SYSTEM_PACKAGE2 only has network permission. + doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(eq(SYSTEM), anyString()); + doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(any(), + eq(SYSTEM_PACKAGE1)); + doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(), + eq(SYSTEM_PACKAGE2)); + doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(), + eq(MOCK_PACKAGE1)); + + // Add SYSTEM_PACKAGE2, expect only have network permission. + mPermissionMonitor.onUserAdded(MOCK_USER1); + addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE2, SYSTEM_UID); + mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID}); + + // Add SYSTEM_PACKAGE1, expect permission escalate. + addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE1, SYSTEM_UID); + mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID}); + + mPermissionMonitor.onUserAdded(MOCK_USER2); + mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2}, + new int[]{SYSTEM_UID}); + + addPackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1); + mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2}, + new int[]{SYSTEM_UID}); + mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2}, + new int[]{MOCK_UID1}); + + // Remove MOCK_UID1, expect no permission left for all user. + mPermissionMonitor.onPackageRemoved(MOCK_UID1); + removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_UID1); + mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, new int[]{MOCK_UID1}); + + // Remove SYSTEM_PACKAGE1, expect permission downgrade. + when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{SYSTEM_PACKAGE2}); + removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, SYSTEM_UID); + mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2}, + new int[]{SYSTEM_UID}); + + mPermissionMonitor.onUserRemoved(MOCK_USER1); + mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER2}, new int[]{SYSTEM_UID}); + + // Remove all packages, expect no permission left. + when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{}); + removePackageForUsers(new int[]{MOCK_USER2}, SYSTEM_UID); + mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, + new int[]{SYSTEM_UID, MOCK_UID1}); + + // Remove last user, expect no redundant clearPermission is invoked. + mPermissionMonitor.onUserRemoved(MOCK_USER2); + mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, + new int[]{SYSTEM_UID, MOCK_UID1}); + } + + // Normal package add/remove operations will trigger multiple intent for uids corresponding to + // each user. To simulate generic package operations, the onPackageAdded/Removed will need to be + // called multiple times with the uid corresponding to each user. + private void addPackageForUsers(int[] users, String packageName, int uid) { + for (final int user : users) { + mPermissionMonitor.onPackageAdded(packageName, UserHandle.getUid(user, uid)); + } + } + + private void removePackageForUsers(int[] users, int uid) { + for (final int user : users) { + mPermissionMonitor.onPackageRemoved(UserHandle.getUid(user, uid)); + } } } diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java index a6ed9f252008..80818120fa3c 100644 --- a/tests/net/java/com/android/server/connectivity/TetheringTest.java +++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java @@ -23,15 +23,15 @@ import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED; import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY; import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER; import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER; -import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE; -import static android.net.ConnectivityManager.TETHERING_WIFI; import static android.net.ConnectivityManager.TETHERING_USB; +import static android.net.ConnectivityManager.TETHERING_WIFI; +import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE; import static android.net.ConnectivityManager.TYPE_MOBILE; -import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; -import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; +import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; +import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER; @@ -39,19 +39,18 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.notNull; -import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import static org.mockito.Mockito.mock; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -91,9 +90,9 @@ import android.os.INetworkManagementService; import android.os.Looper; import android.os.PersistableBundle; import android.os.RemoteException; -import android.os.test.TestLooper; import android.os.UserHandle; import android.os.UserManager; +import android.os.test.TestLooper; import android.provider.Settings; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -126,7 +125,6 @@ import java.util.Vector; public class TetheringTest { private static final int IFINDEX_OFFSET = 100; - private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0"; private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0"; private static final String TEST_USB_IFNAME = "test_rndis0"; @@ -370,61 +368,6 @@ public class TetheringTest { mServiceContext.unregisterReceiver(mBroadcastReceiver); } - private void setupForRequiredProvisioning() { - // Produce some acceptable looking provision app setting if requested. - when(mResources.getStringArray( - com.android.internal.R.array.config_mobile_hotspot_provision_app)) - .thenReturn(PROVISIONING_APP_NAME); - // Don't disable tethering provisioning unless requested. - when(mSystemProperties.getBoolean(eq(Tethering.DISABLE_PROVISIONING_SYSPROP_KEY), - anyBoolean())).thenReturn(false); - // Act like the CarrierConfigManager is present and ready unless told otherwise. - when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) - .thenReturn(mCarrierConfigManager); - when(mCarrierConfigManager.getConfig()).thenReturn(mCarrierConfig); - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true); - } - - @Test - public void canRequireProvisioning() { - setupForRequiredProvisioning(); - sendConfigurationChanged(); - assertTrue(mTethering.isTetherProvisioningRequired()); - } - - @Test - public void toleratesCarrierConfigManagerMissing() { - setupForRequiredProvisioning(); - when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) - .thenReturn(null); - sendConfigurationChanged(); - // Couldn't get the CarrierConfigManager, but still had a declared provisioning app. - // We therefore still require provisioning. - assertTrue(mTethering.isTetherProvisioningRequired()); - } - - @Test - public void toleratesCarrierConfigMissing() { - setupForRequiredProvisioning(); - when(mCarrierConfigManager.getConfig()).thenReturn(null); - sendConfigurationChanged(); - // We still have a provisioning app configured, so still require provisioning. - assertTrue(mTethering.isTetherProvisioningRequired()); - } - - @Test - public void provisioningNotRequiredWhenAppNotFound() { - setupForRequiredProvisioning(); - when(mResources.getStringArray( - com.android.internal.R.array.config_mobile_hotspot_provision_app)) - .thenReturn(null); - assertTrue(!mTethering.isTetherProvisioningRequired()); - when(mResources.getStringArray( - com.android.internal.R.array.config_mobile_hotspot_provision_app)) - .thenReturn(new String[] {"malformedApp"}); - assertTrue(!mTethering.isTetherProvisioningRequired()); - } - private void sendWifiApStateChanged(int state) { final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); intent.putExtra(EXTRA_WIFI_AP_STATE, state); diff --git a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java new file mode 100644 index 000000000000..0f72229d38e6 --- /dev/null +++ b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java @@ -0,0 +1,144 @@ +/* + * 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.connectivity.tethering; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.Resources; +import android.net.util.SharedLog; +import android.os.PersistableBundle; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.telephony.CarrierConfigManager; + +import com.android.internal.R; +import com.android.server.connectivity.MockableSystemProperties; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public final class EntitlementManagerTest { + + private static final int EVENT_EM_UPDATE = 1; + private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; + + @Mock private CarrierConfigManager mCarrierConfigManager; + @Mock private Context mContext; + @Mock private ContentResolver mContent; + @Mock private MockableSystemProperties mSystemProperties; + @Mock private Resources mResources; + @Mock private SharedLog mLog; + + // Like so many Android system APIs, these cannot be mocked because it is marked final. + // We have to use the real versions. + private final PersistableBundle mCarrierConfig = new PersistableBundle(); + + private EntitlementManager mEnMgr; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mContext.getResources()).thenReturn(mResources); + when(mContext.getContentResolver()).thenReturn(mContent); + when(mResources.getStringArray(R.array.config_tether_dhcp_range)) + .thenReturn(new String[0]); + when(mResources.getStringArray(R.array.config_tether_usb_regexs)) + .thenReturn(new String[0]); + when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) + .thenReturn(new String[0]); + when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) + .thenReturn(new String[0]); + when(mResources.getIntArray(R.array.config_tether_upstream_types)) + .thenReturn(new int[0]); + when(mLog.forSubComponent(anyString())).thenReturn(mLog); + + mEnMgr = new EntitlementManager(mContext, mLog, mSystemProperties); + mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog)); + } + + @After + public void tearDown() throws Exception {} + + private void setupForRequiredProvisioning() { + // Produce some acceptable looking provision app setting if requested. + when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) + .thenReturn(PROVISIONING_APP_NAME); + // Don't disable tethering provisioning unless requested. + when(mSystemProperties.getBoolean(eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY), + anyBoolean())).thenReturn(false); + // Act like the CarrierConfigManager is present and ready unless told otherwise. + when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) + .thenReturn(mCarrierConfigManager); + when(mCarrierConfigManager.getConfig()).thenReturn(mCarrierConfig); + mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true); + } + + @Test + public void canRequireProvisioning() { + setupForRequiredProvisioning(); + mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog)); + assertTrue(mEnMgr.isTetherProvisioningRequired()); + } + + @Test + public void toleratesCarrierConfigManagerMissing() { + setupForRequiredProvisioning(); + when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) + .thenReturn(null); + mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog)); + // Couldn't get the CarrierConfigManager, but still had a declared provisioning app. + // Therefore provisioning still be required. + assertTrue(mEnMgr.isTetherProvisioningRequired()); + } + + @Test + public void toleratesCarrierConfigMissing() { + setupForRequiredProvisioning(); + when(mCarrierConfigManager.getConfig()).thenReturn(null); + mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog)); + // We still have a provisioning app configured, so still require provisioning. + assertTrue(mEnMgr.isTetherProvisioningRequired()); + } + + @Test + public void provisioningNotRequiredWhenAppNotFound() { + setupForRequiredProvisioning(); + when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) + .thenReturn(null); + mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog)); + assertFalse(mEnMgr.isTetherProvisioningRequired()); + when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) + .thenReturn(new String[] {"malformedApp"}); + mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog)); + assertFalse(mEnMgr.isTetherProvisioningRequired()); + } + +} diff --git a/tools/stringslint/stringslint.py b/tools/stringslint/stringslint.py index 03c0b9af66a0..afe91cda37b0 100644 --- a/tools/stringslint/stringslint.py +++ b/tools/stringslint/stringslint.py @@ -145,6 +145,13 @@ def lint(path): if "translatable" in child.attrib and child.attrib["translatable"].lower() == "false": continue + misspelled_attributes = [ + ("translateable", "translatable"), + ] + for misspelling, expected in misspelled_attributes: + if misspelling in child.attrib: + error(child, "Misspelled <string> attribute.", misspelling, expected) + limit = re.search("CHAR[ _-]LIMIT=(\d+|NONE|none)", comment.text) if limit is None: info(child, "Missing CHAR LIMIT to aid translation", diff --git a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java index aa8d325419f0..87706b936f03 100644 --- a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java +++ b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java @@ -60,14 +60,27 @@ public class WifiNetworkConfigBuilder { */ private @Nullable Pair<MacAddress, MacAddress> mBssidPatternMatcher; /** + * Whether this is an OWE network or not. + */ + private boolean mIsEnhancedOpen; + /** * Pre-shared key for use with WPA-PSK networks. */ - private @Nullable String mPskPassphrase; + private @Nullable String mWpa2PskPassphrase; + /** + * Pre-shared key for use with WPA3-SAE networks. + */ + private @Nullable String mWpa3SaePassphrase; + /** + * The enterprise configuration details specifying the EAP method, + * certificates and other settings associated with the WPA-EAP networks. + */ + private @Nullable WifiEnterpriseConfig mWpa2EnterpriseConfig; /** * The enterprise configuration details specifying the EAP method, - * certificates and other settings associated with the EAP. + * certificates and other settings associated with the SuiteB networks. */ - private @Nullable WifiEnterpriseConfig mEnterpriseConfig; + private @Nullable WifiEnterpriseConfig mWpa3EnterpriseConfig; /** * This is a network that does not broadcast its SSID, so an * SSID-specific probe request must be used for scans. @@ -94,8 +107,11 @@ public class WifiNetworkConfigBuilder { public WifiNetworkConfigBuilder() { mSsidPatternMatcher = null; mBssidPatternMatcher = null; - mPskPassphrase = null; - mEnterpriseConfig = null; + mIsEnhancedOpen = false; + mWpa2PskPassphrase = null; + mWpa3SaePassphrase = null; + mWpa2EnterpriseConfig = null; + mWpa3EnterpriseConfig = null; mIsHiddenSSID = false; mIsAppInteractionRequired = false; mIsUserInteractionRequired = false; @@ -188,36 +204,81 @@ public class WifiNetworkConfigBuilder { } /** - * Set the ASCII PSK passphrase for this network. Needed for authenticating to - * WPA_PSK networks. + * Specifies whether this represents an Enhanced Open (OWE) network. * - * @param pskPassphrase PSK passphrase of the network. + * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder + * method. + */ + public WifiNetworkConfigBuilder setIsEnhancedOpen() { + mIsEnhancedOpen = true; + return this; + } + + /** + * Set the ASCII WPA2 passphrase for this network. Needed for authenticating to + * WPA2-PSK networks. + * + * @param passphrase passphrase of the network. * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder * method. * @throws IllegalArgumentException if the passphrase is not ASCII encodable. */ - public WifiNetworkConfigBuilder setPskPassphrase(@NonNull String pskPassphrase) { - checkNotNull(pskPassphrase); + public WifiNetworkConfigBuilder setWpa2Passphrase(@NonNull String passphrase) { + checkNotNull(passphrase); final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder(); - if (!asciiEncoder.canEncode(pskPassphrase)) { + if (!asciiEncoder.canEncode(passphrase)) { throw new IllegalArgumentException("passphrase not ASCII encodable"); } - mPskPassphrase = pskPassphrase; + mWpa2PskPassphrase = passphrase; + return this; + } + + /** + * Set the ASCII WPA3 passphrase for this network. Needed for authenticating to + * WPA3-SAE networks. + * + * @param passphrase passphrase of the network. + * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder + * method. + * @throws IllegalArgumentException if the passphrase is not ASCII encodable. + */ + public WifiNetworkConfigBuilder setWpa3Passphrase(@NonNull String passphrase) { + checkNotNull(passphrase); + final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder(); + if (!asciiEncoder.canEncode(passphrase)) { + throw new IllegalArgumentException("passphrase not ASCII encodable"); + } + mWpa3SaePassphrase = passphrase; + return this; + } + + /** + * Set the associated enterprise configuration for this network. Needed for authenticating to + * WPA2-EAP networks. See {@link WifiEnterpriseConfig} for description. + * + * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}. + * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder + * method. + */ + public WifiNetworkConfigBuilder setWpa2EnterpriseConfig( + @NonNull WifiEnterpriseConfig enterpriseConfig) { + checkNotNull(enterpriseConfig); + mWpa2EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig); return this; } /** * Set the associated enterprise configuration for this network. Needed for authenticating to - * WPA_EAP networks. See {@link WifiEnterpriseConfig} for description. + * WPA3-SuiteB networks. See {@link WifiEnterpriseConfig} for description. * * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}. * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder * method. */ - public WifiNetworkConfigBuilder setEnterpriseConfig( + public WifiNetworkConfigBuilder setWpa3EnterpriseConfig( @NonNull WifiEnterpriseConfig enterpriseConfig) { checkNotNull(enterpriseConfig); - mEnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig); + mWpa3EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig); return this; } @@ -324,16 +385,38 @@ public class WifiNetworkConfigBuilder { configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); } - private void setKeyMgmtInWifiConfiguration(@NonNull WifiConfiguration configuration) { - if (!TextUtils.isEmpty(mPskPassphrase)) { - // WPA_PSK network. + private void setSecurityParamsInWifiConfiguration(@NonNull WifiConfiguration configuration) { + if (!TextUtils.isEmpty(mWpa2PskPassphrase)) { // WPA-PSK network. configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); - } else if (mEnterpriseConfig != null) { - // WPA_EAP network + // WifiConfiguration.preSharedKey needs quotes around ASCII password. + configuration.preSharedKey = "\"" + mWpa2PskPassphrase + "\""; + } else if (!TextUtils.isEmpty(mWpa3SaePassphrase)) { // WPA3-SAE network. + configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE); + // PMF mandatory for SAE. + configuration.requirePMF = true; + // WifiConfiguration.preSharedKey needs quotes around ASCII password. + configuration.preSharedKey = "\"" + mWpa3SaePassphrase + "\""; + } else if (mWpa2EnterpriseConfig != null) { // WPA-EAP network configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); - } else { - // Open network + configuration.enterpriseConfig = mWpa2EnterpriseConfig; + } else if (mWpa3EnterpriseConfig != null) { // WPA3-SuiteB network + configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192); + configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256); + // TODO (b/113878056): Verify these params once we verify SuiteB configuration. + configuration.allowedGroupMgmtCiphers.set( + WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256); + configuration.allowedSuiteBCiphers.set( + WifiConfiguration.SuiteBCipher.ECDHE_ECDSA); + configuration.allowedSuiteBCiphers.set( + WifiConfiguration.SuiteBCipher.ECDHE_RSA); + configuration.requirePMF = true; + configuration.enterpriseConfig = mWpa3EnterpriseConfig; + } else if (mIsEnhancedOpen) { // OWE network + configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE); + // PMF mandatory. + configuration.requirePMF = true; + } else { // Open network configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); } } @@ -349,12 +432,7 @@ public class WifiNetworkConfigBuilder { if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) { wifiConfiguration.SSID = "\"" + mSsidPatternMatcher.getPath() + "\""; } - setKeyMgmtInWifiConfiguration(wifiConfiguration); - // WifiConfiguration.preSharedKey needs quotes around ASCII password. - if (mPskPassphrase != null) { - wifiConfiguration.preSharedKey = "\"" + mPskPassphrase + "\""; - } - wifiConfiguration.enterpriseConfig = mEnterpriseConfig; + setSecurityParamsInWifiConfiguration(wifiConfiguration); wifiConfiguration.hiddenSSID = mIsHiddenSSID; wifiConfiguration.priority = mPriority; wifiConfiguration.meteredOverride = @@ -396,6 +474,20 @@ public class WifiNetworkConfigBuilder { return false; } + private void validateSecurityParams() { + int numSecurityTypes = 0; + numSecurityTypes += mIsEnhancedOpen ? 1 : 0; + numSecurityTypes += !TextUtils.isEmpty(mWpa2PskPassphrase) ? 1 : 0; + numSecurityTypes += !TextUtils.isEmpty(mWpa3SaePassphrase) ? 1 : 0; + numSecurityTypes += mWpa2EnterpriseConfig != null ? 1 : 0; + numSecurityTypes += mWpa3EnterpriseConfig != null ? 1 : 0; + if (numSecurityTypes > 1) { + throw new IllegalStateException("only one of setIsEnhancedOpen, setWpa2Passphrase," + + "setWpa3Passphrase, setWpa2EnterpriseConfig or setWpa3EnterpriseConfig" + + " can be invoked for network specifier"); + } + } + /** * Create a specifier object used to request a Wi-Fi network. The generated * {@link NetworkSpecifier} should be used in @@ -464,10 +556,7 @@ public class WifiNetworkConfigBuilder { + "setIsUserInteractionRequired/setPriority/setIsMetered are allowed for " + "specifier"); } - if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) { - throw new IllegalStateException("only one of setPreSharedKey or setEnterpriseConfig can" - + " be invoked for network specifier"); - } + validateSecurityParams(); return new WifiNetworkSpecifier( mSsidPatternMatcher, @@ -493,10 +582,7 @@ public class WifiNetworkConfigBuilder { throw new IllegalStateException("none of setSsidPattern/setBssidPattern/setBssid are" + " allowed for suggestion"); } - if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) { - throw new IllegalStateException("only one of setPreSharedKey or setEnterpriseConfig can" - + "be invoked for suggestion"); - } + validateSecurityParams(); return new WifiNetworkSuggestion( buildWifiConfiguration(), diff --git a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java index 25dcdd8f3fff..893b19ce3a3b 100644 --- a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java +++ b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java @@ -49,7 +49,7 @@ public final class OsuProvider implements Parcelable { /** * SSID of the network to connect for service sign-up. */ - private final WifiSsid mOsuSsid; + private WifiSsid mOsuSsid; /** * Friendly name of the OSU provider. @@ -130,6 +130,10 @@ public final class OsuProvider implements Parcelable { return mOsuSsid; } + public void setOsuSsid(WifiSsid osuSsid) { + mOsuSsid = osuSsid; + } + public String getFriendlyName() { return mFriendlyName; } diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java index 5c9db539c8f5..a62d63cd8910 100644 --- a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java +++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java @@ -150,6 +150,12 @@ public abstract class ProvisioningCallback { public static final int OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION = 22; /** + * The reason code for provisioning failure when an {@link OsuProvider} is not found for + * provisioning. + */ + public static final int OSU_FAILURE_OSU_PROVIDER_NOT_FOUND = 23; + + /** * The status code for provisioning flow to indicate connecting to OSU AP */ public static final int OSU_STATUS_AP_CONNECTING = 1; diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java index 8980ddba238b..c455c6f0836d 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java @@ -22,6 +22,7 @@ import static android.os.PatternMatcher.PATTERN_SIMPLE_GLOB; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import android.net.MacAddress; @@ -81,11 +82,11 @@ public class WifiNetworkConfigBuilderTest { * pattern. */ @Test - public void testWifiNetworkSpecifierBuilderForWpaPskNetworkWithBssidPattern() { + public void testWifiNetworkSpecifierBuilderForWpa2PskNetworkWithBssidPattern() { NetworkSpecifier specifier = new WifiNetworkConfigBuilder() .setBssidPattern(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)) - .setPskPassphrase(TEST_PRESHARED_KEY) + .setWpa2Passphrase(TEST_PRESHARED_KEY) .buildNetworkSpecifier(); assertTrue(specifier instanceof WifiNetworkSpecifier); @@ -119,7 +120,7 @@ public class WifiNetworkConfigBuilderTest { * SSID and BSSID pattern. */ @Test - public void testWifiNetworkSpecifierBuilderForEnterpriseHiddenNetworkWithSsidAndBssid() { + public void testWifiNetworkSpecifierBuilderForWpa2EapHiddenNetworkWithSsidAndBssid() { WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS); enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC); @@ -127,7 +128,7 @@ public class WifiNetworkConfigBuilderTest { NetworkSpecifier specifier = new WifiNetworkConfigBuilder() .setSsid(TEST_SSID) .setBssid(MacAddress.fromString(TEST_BSSID)) - .setEnterpriseConfig(enterpriseConfig) + .setWpa2EnterpriseConfig(enterpriseConfig) .setIsHiddenSsid() .buildNetworkSpecifier(); @@ -174,14 +175,14 @@ public class WifiNetworkConfigBuilderTest { } /** - * Ensure {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} throws an exception + * Ensure {@link WifiNetworkConfigBuilder#setWpa2Passphrase(String)} throws an exception * when the string is not ASCII encodable. */ @Test(expected = IllegalArgumentException.class) - public void testSetPskPassphraseWithNonAsciiString() { + public void testSetWpa2PasphraseWithNonAsciiString() { new WifiNetworkConfigBuilder() .setSsid(TEST_SSID) - .setPskPassphrase("salvē") + .setWpa2Passphrase("salvē") .buildNetworkSpecifier(); } @@ -275,15 +276,15 @@ public class WifiNetworkConfigBuilderTest { /** * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception - * when both {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} and - * {@link WifiNetworkConfigBuilder#setEnterpriseConfig(WifiEnterpriseConfig)} are invoked. + * when both {@link WifiNetworkConfigBuilder#setWpa2Passphrase(String)} and + * {@link WifiNetworkConfigBuilder#setWpa2EnterpriseConfig(WifiEnterpriseConfig)} are invoked. */ @Test(expected = IllegalStateException.class) - public void testWifiNetworkSpecifierBuilderWithBothPskPassphraseAndEnterpriseConfig() { + public void testWifiNetworkSpecifierBuilderWithBothWpa2PasphraseAndEnterpriseConfig() { new WifiNetworkConfigBuilder() .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL)) - .setPskPassphrase(TEST_PRESHARED_KEY) - .setEnterpriseConfig(new WifiEnterpriseConfig()) + .setWpa2Passphrase(TEST_PRESHARED_KEY) + .setWpa2EnterpriseConfig(new WifiEnterpriseConfig()) .buildNetworkSpecifier(); } @@ -375,10 +376,11 @@ public class WifiNetworkConfigBuilderTest { * app interaction and has a priority of zero set. */ @Test - public void testWifiNetworkSuggestionBuilderForWpaEapNetworkWithPriorityAndReqAppInteraction() { + public void + testWifiNetworkSuggestionBuilderForWpa2EapNetworkWithPriorityAndReqAppInteraction() { WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder() .setSsid(TEST_SSID) - .setPskPassphrase(TEST_PRESHARED_KEY) + .setWpa2Passphrase(TEST_PRESHARED_KEY) .setIsAppInteractionRequired() .setPriority(0) .buildNetworkSuggestion(); @@ -401,10 +403,11 @@ public class WifiNetworkConfigBuilderTest { * user interaction and is metered. */ @Test - public void testWifiNetworkSuggestionBuilderForWpaPskNetworkWithMeteredAndReqUserInteraction() { + public void + testWifiNetworkSuggestionBuilderForWpa2PskNetworkWithMeteredAndReqUserInteraction() { WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder() .setSsid(TEST_SSID) - .setPskPassphrase(TEST_PRESHARED_KEY) + .setWpa2Passphrase(TEST_PRESHARED_KEY) .setIsUserInteractionRequired() .setIsMetered() .buildNetworkSuggestion(); @@ -422,6 +425,74 @@ public class WifiNetworkConfigBuilderTest { } /** + * Validate correctness of WifiNetworkSuggestion object created by + * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for OWE network. + */ + @Test + public void testWifiNetworkSuggestionBuilderForEnhancedOpenNetwork() { + WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder() + .setSsid(TEST_SSID) + .setIsEnhancedOpen() + .buildNetworkSuggestion(); + + assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID); + assertTrue(suggestion.wifiConfiguration.allowedKeyManagement + .get(WifiConfiguration.KeyMgmt.OWE)); + assertNull(suggestion.wifiConfiguration.preSharedKey); + assertTrue(suggestion.wifiConfiguration.requirePMF); + } + + /** + * Validate correctness of WifiNetworkSuggestion object created by + * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for SAE network. + */ + @Test + public void testWifiNetworkSuggestionBuilderForWpa3PskNetwork() { + WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder() + .setSsid(TEST_SSID) + .setWpa3Passphrase(TEST_PRESHARED_KEY) + .buildNetworkSuggestion(); + + assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID); + assertTrue(suggestion.wifiConfiguration.allowedKeyManagement + .get(WifiConfiguration.KeyMgmt.SAE)); + assertEquals("\"" + TEST_PRESHARED_KEY + "\"", + suggestion.wifiConfiguration.preSharedKey); + assertTrue(suggestion.wifiConfiguration.requirePMF); + } + + + /** + * Validate correctness of WifiNetworkSuggestion object created by + * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for SuiteB network. + */ + @Test + public void testWifiNetworkSuggestionBuilderForWpa3EapNetwork() { + WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); + enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS); + enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC); + + WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder() + .setSsid(TEST_SSID) + .setWpa3EnterpriseConfig(enterpriseConfig) + .buildNetworkSuggestion(); + + assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID); + assertTrue(suggestion.wifiConfiguration.allowedKeyManagement + .get(WifiConfiguration.KeyMgmt.SUITE_B_192)); + assertTrue(suggestion.wifiConfiguration.allowedGroupCiphers + .get(WifiConfiguration.GroupCipher.GCMP_256)); + assertTrue(suggestion.wifiConfiguration.allowedGroupMgmtCiphers + .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256)); + assertTrue(suggestion.wifiConfiguration.allowedSuiteBCiphers + .get(WifiConfiguration.SuiteBCipher.ECDHE_ECDSA)); + assertTrue(suggestion.wifiConfiguration.allowedSuiteBCiphers + .get(WifiConfiguration.SuiteBCipher.ECDHE_RSA)); + assertTrue(suggestion.wifiConfiguration.requirePMF); + assertNull(suggestion.wifiConfiguration.preSharedKey); + } + + /** * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception * when {@link WifiNetworkConfigBuilder#setSsidPattern(PatternMatcher)} is set. */ @@ -478,4 +549,46 @@ public class WifiNetworkConfigBuilderTest { .setPriority(-1) .buildNetworkSuggestion(); } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when both {@link WifiNetworkConfigBuilder#setWpa2Passphrase(String)} and + * {@link WifiNetworkConfigBuilder#setWpa3Passphrase(String)} are invoked. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithBothWpa2PasphraseAndWpa3Passphrase() { + new WifiNetworkConfigBuilder() + .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL)) + .setWpa2Passphrase(TEST_PRESHARED_KEY) + .setWpa3Passphrase(TEST_PRESHARED_KEY) + .buildNetworkSpecifier(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when both {@link WifiNetworkConfigBuilder#setWpa3Passphrase(String)} and + * {@link WifiNetworkConfigBuilder#setWpa3EnterpriseConfig(WifiEnterpriseConfig)} are invoked. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithBothWpa3PasphraseAndEnterprise() { + new WifiNetworkConfigBuilder() + .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL)) + .setWpa3Passphrase(TEST_PRESHARED_KEY) + .setWpa3EnterpriseConfig(new WifiEnterpriseConfig()) + .buildNetworkSpecifier(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when both {@link WifiNetworkConfigBuilder#setWpa3Passphrase(String)} and + * {@link WifiNetworkConfigBuilder#setIsEnhancedOpen(} are invoked. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithBothWpa3PasphraseAndEnhancedOpen() { + new WifiNetworkConfigBuilder() + .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL)) + .setWpa3Passphrase(TEST_PRESHARED_KEY) + .setIsEnhancedOpen() + .buildNetworkSpecifier(); + } } |