diff options
647 files changed, 19839 insertions, 8520 deletions
diff --git a/Android.bp b/Android.bp index 88b2473169f5..25e738ccb3cf 100644 --- a/Android.bp +++ b/Android.bp @@ -731,8 +731,10 @@ java_defaults { "netd_aidl_interface-java", ], - // Loaded with System.loadLibrary by android.view.textclassifier required: [ + // TODO: remove gps_debug when the build system propagates "required" properly. + "gps_debug.conf", + // Loaded with System.loadLibrary by android.view.textclassifier "libmedia2_jni", ], diff --git a/CleanSpec.mk b/CleanSpec.mk index 6deda0caa9aa..478e4fe86d3b 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -248,6 +248,9 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.mediad $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.location.provider.jar) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.future.usb.accessory.jar) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.media.remotedisplay.jar) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/overlay/ExperimentNavigationBarSlim) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/overlay/ExperimentNavigationBarSlim) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/ExperimentNavigationBarSlim) # ****************************************************************** # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER # ****************************************************************** diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 5936ee4bc5cb..e731138ff40d 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -5,7 +5,9 @@ api_lint_hook = ${REPO_ROOT}/frameworks/base/tools/apilint/apilint_sha.sh ${PREU strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT} -hidden_api_txt_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT} +hidden_api_txt_checksorted_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT} + +hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclude.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT} ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES} diff --git a/api/current.txt b/api/current.txt index 6f2568824da4..d86b8b11a60e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -480,6 +480,10 @@ package android { field public static final int dashGap = 16843175; // 0x10101a7 field public static final int dashWidth = 16843174; // 0x10101a6 field public static final int data = 16842798; // 0x101002e + field public static final int dataRetentionTime = 16844189; // 0x101059d + field public static final int dataSentOffDevice = 16844186; // 0x101059a + field public static final int dataSharedWithThirdParty = 16844187; // 0x101059b + field public static final int dataUsedForMonetization = 16844188; // 0x101059c field public static final int datePickerDialogTheme = 16843948; // 0x10104ac field public static final int datePickerMode = 16843955; // 0x10104b3 field public static final int datePickerStyle = 16843612; // 0x101035c @@ -1312,7 +1316,6 @@ package android { field public static final int summaryColumn = 16843426; // 0x10102a2 field public static final int summaryOff = 16843248; // 0x10101f0 field public static final int summaryOn = 16843247; // 0x10101ef - field public static final int supportsAmbientMode = 16844173; // 0x101058d field public static final int supportsAssist = 16844016; // 0x10104f0 field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1 field public static final int supportsLocalInteraction = 16844047; // 0x101050f @@ -1496,6 +1499,7 @@ package android { field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344 field public static final int unselectedAlpha = 16843278; // 0x101020e field public static final int updatePeriodMillis = 16843344; // 0x1010250 + field public static final int usageInfoRequired = 16844185; // 0x1010599 field public static final int use32bitAbi = 16844053; // 0x1010515 field public static final int useAppZygote = 16844184; // 0x1010598 field public static final int useDefaultMargins = 16843641; // 0x1010379 @@ -3803,6 +3807,7 @@ package android.app { method public void overridePendingTransition(int, int); method public void postponeEnterTransition(); method public void recreate(); + method public void registerActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks); method public void registerForContextMenu(android.view.View); method public boolean releaseInstance(); method public final deprecated void removeDialog(int); @@ -3880,6 +3885,7 @@ package android.app { method public deprecated void stopManagingCursor(android.database.Cursor); method public void takeKeyEvents(boolean); method public void triggerSearch(java.lang.String, android.os.Bundle); + method public void unregisterActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks); method public void unregisterForContextMenu(android.view.View); field public static final int DEFAULT_KEYS_DIALER = 1; // 0x1 field public static final int DEFAULT_KEYS_DISABLE = 0; // 0x0 @@ -6352,7 +6358,6 @@ package android.app { method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager); 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; @@ -6511,6 +6516,7 @@ package android.app.admin { } public class DevicePolicyManager { + method public void addCrossProfileCalendarPackage(android.content.ComponentName, java.lang.String); method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int); method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String); method public int addOverrideApn(android.content.ComponentName, android.telephony.data.ApnSetting); @@ -6540,6 +6546,7 @@ package android.app.admin { method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName); method public boolean getCameraDisabled(android.content.ComponentName); method public deprecated java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException; + method public java.util.Set<java.lang.String> getCrossProfileCalendarPackages(android.content.ComponentName); method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName); method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName); method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName); @@ -6628,6 +6635,7 @@ package android.app.admin { method public int logoutUser(android.content.ComponentName); method public void reboot(android.content.ComponentName); method public void removeActiveAdmin(android.content.ComponentName); + method public boolean removeCrossProfileCalendarPackage(android.content.ComponentName, java.lang.String); method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String); method public boolean removeKeyPair(android.content.ComponentName, java.lang.String); method public boolean removeOverrideApn(android.content.ComponentName, int); @@ -9548,6 +9556,7 @@ package android.content { method public abstract java.io.File getNoBackupFilesDir(); method public abstract java.io.File getObbDir(); method public abstract java.io.File[] getObbDirs(); + method public abstract java.lang.String getOpPackageName(); method public abstract java.lang.String getPackageCodePath(); method public abstract android.content.pm.PackageManager getPackageManager(); method public abstract java.lang.String getPackageName(); @@ -9758,6 +9767,7 @@ package android.content { method public java.io.File getNoBackupFilesDir(); method public java.io.File getObbDir(); method public java.io.File[] getObbDirs(); + method public java.lang.String getOpPackageName(); method public java.lang.String getPackageCodePath(); method public android.content.pm.PackageManager getPackageManager(); method public java.lang.String getPackageName(); @@ -11168,8 +11178,8 @@ package android.content.pm { field public android.content.pm.ProviderInfo[] providers; field public android.content.pm.ActivityInfo[] receivers; field public android.content.pm.FeatureInfo[] reqFeatures; - field public java.lang.String[] requestedPermissions; - field public int[] requestedPermissionsFlags; + field public deprecated java.lang.String[] requestedPermissions; + field public deprecated int[] requestedPermissionsFlags; field public android.content.pm.ServiceInfo[] services; field public java.lang.String sharedUserId; field public int sharedUserLabel; @@ -11177,6 +11187,7 @@ package android.content.pm { field public android.content.pm.SigningInfo signingInfo; field public java.lang.String[] splitNames; field public int[] splitRevisionCodes; + field public android.content.pm.UsesPermissionInfo[] usesPermissions; field public deprecated int versionCode; field public java.lang.String versionName; } @@ -11652,6 +11663,7 @@ package android.content.pm { field public java.lang.String group; field public java.lang.CharSequence nonLocalizedDescription; field public deprecated int protectionLevel; + field public boolean usageInfoRequired; } public final class ProviderInfo extends android.content.pm.ComponentInfo implements android.os.Parcelable { @@ -11831,6 +11843,28 @@ package android.content.pm { field public static final android.os.Parcelable.Creator<android.content.pm.SigningInfo> CREATOR; } + public final class UsesPermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { + method public int describeContents(); + method public int getDataRetention(); + method public int getDataRetentionWeeks(); + method public int getDataSentOffDevice(); + method public int getDataSharedWithThirdParty(); + method public int getDataUsedForMonetization(); + method public int getFlags(); + method public java.lang.String getPermission(); + field public static final android.os.Parcelable.Creator<android.content.pm.UsesPermissionInfo> CREATOR; + field public static final int FLAG_REQUESTED_PERMISSION_GRANTED = 2; // 0x2 + field public static final int RETENTION_NOT_RETAINED = 1; // 0x1 + field public static final int RETENTION_SPECIFIED = 4; // 0x4 + field public static final int RETENTION_UNDEFINED = 0; // 0x0 + field public static final int RETENTION_UNLIMITED = 3; // 0x3 + field public static final int RETENTION_USER_SELECTED = 2; // 0x2 + field public static final int USAGE_NO = 3; // 0x3 + field public static final int USAGE_UNDEFINED = 0; // 0x0 + field public static final int USAGE_USER_TRIGGERED = 2; // 0x2 + field public static final int USAGE_YES = 1; // 0x1 + } + public final class VersionedPackage implements android.os.Parcelable { ctor public VersionedPackage(java.lang.String, int); ctor public VersionedPackage(java.lang.String, long); @@ -13940,6 +13974,7 @@ package android.graphics { } public final class Insets { + method public static android.graphics.Insets add(android.graphics.Insets, android.graphics.Insets); method public static android.graphics.Insets of(int, int, int, int); method public static android.graphics.Insets of(android.graphics.Rect); field public static final android.graphics.Insets NONE; @@ -14692,6 +14727,7 @@ package android.graphics { method public float getTranslationX(); method public float getTranslationY(); method public float getTranslationZ(); + method public long getUniqueId(); method public int getWidth(); method public boolean hasDisplayList(); method public boolean hasIdentityMatrix(); @@ -15045,6 +15081,7 @@ package android.graphics.drawable { method public void invalidateSelf(); method public boolean isAutoMirrored(); method public boolean isFilterBitmap(); + method public boolean isProjected(); method public boolean isStateful(); method public final boolean isVisible(); method public void jumpToCurrentState(); @@ -29408,11 +29445,24 @@ package android.net.wifi.p2p { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pConfig> CREATOR; + field public static final int GROUP_OWNER_BAND_2GHZ = 1; // 0x1 + field public static final int GROUP_OWNER_BAND_5GHZ = 2; // 0x2 + field public static final int GROUP_OWNER_BAND_AUTO = 0; // 0x0 field public java.lang.String deviceAddress; field public int groupOwnerIntent; field public android.net.wifi.WpsInfo wps; } + public static final class WifiP2pConfig.Builder { + ctor public WifiP2pConfig.Builder(); + method public android.net.wifi.p2p.WifiP2pConfig build(); + method public android.net.wifi.p2p.WifiP2pConfig.Builder enablePersistentMode(boolean); + method public android.net.wifi.p2p.WifiP2pConfig.Builder setDeviceAddress(android.net.MacAddress); + method public android.net.wifi.p2p.WifiP2pConfig.Builder setGroupOwnerBand(int); + method public android.net.wifi.p2p.WifiP2pConfig.Builder setNetworkName(java.lang.String); + method public android.net.wifi.p2p.WifiP2pConfig.Builder setPassphrase(java.lang.String); + } + public class WifiP2pDevice implements android.os.Parcelable { ctor public WifiP2pDevice(); ctor public WifiP2pDevice(android.net.wifi.p2p.WifiP2pDevice); @@ -29479,6 +29529,7 @@ package android.net.wifi.p2p { method public void clearServiceRequests(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener); method public void connect(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pConfig, android.net.wifi.p2p.WifiP2pManager.ActionListener); method public void createGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener); + method public void createGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pConfig, android.net.wifi.p2p.WifiP2pManager.ActionListener); method public void discoverPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener); method public void discoverServices(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener); method public android.net.wifi.p2p.WifiP2pManager.Channel initialize(android.content.Context, android.os.Looper, android.net.wifi.p2p.WifiP2pManager.ChannelListener); @@ -29486,7 +29537,10 @@ package android.net.wifi.p2p { method public void removeLocalService(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceInfo, android.net.wifi.p2p.WifiP2pManager.ActionListener); method public void removeServiceRequest(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceRequest, android.net.wifi.p2p.WifiP2pManager.ActionListener); method public void requestConnectionInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener); + method public void requestDiscoveryState(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.DiscoveryStateListener); method public void requestGroupInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.GroupInfoListener); + method public void requestNetworkInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.NetworkInfoListener); + method public void requestP2pState(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.P2pStateListener); method public void requestPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.PeerListListener); method public void setDnsSdResponseListeners(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener, android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener); method public void setServiceResponseListener(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ServiceResponseListener); @@ -29531,6 +29585,10 @@ package android.net.wifi.p2p { method public abstract void onConnectionInfoAvailable(android.net.wifi.p2p.WifiP2pInfo); } + public static abstract interface WifiP2pManager.DiscoveryStateListener { + method public abstract void onDiscoveryStateAvailable(int); + } + public static abstract interface WifiP2pManager.DnsSdServiceResponseListener { method public abstract void onDnsSdServiceAvailable(java.lang.String, java.lang.String, android.net.wifi.p2p.WifiP2pDevice); } @@ -29543,6 +29601,14 @@ package android.net.wifi.p2p { method public abstract void onGroupInfoAvailable(android.net.wifi.p2p.WifiP2pGroup); } + public static abstract interface WifiP2pManager.NetworkInfoListener { + method public abstract void onNetworkInfoAvailable(android.net.NetworkInfo); + } + + public static abstract interface WifiP2pManager.P2pStateListener { + method public abstract void onP2pStateAvailable(int); + } + public static abstract interface WifiP2pManager.PeerListListener { method public abstract void onPeersAvailable(android.net.wifi.p2p.WifiP2pDeviceList); } @@ -37394,6 +37460,17 @@ package android.provider { field public static final java.lang.String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/radio"; } + public static abstract interface MediaStore.DownloadColumns implements android.provider.MediaStore.MediaColumns { + field public static final java.lang.String DOWNLOAD_URI = "download_uri"; + field public static final java.lang.String REFERER_URI = "referer_uri"; + } + + public static final class MediaStore.Downloads implements android.provider.MediaStore.DownloadColumns { + method public static android.net.Uri getContentUri(java.lang.String); + field public static final android.net.Uri EXTERNAL_CONTENT_URI; + field public static final android.net.Uri INTERNAL_CONTENT_URI; + } + public static final class MediaStore.Files { ctor public MediaStore.Files(); method public static android.net.Uri getContentUri(java.lang.String); @@ -37485,7 +37562,9 @@ package android.provider { public static class MediaStore.PendingParams { ctor public MediaStore.PendingParams(android.net.Uri, java.lang.String, java.lang.String); + method public void setDownloadUri(android.net.Uri); method public void setPrimaryDirectory(java.lang.String); + method public void setRefererUri(android.net.Uri); method public void setSecondaryDirectory(java.lang.String); } @@ -40865,11 +40944,9 @@ package android.service.wallpaper { method public int getDesiredMinimumHeight(); method public int getDesiredMinimumWidth(); method public android.view.SurfaceHolder getSurfaceHolder(); - method public boolean isInAmbientMode(); method public boolean isPreview(); method public boolean isVisible(); method public void notifyColorsChanged(); - method public void onAmbientModeChanged(boolean, boolean); method public void onApplyWindowInsets(android.view.WindowInsets); method public android.os.Bundle onCommand(java.lang.String, int, int, int, android.os.Bundle, boolean); method public android.app.WallpaperColors onComputeColors(); @@ -49249,6 +49326,7 @@ package android.view { method public void setLayoutDirection(int); method public void setLayoutParams(android.view.ViewGroup.LayoutParams); method public final void setLeft(int); + method public void setLeftTopRightBottom(int, int, int, int); method public void setLongClickable(boolean); method protected final void setMeasuredDimension(int, int); method public void setMinimumHeight(int); diff --git a/api/system-current.txt b/api/system-current.txt index 53e819dfa289..6cfcad3c4b9f 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -17,6 +17,7 @@ package android { field public static final java.lang.String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING"; field public static final java.lang.String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE"; field public static final java.lang.String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"; + field public static final java.lang.String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER"; field public static final java.lang.String BACKUP = "android.permission.BACKUP"; field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS"; field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; @@ -158,6 +159,7 @@ package android { field public static final java.lang.String REAL_GET_TASKS = "android.permission.REAL_GET_TASKS"; field public static final java.lang.String REBOOT = "android.permission.REBOOT"; field public static final java.lang.String RECEIVE_DATA_ACTIVITY_CHANGE = "android.permission.RECEIVE_DATA_ACTIVITY_CHANGE"; + field public static final java.lang.String RECEIVE_DEVICE_CUSTOMIZATION_READY = "android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY"; field public static final java.lang.String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST"; field public static final java.lang.String RECEIVE_WIFI_CREDENTIAL_CHANGE = "android.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE"; field public static final java.lang.String RECOVERY = "android.permission.RECOVERY"; @@ -171,6 +173,7 @@ package android { field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT"; field public static final java.lang.String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS"; field public static final java.lang.String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS"; + field public static final java.lang.String SEND_DEVICE_CUSTOMIZATION_READY = "android.permission.SEND_DEVICE_CUSTOMIZATION_READY"; field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE"; field public static final java.lang.String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS"; field public static final java.lang.String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION"; @@ -222,6 +225,7 @@ package android { field public static final int isVrOnly = 16844152; // 0x1010578 field public static final int requiredSystemPropertyName = 16844133; // 0x1010565 field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566 + field public static final int supportsAmbientMode = 16844173; // 0x101058d field public static final int userRestriction = 16844164; // 0x1010584 } @@ -553,6 +557,10 @@ package android.app { method public void onVrStateChanged(boolean); } + public final class WallpaperInfo implements android.os.Parcelable { + method public boolean supportsAmbientMode(); + } + public class WallpaperManager { method public void clearWallpaper(int, int); method public void setDisplayOffset(android.os.IBinder, int, int); @@ -1055,6 +1063,7 @@ package android.content { field public static final java.lang.String ACTION_BATTERY_LEVEL_CHANGED = "android.intent.action.BATTERY_LEVEL_CHANGED"; field public static final java.lang.String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY"; field public static final java.lang.String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED"; + field public static final java.lang.String ACTION_DEVICE_CUSTOMIZATION_READY = "android.intent.action.DEVICE_CUSTOMIZATION_READY"; field public static final java.lang.String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET"; field public static final java.lang.String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON"; field public static final java.lang.String ACTION_INSTALL_INSTANT_APP_PACKAGE = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE"; @@ -1234,6 +1243,7 @@ package android.content.pm { method public abstract void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener); method public void replacePreferredActivity(android.content.IntentFilter, int, java.util.List<android.content.ComponentName>, android.content.ComponentName); method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle); + method public void sendDeviceCustomizationReadyBroadcast(); method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int); method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence); method public deprecated java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String); @@ -2861,6 +2871,7 @@ package android.media { field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1 field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4 field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2 + field public static final int FLAG_FROM_KEY = 4096; // 0x1000 } public static abstract class AudioManager.AudioServerStateCallback { @@ -3675,6 +3686,10 @@ package android.net.wifi { public class WifiManager { method public void connect(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener); + method public void connect(int, android.net.wifi.WifiManager.ActionListener); + method public void disable(int, android.net.wifi.WifiManager.ActionListener); + method public void disableEphemeralNetwork(java.lang.String); + method public void forget(int, android.net.wifi.WifiManager.ActionListener); method public java.util.List<android.net.wifi.WifiConfiguration> getAllMatchingWifiConfigs(java.util.List<android.net.wifi.ScanResult>); method public java.util.List<android.net.wifi.hotspot2.OsuProvider> getMatchingOsuProviders(java.util.List<android.net.wifi.ScanResult>); method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks(); @@ -3686,6 +3701,7 @@ package android.net.wifi { method public boolean isWifiApEnabled(); method public boolean isWifiScannerSupported(); method public void registerNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback, android.os.Handler); + method public void save(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener); method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration); method public boolean startScan(android.os.WorkSource); method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback); @@ -5096,6 +5112,7 @@ package android.service.notification { ctor public NotificationAssistantService(); method public final void adjustNotification(android.service.notification.Adjustment); method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>); + method public void onActionClicked(java.lang.String, android.app.Notification.Action, int); method public final android.os.IBinder onBind(android.content.Intent); method public void onNotificationDirectReply(java.lang.String); method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification); @@ -5329,6 +5346,15 @@ package android.service.trust { } +package android.service.wallpaper { + + public class WallpaperService.Engine { + method public boolean isInAmbientMode(); + method public void onAmbientModeChanged(boolean, long); + } + +} + package android.telecom { public deprecated class AudioState implements android.os.Parcelable { diff --git a/api/test-current.txt b/api/test-current.txt index 738caeca1b64..b871c78571ad 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -61,6 +61,7 @@ package android.app { } public class ActivityTaskManager { + method public void clearLaunchParamsForPackages(java.util.List<java.lang.String>); method public java.lang.String listAllStacks(); method public void moveTaskToStack(int, int, boolean); method public boolean moveTopActivityToPinnedStack(int, android.graphics.Rect); @@ -81,6 +82,10 @@ package android.app { field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0 } + public class AppDetailsActivity extends android.app.Activity { + ctor public AppDetailsActivity(); + } + public class AppOpsManager { method public java.util.List<android.app.AppOpsManager.HistoricalPackageOps> getAllHistoricPackagesOps(java.lang.String[], long, long); method public android.app.AppOpsManager.HistoricalPackageOps getHistoricalPackagesOps(int, java.lang.String, java.lang.String[], long, long); @@ -300,16 +305,11 @@ package android.content { public abstract class Context { method public android.content.Context createPackageContextAsUser(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract java.lang.String getOpPackageName(); method public android.os.UserHandle getUser(); method public int getUserId(); method public void setAutofillCompatibilityEnabled(boolean); } - public class ContextWrapper extends android.content.Context { - method public java.lang.String getOpPackageName(); - } - } package android.content.pm { @@ -1155,6 +1155,7 @@ package android.service.notification { ctor public NotificationAssistantService(); method public final void adjustNotification(android.service.notification.Adjustment); method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>); + method public void onActionClicked(java.lang.String, android.app.Notification.Action, int); method public final android.os.IBinder onBind(android.content.Intent); method public void onNotificationDirectReply(java.lang.String); method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 2fc7e03ca91f..c8405a279f7d 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -1338,8 +1338,9 @@ message PhysicalDropDetected { * Log bucketed battery charge cycles. * * Each bucket represents cycles of the battery past - * a given charge point. For example, bucket 1 is the - * lowest 1/8th of the battery, and bucket 8 is 100%. + * a given charge point. For example, if 10 cycle buckets are + * initialized, bucket 1 is the lowest 1/10th of the battery, + * and bucket 10 is 100%. * * Logged from: * /sys/class/power_supply/bms/cycle_count, via Vendor. @@ -1353,6 +1354,8 @@ message ChargeCyclesReported { optional int32 cycle_bucket_6 = 6; optional int32 cycle_bucket_7 = 7; optional int32 cycle_bucket_8 = 8; + optional int32 cycle_bucket_9 = 9; + optional int32 cycle_bucket_10 = 10; } /** diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp index 3eb05a90e3b4..4e4b8f35726d 100644 --- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp +++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp @@ -59,7 +59,7 @@ bool StatsCompanionServicePuller::PullInternal(vector<shared_ptr<LogEvent> >* da } data->clear(); for (const StatsLogEventWrapper& it : returned_value) { - data->push_back(make_shared<LogEvent>(it)); + LogEvent::createLogEvents(it, *data); } VLOG("StatsCompanionServicePuller::pull succeeded for %d", mTagId); return true; diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp index e3f251a5263d..f501574d6afc 100644 --- a/cmds/statsd/src/external/StatsPuller.cpp +++ b/cmds/statsd/src/external/StatsPuller.cpp @@ -32,9 +32,10 @@ using std::lock_guard; sp<UidMap> StatsPuller::mUidMap = nullptr; void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; } -// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently StatsPuller::StatsPuller(const int tagId) : mTagId(tagId) { + // Pullers can cause significant impact to system health and battery. + // So that we don't pull too frequently. mCoolDownNs = StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.coolDownNs; VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs); } @@ -64,8 +65,8 @@ bool StatsPuller::Pull(const int64_t elapsedTimeNs, std::vector<std::shared_ptr< data->setLogdWallClockTimestampNs(wallClockTimeNs); } if (ret && mCachedData.size() > 0) { - mergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId); - (*data) = mCachedData; + mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId); + (*data) = mCachedData; } StatsdStats::getInstance().notePullDelay(mTagId, getElapsedRealtimeNs() - elapsedTimeNs); return ret; diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 87a065b81813..f9b79823741b 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -54,52 +54,42 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { // wifi_bytes_transfer {android::util::WIFI_BYTES_TRANSFER, {{2, 3, 4, 5}, - {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}}, // wifi_bytes_transfer_by_fg_bg {android::util::WIFI_BYTES_TRANSFER_BY_FG_BG, {{3, 4, 5, 6}, - {2}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}}, // mobile_bytes_transfer {android::util::MOBILE_BYTES_TRANSFER, {{2, 3, 4, 5}, - {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}}, // mobile_bytes_transfer_by_fg_bg {android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG, {{3, 4, 5, 6}, - {2}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}}, // bluetooth_bytes_transfer {android::util::BLUETOOTH_BYTES_TRANSFER, {{2, 3}, - {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}}, // kernel_wakelock {android::util::KERNEL_WAKELOCK, - {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}}, + {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}}, // subsystem_sleep_state {android::util::SUBSYSTEM_SLEEP_STATE, - {{}, {}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}}, + {{}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}}, // on_device_power_measurement - {android::util::ON_DEVICE_POWER_MEASUREMENT, - {{}, {}, 1 * NS_PER_SEC, new PowerStatsPuller()}}, + {android::util::ON_DEVICE_POWER_MEASUREMENT, {{}, 1 * NS_PER_SEC, new PowerStatsPuller()}}, // cpu_time_per_freq {android::util::CPU_TIME_PER_FREQ, - {{3}, - {2}, - 1 * NS_PER_SEC, - new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}}, + {{3}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}}, // cpu_time_per_uid {android::util::CPU_TIME_PER_UID, {{2, 3}, - {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}}, // cpu_time_per_uid_freq @@ -107,169 +97,140 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader {android::util::CPU_TIME_PER_UID_FREQ, {{4}, - {2, 3}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}}, // cpu_active_time // the throttling is 3sec, handled in // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader {android::util::CPU_ACTIVE_TIME, - {{2}, - {}, - 1 * NS_PER_SEC, - new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}}, + {{2}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}}, // cpu_cluster_time // the throttling is 3sec, handled in // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader {android::util::CPU_CLUSTER_TIME, - {{3}, - {2}, - 1 * NS_PER_SEC, - new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}}, + {{3}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}}, // wifi_activity_energy_info {android::util::WIFI_ACTIVITY_INFO, - {{}, - {}, - 1 * NS_PER_SEC, - new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}}, + {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}}, // modem_activity_info {android::util::MODEM_ACTIVITY_INFO, - {{}, - {}, - 1 * NS_PER_SEC, - new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}}, + {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}}, // bluetooth_activity_info {android::util::BLUETOOTH_ACTIVITY_INFO, {{}, - {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}}, // system_elapsed_realtime {android::util::SYSTEM_ELAPSED_REALTIME, {{}, - {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}}, // system_uptime {android::util::SYSTEM_UPTIME, - {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}}, + {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}}, // remaining_battery_capacity {android::util::REMAINING_BATTERY_CAPACITY, {{}, - {}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}}, // full_battery_capacity {android::util::FULL_BATTERY_CAPACITY, {{}, - {}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}}, // battery_voltage {android::util::BATTERY_VOLTAGE, - {{}, {}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}}, - // battery_voltage + {{}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}}, + // battery_level {android::util::BATTERY_LEVEL, - {{}, {}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}}, + {{}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}}, // process_memory_state {android::util::PROCESS_MEMORY_STATE, {{4, 5, 6, 7, 8, 9}, - {2, 3, 10}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}}, // native_process_memory_state {android::util::NATIVE_PROCESS_MEMORY_STATE, {{3, 4, 5, 6}, - {2, 7}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}}, {android::util::PROCESS_MEMORY_HIGH_WATER_MARK, {{3}, - {2}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}}, // temperature - {android::util::TEMPERATURE, {{}, {}, 1 * NS_PER_SEC, new ResourceThermalManagerPuller()}}, + {android::util::TEMPERATURE, {{}, 1 * NS_PER_SEC, new ResourceThermalManagerPuller()}}, // binder_calls {android::util::BINDER_CALLS, {{4, 5, 6, 8, 12}, - {2, 3, 7, 9, 10, 11, 13}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::BINDER_CALLS)}}, // binder_calls_exceptions {android::util::BINDER_CALLS_EXCEPTIONS, {{}, - {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}}, // looper_stats {android::util::LOOPER_STATS, {{5, 6, 7, 8, 9}, - {2, 3, 4, 10}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::LOOPER_STATS)}}, // Disk Stats {android::util::DISK_STATS, - {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_STATS)}}, + {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_STATS)}}, // Directory usage {android::util::DIRECTORY_USAGE, - {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}}, + {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}}, // Size of app's code, data, and cache {android::util::APP_SIZE, - {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}}, + {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}}, // Size of specific categories of files. Eg. Music. {android::util::CATEGORY_SIZE, - {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}}, + {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}}, // Number of fingerprints registered to each user. {android::util::NUM_FINGERPRINTS, - {{}, - {}, - 1 * NS_PER_SEC, - new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}}, + {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}}, // ProcStats. {android::util::PROC_STATS, - {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}}, + {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}}, // ProcStatsPkgProc. {android::util::PROC_STATS_PKG_PROC, - {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}}, + {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}}, // Disk I/O stats per uid. {android::util::DISK_IO, - {{2,3,4,5,6,7,8,9,10,11}, - {}, + {{2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 3 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_IO)}}, // PowerProfile constants for power model calculations. {android::util::POWER_PROFILE, - {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::POWER_PROFILE)}}, + {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::POWER_PROFILE)}}, // Process cpu stats. Min cool-down is 5 sec, inline with what AcitivityManagerService uses. {android::util::PROCESS_CPU_TIME, - {{} /* additive fields */, {} /* non additive fields */, - 5 * NS_PER_SEC /* min cool-down in seconds*/, - new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}}, + {{} /* additive fields */, + 5 * NS_PER_SEC /* min cool-down in seconds*/, + new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}}, {android::util::CPU_TIME_PER_THREAD_FREQ, {{7}, - {2, 3, 4, 5, 6}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}}, // DeviceCalculatedPowerUse. {android::util::DEVICE_CALCULATED_POWER_USE, - {{}, {}, 1 * NS_PER_SEC, + {{}, + 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}}, // DeviceCalculatedPowerBlameUid. {android::util::DEVICE_CALCULATED_POWER_BLAME_UID, - {{}, {}, // BatteryStats already merged isolated with host ids so it's unnecessary here. + {{}, // BatteryStats already merged isolated with host ids so it's unnecessary here. 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}}, // DeviceCalculatedPowerBlameOther. {android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER, - {{}, {}, + {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}}, // BuildInformation. {android::util::BUILD_INFORMATION, - {{}, {}, - 1 * NS_PER_SEC, - new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}}, + {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}}, }; StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) { diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h index bbf5d9dc69db..335073613a4a 100644 --- a/cmds/statsd/src/external/StatsPullerManager.h +++ b/cmds/statsd/src/external/StatsPullerManager.h @@ -36,9 +36,6 @@ typedef struct { // The field numbers of the fields that need to be summed when merging // isolated uid with host uid. std::vector<int> additiveFields; - // The field numbers of the fields that can't be merged when merging - // data belong to isolated uid and host uid. - std::vector<int> nonAdditiveFields; // How long should the puller wait before doing an actual pull again. Default // 1 sec. Set this to 0 if this is handled elsewhere. int64_t coolDownNs = 1 * NS_PER_SEC; diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp index ea7fa972cb9c..0b9b6abfd6d6 100644 --- a/cmds/statsd/src/external/puller_util.cpp +++ b/cmds/statsd/src/external/puller_util.cpp @@ -25,67 +25,13 @@ namespace android { namespace os { namespace statsd { +using std::list; using std::map; +using std::set; using std::shared_ptr; +using std::sort; using std::vector; -namespace { -bool shouldMerge(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs, - const vector<int>& nonAdditiveFields) { - const auto& l_values = lhs->getValues(); - const auto& r_values = rhs->getValues(); - - for (size_t i : nonAdditiveFields) { - // We store everything starting from index 0, so we need to use i-1 - if (!(l_values.size() > i - 1 && r_values.size() > i - 1 && - l_values[i - 1].mValue == r_values[i - 1].mValue)) { - return false; - } - } - return true; -} - -// merge rhs to lhs -// when calling this function, all sanity check should be done already. -// e.g., index boundary, nonAdditiveFields matching etc. -bool mergeEvent(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs, - const vector<int>& additiveFields) { - vector<FieldValue>* host_values = lhs->getMutableValues(); - const auto& child_values = rhs->getValues(); - for (int i : additiveFields) { - Value& host = (*host_values)[i - 1].mValue; - const Value& child = (child_values[i - 1]).mValue; - if (child.getType() != host.getType()) { - return false; - } - switch (child.getType()) { - case INT: - host.setInt(host.int_value + child.int_value); - break; - case LONG: - host.setLong(host.long_value + child.long_value); - break; - default: - ALOGE("Tried to merge 2 fields with unsupported type"); - return false; - } - } - return true; -} - -bool tryMerge(vector<shared_ptr<LogEvent>>& data, int child_pos, const vector<int>& host_pos, - const vector<int>& nonAdditiveFields, const vector<int>& additiveFields) { - for (const auto& pos : host_pos) { - if (shouldMerge(data[pos], data[child_pos], nonAdditiveFields) && - mergeEvent(data[pos], data[child_pos], additiveFields)) { - return true; - } - } - return false; -} - -} // namespace - /** * Process all data and merge isolated with host if necessary. * For example: @@ -95,7 +41,7 @@ bool tryMerge(vector<shared_ptr<LogEvent>>& data, int child_pos, const vector<in * int byte_send = 3; * int byte_recv = 4; * } - * additive fields are {3, 4}, non-additive field is {2} + * additive fields are {3, 4} * If we pulled the following events (uid1_child is an isolated uid which maps to uid1): * [uid1, fg, 100, 200] * [uid1_child, fg, 100, 200] @@ -104,65 +50,119 @@ bool tryMerge(vector<shared_ptr<LogEvent>>& data, int child_pos, const vector<in * We want to merge them and results should be: * [uid1, fg, 200, 400] * [uid1, bg, 100, 200] + * + * All atoms should be of the same tagId. All fields should be present. */ -void mergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap, - int tagId) { +void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap, + int tagId) { if (StatsPullerManager::kAllPullAtomInfo.find(tagId) == StatsPullerManager::kAllPullAtomInfo.end()) { VLOG("Unknown pull atom id %d", tagId); return; } - int uidField; - auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId); - if (it == android::util::AtomsInfo::kAtomsWithUidField.end()) { - VLOG("No uid to merge for atom %d", tagId); + if ((android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) == + android::util::AtomsInfo::kAtomsWithAttributionChain.end()) && + (android::util::AtomsInfo::kAtomsWithUidField.find(tagId) == + android::util::AtomsInfo::kAtomsWithUidField.end())) { + VLOG("No uid or attribution chain to merge, atom %d", tagId); return; - } else { - uidField = it->second; // uidField is the field number in proto, } - const vector<int>& additiveFields = - StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.additiveFields; - const vector<int>& nonAdditiveFields = - StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.nonAdditiveFields; - - // map of host uid to their position in the original vector - map<int, vector<int>> hostPosition; - vector<bool> toRemove = vector<bool>(data.size(), false); - for (size_t i = 0; i < data.size(); i++) { - vector<FieldValue>* valueList = data[i]->getMutableValues(); - - int uid; - if (uidField > 0 && (int)data[i]->getValues().size() >= uidField && - (data[i]->getValues())[uidField - 1].mValue.getType() == INT) { - uid = (*data[i]->getMutableValues())[uidField - 1].mValue.int_value; - } else { - ALOGE("Malformed log, uid not found. %s", data[i]->ToString().c_str()); - continue; + // 1. Map all isolated uid in-place to host uid + for (shared_ptr<LogEvent>& event : data) { + if (event->GetTagId() != tagId) { + ALOGE("Wrong atom. Expecting %d, got %d", tagId, event->GetTagId()); + return; } - - const int hostUid = uidMap->getHostUidOrSelf(uid); - - if (hostUid != uid) { - (*valueList)[0].mValue.setInt(hostUid); - } - if (hostPosition.find(hostUid) == hostPosition.end()) { - hostPosition[hostUid].push_back(i); + if (android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) != + android::util::AtomsInfo::kAtomsWithAttributionChain.end()) { + for (auto& value : *(event->getMutableValues())) { + if (value.mField.getPosAtDepth(0) > kAttributionField) { + break; + } + if (isAttributionUidField(value)) { + const int hostUid = uidMap->getHostUidOrSelf(value.mValue.int_value); + value.mValue.setInt(hostUid); + } + } } else { - if (tryMerge(data, i, hostPosition[hostUid], nonAdditiveFields, additiveFields)) { - toRemove[i] = true; - } else { - hostPosition[hostUid].push_back(i); + auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId); + if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) { + int uidField = it->second; // uidField is the field number in proto, + // starting from 1 + if (uidField > 0 && (int)event->getValues().size() >= uidField && + (event->getValues())[uidField - 1].mValue.getType() == INT) { + Value& value = (*event->getMutableValues())[uidField - 1].mValue; + const int hostUid = uidMap->getHostUidOrSelf(value.int_value); + value.setInt(hostUid); + } else { + ALOGE("Malformed log, uid not found. %s", event->ToString().c_str()); + return; + } } } } + // 2. sort the data, bit-wise + sort(data.begin(), data.end(), + [](const shared_ptr<LogEvent>& lhs, const shared_ptr<LogEvent>& rhs) { + if (lhs->size() != rhs->size()) { + return lhs->size() < rhs->size(); + } + const std::vector<FieldValue>& lhsValues = lhs->getValues(); + const std::vector<FieldValue>& rhsValues = rhs->getValues(); + for (int i = 0; i < (int)lhs->size(); i++) { + if (lhsValues[i] != rhsValues[i]) { + return lhsValues[i] < rhsValues[i]; + } + } + return false; + }); + vector<shared_ptr<LogEvent>> mergedData; - for (size_t i = 0; i < toRemove.size(); i++) { - if (!toRemove[i]) { + const vector<int>& additiveFieldsVec = + StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.additiveFields; + const set<int> additiveFields(additiveFieldsVec.begin(), additiveFieldsVec.end()); + bool needMerge = true; + + // 3. do the merge. + // The loop invariant is this: for every event, check if it differs on + // non-additive fields, or have different attribution chain length. + // If so, no need to merge, add itself to the result. + // Otherwise, merge the value onto the one immediately next to it. + for (int i = 0; i < (int)data.size() - 1; i++) { + // Size different, must be different chains. + if (data[i]->size() != data[i + 1]->size()) { mergedData.push_back(data[i]); + continue; + } + vector<FieldValue>* lhsValues = data[i]->getMutableValues(); + vector<FieldValue>* rhsValues = data[i + 1]->getMutableValues(); + needMerge = true; + for (int p = 0; p < (int)lhsValues->size(); p++) { + if ((*lhsValues)[p] != (*rhsValues)[p]) { + int pos = (*lhsValues)[p].mField.getPosAtDepth(0); + // Differ on non-additive field, abort. + if (additiveFields.find(pos) == additiveFields.end()) { + needMerge = false; + break; + } + } + } + if (!needMerge) { + mergedData.push_back(data[i]); + continue; + } + // This should be infrequent operation. + for (int p = 0; p < (int)lhsValues->size(); p++) { + int pos = (*lhsValues)[p].mField.getPosAtDepth(0); + if (additiveFields.find(pos) != additiveFields.end()) { + (*rhsValues)[p].mValue += (*lhsValues)[p].mValue; + } } } + mergedData.push_back(data.back()); + data.clear(); data = mergedData; } diff --git a/cmds/statsd/src/external/puller_util.h b/cmds/statsd/src/external/puller_util.h index fd4a4a2a5755..f703e6c589c1 100644 --- a/cmds/statsd/src/external/puller_util.h +++ b/cmds/statsd/src/external/puller_util.h @@ -25,8 +25,8 @@ namespace android { namespace os { namespace statsd { -void mergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data, - const sp<UidMap>& uidMap, int tagId); +void mapAndMergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data, + const sp<UidMap>& uidMap, int tagId); } // namespace statsd } // namespace os diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 625294ce5e49..8d61aba432d4 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -41,13 +41,28 @@ LogEvent::LogEvent(log_msg& msg) { } } -LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper) { +LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex) { mTagId = statsLogEventWrapper.getTagId(); mLogdTimestampNs = statsLogEventWrapper.getWallClockTimeNs(); mElapsedTimestampNs = statsLogEventWrapper.getElapsedRealTimeNs(); mLogUid = 0; + int workChainPosOffset = 0; + if (workChainIndex != -1) { + const WorkChain& wc = statsLogEventWrapper.getWorkChains()[workChainIndex]; + // chains are at field 1, level 2 + int depth = 2; + for (int i = 0; i < (int)wc.uids.size(); i++) { + int pos[] = {1, i + 1, 1}; + mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(wc.uids[i]))); + pos[2]++; + mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(wc.tags[i]))); + mValues.back().mField.decorateLastPos(2); + } + mValues.back().mField.decorateLastPos(1); + workChainPosOffset = 1; + } for (int i = 0; i < (int)statsLogEventWrapper.getElements().size(); i++) { - Field field(statsLogEventWrapper.getTagId(), getSimpleField(i + 1)); + Field field(statsLogEventWrapper.getTagId(), getSimpleField(i + 1 + workChainPosOffset)); switch (statsLogEventWrapper.getElements()[i].type) { case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::INT: mValues.push_back( @@ -79,6 +94,17 @@ LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper) { } } +void LogEvent::createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper, + std::vector<std::shared_ptr<LogEvent>>& logEvents) { + if (statsLogEventWrapper.getWorkChains().size() == 0) { + logEvents.push_back(std::make_shared<LogEvent>(statsLogEventWrapper, -1)); + } else { + for (size_t i = 0; i < statsLogEventWrapper.getWorkChains().size(); i++) { + logEvents.push_back(std::make_shared<LogEvent>(statsLogEventWrapper, i)); + } + } +} + LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs) { mLogdTimestampNs = wallClockTimestampNs; mTagId = tagId; @@ -653,7 +679,7 @@ float LogEvent::GetFloat(size_t key, status_t* err) const { string LogEvent::ToString() const { string result; - result += StringPrintf("{ %lld %lld (%d)", (long long)mLogdTimestampNs, + result += StringPrintf("{ uid(%d) %lld %lld (%d)", mLogUid, (long long)mLogdTimestampNs, (long long)mElapsedTimestampNs, mTagId); for (const auto& value : mValues) { result += diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 3d5b2abc7e3a..5408d17b02b2 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -65,7 +65,16 @@ public: */ explicit LogEvent(log_msg& msg); - explicit LogEvent(const StatsLogEventWrapper& statsLogEventWrapper); + /** + * Creates LogEvent from StatsLogEventWrapper. + */ + static void createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper, + std::vector<std::shared_ptr<LogEvent>>& logEvents); + + /** + * Construct one LogEvent from a StatsLogEventWrapper with the i-th work chain. -1 if no chain. + */ + explicit LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex); /** * Constructs a LogEvent with synthetic data for testing. Must call init() before reading. diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index c3912eebbc11..9fe84dcf93aa 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -165,7 +165,7 @@ private: const bool mSkipZeroDiffOutput; FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition); - FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering); + FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering); FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset); FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset); FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition); diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 4ac55b5c14b6..b317361a5a3d 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -454,6 +454,16 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t ALOGW("cannot find \"what\" in ValueMetric \"%lld\"", (long long)metric.id()); return false; } + if (!metric.has_value_field()) { + ALOGW("cannot find \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id()); + return false; + } + std::vector<Matcher> fieldMatchers; + translateFieldMatcher(metric.value_field(), &fieldMatchers); + if (fieldMatchers.size() < 1) { + ALOGW("incorrect \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id()); + return false; + } int metricIndex = allMetricProducers.size(); metricMap.insert({metric.id(), metricIndex}); diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp index 504c5864f2ec..f1310db03d45 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -343,9 +343,11 @@ void writeFieldValueTreeToStreamHelper(int tagId, const std::vector<FieldValue>& } } if (isBytesField) { - protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum, - (const char*)dim.mValue.str_value.c_str(), - dim.mValue.str_value.length()); + if (dim.mValue.str_value.length() > 0) { + protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum, + (const char*)dim.mValue.str_value.c_str(), + dim.mValue.str_value.length()); + } } else { protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value); } diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp index 6384757b7fc8..3a5be43ed695 100644 --- a/cmds/statsd/tests/LogEvent_test.cpp +++ b/cmds/statsd/tests/LogEvent_test.cpp @@ -394,6 +394,167 @@ TEST(LogEventTest, TestKeyValuePairsEvent) { EXPECT_EQ(1.1f, item16.mValue.float_value); } +TEST(LogEventTest, TestStatsLogEventWrapperNoChain) { + Parcel parcel; + // tag id + parcel.writeInt32(1); + // elapsed realtime + parcel.writeInt64(1111L); + // wallclock time + parcel.writeInt64(2222L); + // no chain + parcel.writeInt32(0); + // 2 data + parcel.writeInt32(2); + // int 6 + parcel.writeInt32(1); + parcel.writeInt32(6); + // long 10 + parcel.writeInt32(2); + parcel.writeInt64(10); + parcel.setDataPosition(0); + + StatsLogEventWrapper statsLogEventWrapper; + EXPECT_EQ(NO_ERROR, statsLogEventWrapper.readFromParcel(&parcel)); + EXPECT_EQ(1, statsLogEventWrapper.getTagId()); + EXPECT_EQ(1111L, statsLogEventWrapper.getElapsedRealTimeNs()); + EXPECT_EQ(2222L, statsLogEventWrapper.getWallClockTimeNs()); + EXPECT_EQ(0, statsLogEventWrapper.getWorkChains().size()); + EXPECT_EQ(2, statsLogEventWrapper.getElements().size()); + EXPECT_EQ(6, statsLogEventWrapper.getElements()[0].int_value); + EXPECT_EQ(10L, statsLogEventWrapper.getElements()[1].long_value); + LogEvent event(statsLogEventWrapper, -1); + EXPECT_EQ(1, event.GetTagId()); + EXPECT_EQ(1111L, event.GetElapsedTimestampNs()); + EXPECT_EQ(2222L, event.GetLogdTimestampNs()); + EXPECT_EQ(2, event.size()); + EXPECT_EQ(6, event.getValues()[0].mValue.int_value); + EXPECT_EQ(10, event.getValues()[1].mValue.long_value); +} + +TEST(LogEventTest, TestStatsLogEventWrapperWithChain) { + Parcel parcel; + // tag id + parcel.writeInt32(1); + // elapsed realtime + parcel.writeInt64(1111L); + // wallclock time + parcel.writeInt64(2222L); + // 3 chains + parcel.writeInt32(3); + // chain1, 2 nodes (1, "tag1") (2, "tag2") + parcel.writeInt32(2); + parcel.writeInt32(1); + parcel.writeString16(String16("tag1")); + parcel.writeInt32(2); + parcel.writeString16(String16("tag2")); + // chain2, 1 node (3, "tag3") + parcel.writeInt32(1); + parcel.writeInt32(3); + parcel.writeString16(String16("tag3")); + // chain3, 2 nodes (4, "") (5, "") + parcel.writeInt32(2); + parcel.writeInt32(4); + parcel.writeString16(String16("")); + parcel.writeInt32(5); + parcel.writeString16(String16("")); + // 2 data + parcel.writeInt32(2); + // int 6 + parcel.writeInt32(1); + parcel.writeInt32(6); + // long 10 + parcel.writeInt32(2); + parcel.writeInt64(10); + parcel.setDataPosition(0); + + StatsLogEventWrapper statsLogEventWrapper; + EXPECT_EQ(NO_ERROR, statsLogEventWrapper.readFromParcel(&parcel)); + EXPECT_EQ(1, statsLogEventWrapper.getTagId()); + EXPECT_EQ(1111L, statsLogEventWrapper.getElapsedRealTimeNs()); + EXPECT_EQ(2222L, statsLogEventWrapper.getWallClockTimeNs()); + EXPECT_EQ(3, statsLogEventWrapper.getWorkChains().size()); + EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].uids.size()); + EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[0].uids[0]); + EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].uids[1]); + EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].tags.size()); + EXPECT_EQ("tag1", statsLogEventWrapper.getWorkChains()[0].tags[0]); + EXPECT_EQ("tag2", statsLogEventWrapper.getWorkChains()[0].tags[1]); + EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[1].uids.size()); + EXPECT_EQ(3, statsLogEventWrapper.getWorkChains()[1].uids[0]); + EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[1].tags.size()); + EXPECT_EQ("tag3", statsLogEventWrapper.getWorkChains()[1].tags[0]); + EXPECT_EQ(2, statsLogEventWrapper.getElements().size()); + EXPECT_EQ(6, statsLogEventWrapper.getElements()[0].int_value); + EXPECT_EQ(10L, statsLogEventWrapper.getElements()[1].long_value); + EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[2].uids.size()); + EXPECT_EQ(4, statsLogEventWrapper.getWorkChains()[2].uids[0]); + EXPECT_EQ(5, statsLogEventWrapper.getWorkChains()[2].uids[1]); + EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[2].tags.size()); + EXPECT_EQ("", statsLogEventWrapper.getWorkChains()[2].tags[0]); + EXPECT_EQ("", statsLogEventWrapper.getWorkChains()[2].tags[1]); + + LogEvent event(statsLogEventWrapper, -1); + EXPECT_EQ(1, event.GetTagId()); + EXPECT_EQ(1111L, event.GetElapsedTimestampNs()); + EXPECT_EQ(2222L, event.GetLogdTimestampNs()); + EXPECT_EQ(2, event.size()); + EXPECT_EQ(6, event.getValues()[0].mValue.int_value); + EXPECT_EQ(10, event.getValues()[1].mValue.long_value); + + LogEvent event1(statsLogEventWrapper, 0); + + EXPECT_EQ(1, event1.GetTagId()); + EXPECT_EQ(1111L, event1.GetElapsedTimestampNs()); + EXPECT_EQ(2222L, event1.GetLogdTimestampNs()); + EXPECT_EQ(6, event1.size()); + EXPECT_EQ(1, event1.getValues()[0].mValue.int_value); + EXPECT_EQ(0x2010101, event1.getValues()[0].mField.getField()); + EXPECT_EQ("tag1", event1.getValues()[1].mValue.str_value); + EXPECT_EQ(0x2010182, event1.getValues()[1].mField.getField()); + EXPECT_EQ(2, event1.getValues()[2].mValue.int_value); + EXPECT_EQ(0x2010201, event1.getValues()[2].mField.getField()); + EXPECT_EQ("tag2", event1.getValues()[3].mValue.str_value); + EXPECT_EQ(0x2018282, event1.getValues()[3].mField.getField()); + EXPECT_EQ(6, event1.getValues()[4].mValue.int_value); + EXPECT_EQ(0x20000, event1.getValues()[4].mField.getField()); + EXPECT_EQ(10, event1.getValues()[5].mValue.long_value); + EXPECT_EQ(0x30000, event1.getValues()[5].mField.getField()); + + LogEvent event2(statsLogEventWrapper, 1); + + EXPECT_EQ(1, event2.GetTagId()); + EXPECT_EQ(1111L, event2.GetElapsedTimestampNs()); + EXPECT_EQ(2222L, event2.GetLogdTimestampNs()); + EXPECT_EQ(4, event2.size()); + EXPECT_EQ(3, event2.getValues()[0].mValue.int_value); + EXPECT_EQ(0x2010101, event2.getValues()[0].mField.getField()); + EXPECT_EQ("tag3", event2.getValues()[1].mValue.str_value); + EXPECT_EQ(0x2018182, event2.getValues()[1].mField.getField()); + EXPECT_EQ(6, event2.getValues()[2].mValue.int_value); + EXPECT_EQ(0x20000, event2.getValues()[2].mField.getField()); + EXPECT_EQ(10, event2.getValues()[3].mValue.long_value); + EXPECT_EQ(0x30000, event2.getValues()[3].mField.getField()); + + LogEvent event3(statsLogEventWrapper, 2); + + EXPECT_EQ(1, event3.GetTagId()); + EXPECT_EQ(1111L, event3.GetElapsedTimestampNs()); + EXPECT_EQ(2222L, event3.GetLogdTimestampNs()); + EXPECT_EQ(6, event3.size()); + EXPECT_EQ(4, event3.getValues()[0].mValue.int_value); + EXPECT_EQ(0x2010101, event3.getValues()[0].mField.getField()); + EXPECT_EQ("", event3.getValues()[1].mValue.str_value); + EXPECT_EQ(0x2010182, event3.getValues()[1].mField.getField()); + EXPECT_EQ(5, event3.getValues()[2].mValue.int_value); + EXPECT_EQ(0x2010201, event3.getValues()[2].mField.getField()); + EXPECT_EQ("", event3.getValues()[3].mValue.str_value); + EXPECT_EQ(0x2018282, event3.getValues()[3].mField.getField()); + EXPECT_EQ(6, event3.getValues()[4].mValue.int_value); + EXPECT_EQ(0x20000, event3.getValues()[4].mField.getField()); + EXPECT_EQ(10, event3.getValues()[5].mValue.long_value); + EXPECT_EQ(0x30000, event3.getValues()[5].mField.getField()); +} TEST(LogEventTest, TestBinaryFieldAtom) { Atom launcherAtom; @@ -444,7 +605,44 @@ TEST(LogEventTest, TestBinaryFieldAtom) { EXPECT_EQ(orig_str, result_str); } +TEST(LogEventTest, TestBinaryFieldAtom_empty) { + Atom launcherAtom; + auto launcher_event = launcherAtom.mutable_launcher_event(); + launcher_event->set_action(stats::launcher::LauncherAction::LONGPRESS); + launcher_event->set_src_state(stats::launcher::LauncherState::OVERVIEW); + launcher_event->set_dst_state(stats::launcher::LauncherState::ALLAPPS); + + // empty string. + string extension_str; + + LogEvent event1(Atom::kLauncherEventFieldNumber, 1000); + + event1.write((int32_t)stats::launcher::LauncherAction::LONGPRESS); + event1.write((int32_t)stats::launcher::LauncherState::OVERVIEW); + event1.write((int64_t)stats::launcher::LauncherState::ALLAPPS); + event1.write(extension_str); + event1.init(); + + ProtoOutputStream proto; + event1.ToProto(proto); + + std::vector<uint8_t> outData; + outData.resize(proto.size()); + size_t pos = 0; + auto iter = proto.data(); + while (iter.readBuffer() != NULL) { + size_t toRead = iter.currentToRead(); + std::memcpy(&(outData[pos]), iter.readBuffer(), toRead); + pos += toRead; + iter.rp()->move(toRead); + } + + std::string result_str(outData.begin(), outData.end()); + std::string orig_str; + launcherAtom.SerializeToString(&orig_str); + EXPECT_EQ(orig_str, result_str); +} } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp index fc6e42010710..266ea351b5d4 100644 --- a/cmds/statsd/tests/external/puller_util_test.cpp +++ b/cmds/statsd/tests/external/puller_util_test.cpp @@ -80,7 +80,7 @@ TEST(puller_util, MergeNoDimension) { .WillRepeatedly(Return(hostUid)); EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))) .WillRepeatedly(ReturnArg<0>()); - mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId); + mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId); vector<vector<int>> actual; extractIntoVector(inputData, actual); @@ -120,7 +120,7 @@ TEST(puller_util, MergeWithDimension) { .WillRepeatedly(Return(hostUid)); EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))) .WillRepeatedly(ReturnArg<0>()); - mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId); + mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId); vector<vector<int>> actual; extractIntoVector(inputData, actual); @@ -154,7 +154,7 @@ TEST(puller_util, NoMergeHostUidOnly) { .WillRepeatedly(Return(hostUid)); EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))) .WillRepeatedly(ReturnArg<0>()); - mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId); + mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId); // 20->32->31 // 20->22->21 @@ -190,7 +190,7 @@ TEST(puller_util, IsolatedUidOnly) { .WillRepeatedly(Return(hostUid)); EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))) .WillRepeatedly(ReturnArg<0>()); - mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId); + mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId); // 20->32->31 // 20->22->21 @@ -231,7 +231,7 @@ TEST(puller_util, MultipleIsolatedUidToOneHostUid) { sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid)); - mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId); + mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId); vector<vector<int>> actual; extractIntoVector(inputData, actual); @@ -256,7 +256,7 @@ TEST(puller_util, NoNeedToMerge) { inputData.push_back(event); sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); - mergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId); + mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId); EXPECT_EQ(2, (int)inputData.size()); } diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 0bfcadd04553..25bd0330ad5c 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -1461,7 +1461,6 @@ Landroid/view/IWindowManager;->setShelfHeight(ZI)V Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V Landroid/view/IWindowManager;->showStrictModeViolation(Z)V Landroid/view/IWindowManager;->thawRotation()V -Landroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I Landroid/view/IWindowSession$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowSession; Landroid/view/IWindowSession;->finishDrawing(Landroid/view/IWindow;)V Landroid/view/IWindowSession;->getInTouchMode()Z @@ -2615,7 +2614,6 @@ Lcom/android/internal/telephony/dataconnection/DataConnection;->initConnection(L Lcom/android/internal/telephony/dataconnection/DataConnection;->log(Ljava/lang/String;)V Lcom/android/internal/telephony/dataconnection/DataConnection;->mActivatingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActivatingState; Lcom/android/internal/telephony/dataconnection/DataConnection;->mActiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActiveState; -Lcom/android/internal/telephony/dataconnection/DataConnection;->mApnContexts:Ljava/util/HashMap; Lcom/android/internal/telephony/dataconnection/DataConnection;->mConnectionParams:Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams; Lcom/android/internal/telephony/dataconnection/DataConnection;->mDataRegState:I Lcom/android/internal/telephony/dataconnection/DataConnection;->mDcFailCause:Lcom/android/internal/telephony/dataconnection/DcFailCause; @@ -4368,7 +4366,6 @@ Lcom/google/android/util/AbstractMessageParser$Token$Type;->PHOTO:Lcom/google/an Lcom/google/android/util/AbstractMessageParser$Token$Type;->SMILEY:Lcom/google/android/util/AbstractMessageParser$Token$Type; Lcom/google/android/util/AbstractMessageParser$Token$Type;->values()[Lcom/google/android/util/AbstractMessageParser$Token$Type; Lcom/google/android/util/AbstractMessageParser$Token$Type;->YOUTUBE_VIDEO:Lcom/google/android/util/AbstractMessageParser$Token$Type; -Lcom/sun/nio/file/ExtendedWatchEventModifier;->FILE_TREE:Lcom/sun/nio/file/ExtendedWatchEventModifier; Lgov/nist/core/Debug;->printStackTrace(Ljava/lang/Exception;)V Lgov/nist/core/GenericObject;-><init>()V Lgov/nist/core/GenericObject;->dbgPrint()V @@ -4508,177 +4505,9 @@ Lgov/nist/javax/sip/address/SipUri;->setParameter(Ljava/lang/String;Ljava/lang/S Lgov/nist/javax/sip/address/SipUri;->setUserParam(Ljava/lang/String;)V Lgov/nist/javax/sip/parser/URLParser;-><init>(Ljava/lang/String;)V Lgov/nist/javax/sip/parser/URLParser;->sipURL(Z)Lgov/nist/javax/sip/address/SipUri; -Ljava/lang/DexCache;->dexFile:J -Ljava/lang/invoke/SerializedLambda;-><init>(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V -Ljava/lang/invoke/SerializedLambda;->getCapturedArg(I)Ljava/lang/Object; -Ljava/lang/invoke/SerializedLambda;->getCapturedArgCount()I -Ljava/lang/invoke/SerializedLambda;->getCapturingClass()Ljava/lang/String; -Ljava/lang/invoke/SerializedLambda;->getFunctionalInterfaceClass()Ljava/lang/String; -Ljava/lang/invoke/SerializedLambda;->getFunctionalInterfaceMethodName()Ljava/lang/String; -Ljava/lang/invoke/SerializedLambda;->getFunctionalInterfaceMethodSignature()Ljava/lang/String; -Ljava/lang/invoke/SerializedLambda;->getImplClass()Ljava/lang/String; -Ljava/lang/invoke/SerializedLambda;->getImplMethodKind()I -Ljava/lang/invoke/SerializedLambda;->getImplMethodName()Ljava/lang/String; -Ljava/lang/invoke/SerializedLambda;->getImplMethodSignature()Ljava/lang/String; -Ljava/lang/invoke/SerializedLambda;->getInstantiatedMethodType()Ljava/lang/String; -Ljava/lang/UNIXProcess;->pid:I -Ljava/net/AddressCache$AddressCacheEntry;-><init>(Ljava/lang/Object;)V -Ljava/net/AddressCache$AddressCacheEntry;->expiryNanos:J -Ljava/net/AddressCache$AddressCacheEntry;->value:Ljava/lang/Object; -Ljava/net/AddressCache$AddressCacheKey;->mHostname:Ljava/lang/String; -Ljava/net/AddressCache;->cache:Llibcore/util/BasicLruCache; -Ljava/net/Inet6AddressImpl;->addressCache:Ljava/net/AddressCache; -Ljava/net/PlainSocketImpl;-><init>()V -Ljava/nio/DirectByteBuffer;->cleaner()Lsun/misc/Cleaner; -Ljava/nio/file/FileTreeWalker;->followLinks:Z -Ljava/nio/file/FileTreeWalker;->linkOptions:[Ljava/nio/file/LinkOption; -Ljava/nio/file/FileTreeWalker;->maxDepth:I -Ljava/util/zip/ZipFile$ZipEntryIterator;->nextElement()Ljava/util/zip/ZipEntry; Ljunit/framework/TestCase;->fName:Ljava/lang/String; Ljunit/framework/TestSuite;->isPublicTestMethod(Ljava/lang/reflect/Method;)Z Ljunit/framework/TestSuite;->isTestMethod(Ljava/lang/reflect/Method;)Z -Llibcore/icu/DateIntervalFormat;->formatDateRange(JJILjava/lang/String;)Ljava/lang/String; -Llibcore/icu/ICU;->CACHED_PATTERNS:Llibcore/util/BasicLruCache; -Llibcore/icu/ICU;->getBestDateTimePattern(Ljava/lang/String;Ljava/util/Locale;)Ljava/lang/String; -Llibcore/icu/ICU;->getBestDateTimePatternNative(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; -Llibcore/icu/ICU;->getDateFormatOrder(Ljava/lang/String;)[C -Llibcore/icu/LocaleData;->firstDayOfWeek:Ljava/lang/Integer; -Llibcore/icu/LocaleData;->get(Ljava/util/Locale;)Llibcore/icu/LocaleData; -Llibcore/icu/LocaleData;->longStandAloneWeekdayNames:[Ljava/lang/String; -Llibcore/icu/LocaleData;->mapInvalidAndNullLocales(Ljava/util/Locale;)Ljava/util/Locale; -Llibcore/icu/LocaleData;->minimalDaysInFirstWeek:Ljava/lang/Integer; -Llibcore/icu/LocaleData;->shortMonthNames:[Ljava/lang/String; -Llibcore/icu/LocaleData;->shortStandAloneMonthNames:[Ljava/lang/String; -Llibcore/icu/LocaleData;->shortStandAloneWeekdayNames:[Ljava/lang/String; -Llibcore/icu/LocaleData;->timeFormat_Hm:Ljava/lang/String; -Llibcore/icu/LocaleData;->timeFormat_hm:Ljava/lang/String; -Llibcore/icu/LocaleData;->today:Ljava/lang/String; -Llibcore/icu/LocaleData;->tomorrow:Ljava/lang/String; -Llibcore/icu/LocaleData;->zeroDigit:C -Llibcore/icu/TimeZoneNames;->forLocale(Ljava/util/Locale;)[Ljava/lang/String; -Llibcore/io/AsynchronousCloseMonitor;->signalBlockedThreads(Ljava/io/FileDescriptor;)V -Llibcore/io/BlockGuardOs;-><init>(Llibcore/io/Os;)V -Llibcore/io/BlockGuardOs;->chmod(Ljava/lang/String;I)V -Llibcore/io/BlockGuardOs;->chown(Ljava/lang/String;II)V -Llibcore/io/BlockGuardOs;->close(Ljava/io/FileDescriptor;)V -Llibcore/io/BlockGuardOs;->fchmod(Ljava/io/FileDescriptor;I)V -Llibcore/io/BlockGuardOs;->fchown(Ljava/io/FileDescriptor;II)V -Llibcore/io/BlockGuardOs;->fdatasync(Ljava/io/FileDescriptor;)V -Llibcore/io/BlockGuardOs;->fstat(Ljava/io/FileDescriptor;)Landroid/system/StructStat; -Llibcore/io/BlockGuardOs;->fstatvfs(Ljava/io/FileDescriptor;)Landroid/system/StructStatVfs; -Llibcore/io/BlockGuardOs;->lchown(Ljava/lang/String;II)V -Llibcore/io/BlockGuardOs;->link(Ljava/lang/String;Ljava/lang/String;)V -Llibcore/io/BlockGuardOs;->lseek(Ljava/io/FileDescriptor;JI)J -Llibcore/io/BlockGuardOs;->lstat(Ljava/lang/String;)Landroid/system/StructStat; -Llibcore/io/BlockGuardOs;->mkdir(Ljava/lang/String;I)V -Llibcore/io/BlockGuardOs;->mkfifo(Ljava/lang/String;I)V -Llibcore/io/BlockGuardOs;->open(Ljava/lang/String;II)Ljava/io/FileDescriptor; -Llibcore/io/BlockGuardOs;->posix_fallocate(Ljava/io/FileDescriptor;JJ)V -Llibcore/io/BlockGuardOs;->pread(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;J)I -Llibcore/io/BlockGuardOs;->pread(Ljava/io/FileDescriptor;[BIIJ)I -Llibcore/io/BlockGuardOs;->pwrite(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;J)I -Llibcore/io/BlockGuardOs;->pwrite(Ljava/io/FileDescriptor;[BIIJ)I -Llibcore/io/BlockGuardOs;->read(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;)I -Llibcore/io/BlockGuardOs;->read(Ljava/io/FileDescriptor;[BII)I -Llibcore/io/BlockGuardOs;->readlink(Ljava/lang/String;)Ljava/lang/String; -Llibcore/io/BlockGuardOs;->readv(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I -Llibcore/io/BlockGuardOs;->realpath(Ljava/lang/String;)Ljava/lang/String; -Llibcore/io/BlockGuardOs;->remove(Ljava/lang/String;)V -Llibcore/io/BlockGuardOs;->rename(Ljava/lang/String;Ljava/lang/String;)V -Llibcore/io/BlockGuardOs;->stat(Ljava/lang/String;)Landroid/system/StructStat; -Llibcore/io/BlockGuardOs;->statvfs(Ljava/lang/String;)Landroid/system/StructStatVfs; -Llibcore/io/BlockGuardOs;->symlink(Ljava/lang/String;Ljava/lang/String;)V -Llibcore/io/BlockGuardOs;->write(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;)I -Llibcore/io/BlockGuardOs;->write(Ljava/io/FileDescriptor;[BII)I -Llibcore/io/BlockGuardOs;->writev(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I -Llibcore/io/BufferIterator;->readByte()B -Llibcore/io/BufferIterator;->readByteArray([BII)V -Llibcore/io/BufferIterator;->readInt()I -Llibcore/io/BufferIterator;->readIntArray([III)V -Llibcore/io/BufferIterator;->seek(I)V -Llibcore/io/BufferIterator;->skip(I)V -Llibcore/io/DropBox;->addText(Ljava/lang/String;Ljava/lang/String;)V -Llibcore/io/ForwardingOs;-><init>(Llibcore/io/Os;)V -Llibcore/io/ForwardingOs;->access(Ljava/lang/String;I)Z -Llibcore/io/ForwardingOs;->chmod(Ljava/lang/String;I)V -Llibcore/io/ForwardingOs;->chown(Ljava/lang/String;II)V -Llibcore/io/ForwardingOs;->getenv(Ljava/lang/String;)Ljava/lang/String; -Llibcore/io/ForwardingOs;->lchown(Ljava/lang/String;II)V -Llibcore/io/ForwardingOs;->link(Ljava/lang/String;Ljava/lang/String;)V -Llibcore/io/ForwardingOs;->lstat(Ljava/lang/String;)Landroid/system/StructStat; -Llibcore/io/ForwardingOs;->mkdir(Ljava/lang/String;I)V -Llibcore/io/ForwardingOs;->mkfifo(Ljava/lang/String;I)V -Llibcore/io/ForwardingOs;->open(Ljava/lang/String;II)Ljava/io/FileDescriptor; -Llibcore/io/ForwardingOs;->os:Llibcore/io/Os; -Llibcore/io/ForwardingOs;->readlink(Ljava/lang/String;)Ljava/lang/String; -Llibcore/io/ForwardingOs;->remove(Ljava/lang/String;)V -Llibcore/io/ForwardingOs;->removexattr(Ljava/lang/String;Ljava/lang/String;)V -Llibcore/io/ForwardingOs;->rename(Ljava/lang/String;Ljava/lang/String;)V -Llibcore/io/ForwardingOs;->setenv(Ljava/lang/String;Ljava/lang/String;Z)V -Llibcore/io/ForwardingOs;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V -Llibcore/io/ForwardingOs;->setxattr(Ljava/lang/String;Ljava/lang/String;[BI)V -Llibcore/io/ForwardingOs;->stat(Ljava/lang/String;)Landroid/system/StructStat; -Llibcore/io/ForwardingOs;->statvfs(Ljava/lang/String;)Landroid/system/StructStatVfs; -Llibcore/io/ForwardingOs;->symlink(Ljava/lang/String;Ljava/lang/String;)V -Llibcore/io/ForwardingOs;->sysconf(I)J -Llibcore/io/ForwardingOs;->unlink(Ljava/lang/String;)V -Llibcore/io/IoBridge;->isConnected(Ljava/io/FileDescriptor;Ljava/net/InetAddress;III)Z -Llibcore/io/IoUtils;->closeQuietly(Ljava/io/FileDescriptor;)V -Llibcore/io/IoUtils;->closeQuietly(Ljava/lang/AutoCloseable;)V -Llibcore/io/IoUtils;->closeQuietly(Ljava/net/Socket;)V -Llibcore/io/IoUtils;->readFileAsByteArray(Ljava/lang/String;)[B -Llibcore/io/IoUtils;->readFileAsString(Ljava/lang/String;)Ljava/lang/String; -Llibcore/io/IoUtils;->setBlocking(Ljava/io/FileDescriptor;Z)V -Llibcore/io/MemoryMappedFile;->bigEndianIterator()Llibcore/io/BufferIterator; -Llibcore/io/MemoryMappedFile;->mmapRO(Ljava/lang/String;)Llibcore/io/MemoryMappedFile; -Llibcore/io/Os;->chmod(Ljava/lang/String;I)V -Llibcore/io/Os;->close(Ljava/io/FileDescriptor;)V -Llibcore/io/Os;->connect(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V -Llibcore/io/Os;->gai_strerror(I)Ljava/lang/String; -Llibcore/io/Os;->remove(Ljava/lang/String;)V -Llibcore/io/Os;->setenv(Ljava/lang/String;Ljava/lang/String;Z)V -Llibcore/io/Os;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V -Llibcore/io/Os;->stat(Ljava/lang/String;)Landroid/system/StructStat; -Llibcore/io/Os;->strerror(I)Ljava/lang/String; -Llibcore/io/Os;->sysconf(I)J -Llibcore/io/Streams;->readAsciiLine(Ljava/io/InputStream;)Ljava/lang/String; -Llibcore/io/Streams;->readFully(Ljava/io/InputStream;)[B -Llibcore/io/Streams;->readFully(Ljava/io/InputStream;[B)V -Llibcore/io/Streams;->readSingleByte(Ljava/io/InputStream;)I -Llibcore/io/Streams;->skipAll(Ljava/io/InputStream;)V -Llibcore/io/Streams;->writeSingleByte(Ljava/io/OutputStream;I)V -Llibcore/net/event/NetworkEventDispatcher;->addListener(Llibcore/net/event/NetworkEventListener;)V -Llibcore/net/event/NetworkEventDispatcher;->getInstance()Llibcore/net/event/NetworkEventDispatcher; -Llibcore/net/event/NetworkEventListener;-><init>()V -Llibcore/net/http/HttpDate;->format(Ljava/util/Date;)Ljava/lang/String; -Llibcore/net/http/HttpDate;->parse(Ljava/lang/String;)Ljava/util/Date; -Llibcore/net/MimeUtils;->guessExtensionFromMimeType(Ljava/lang/String;)Ljava/lang/String; -Llibcore/net/MimeUtils;->guessMimeTypeFromExtension(Ljava/lang/String;)Ljava/lang/String; -Llibcore/net/NetworkSecurityPolicy;->isCleartextTrafficPermitted()Z -Llibcore/util/BasicLruCache;-><init>(I)V -Llibcore/util/BasicLruCache;->evictAll()V -Llibcore/util/BasicLruCache;->get(Ljava/lang/Object;)Ljava/lang/Object; -Llibcore/util/BasicLruCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; -Llibcore/util/EmptyArray;->BYTE:[B -Llibcore/util/EmptyArray;->INT:[I -Llibcore/util/EmptyArray;->OBJECT:[Ljava/lang/Object; -Lorg/apache/harmony/dalvik/ddmc/Chunk;-><init>(ILjava/nio/ByteBuffer;)V -Lorg/apache/harmony/dalvik/ddmc/ChunkHandler;->CHUNK_ORDER:Ljava/nio/ByteOrder; -Lorg/apache/harmony/dalvik/ddmc/DdmServer;->broadcast(I)V -Lorg/apache/harmony/dalvik/ddmc/DdmServer;->sendChunk(Lorg/apache/harmony/dalvik/ddmc/Chunk;)V -Lorg/apache/harmony/dalvik/ddmc/DdmVmInternal;->getThreadStats()[B -Lorg/apache/harmony/xml/dom/ElementImpl;->localName:Ljava/lang/String; -Lorg/apache/harmony/xml/ExpatAttributes;-><init>()V -Lorg/apache/harmony/xml/ExpatParser$EntityParser;->depth:I -Lorg/apache/harmony/xml/ExpatParser;-><init>(Ljava/lang/String;Lorg/apache/harmony/xml/ExpatReader;ZLjava/lang/String;Ljava/lang/String;)V -Lorg/apache/harmony/xml/ExpatParser;->append([BII)V -Lorg/apache/harmony/xml/ExpatParser;->append([CII)V -Lorg/apache/harmony/xml/ExpatParser;->attributes:Lorg/apache/harmony/xml/ExpatAttributes; -Lorg/apache/harmony/xml/ExpatParser;->cloneAttributes()Lorg/xml/sax/Attributes; -Lorg/apache/harmony/xml/ExpatParser;->finish()V -Lorg/apache/harmony/xml/ExpatParser;->xmlReader:Lorg/apache/harmony/xml/ExpatReader; -Lorg/apache/harmony/xml/ExpatReader;-><init>()V -Lorg/apache/harmony/xml/ExpatReader;->contentHandler:Lorg/xml/sax/ContentHandler; Lorg/apache/xalan/extensions/ExpressionContext;->getContextNode()Lorg/w3c/dom/Node; Lorg/apache/xalan/extensions/ExpressionContext;->getErrorListener()Ljavax/xml/transform/ErrorListener; Lorg/apache/xalan/extensions/ExpressionContext;->getVariableOrParam(Lorg/apache/xml/utils/QName;)Lorg/apache/xpath/objects/XObject; @@ -5357,431 +5186,3 @@ Lorg/ccil/cowan/tagsoup/XMLWriter;->htmlMode:Z Lorg/ccil/cowan/tagsoup/XMLWriter;->setOutput(Ljava/io/Writer;)V Lorg/ccil/cowan/tagsoup/XMLWriter;->setOutputProperty(Ljava/lang/String;Ljava/lang/String;)V Lorg/ccil/cowan/tagsoup/XMLWriter;->setPrefix(Ljava/lang/String;Ljava/lang/String;)V -Lorg/xml/sax/helpers/NamespaceSupport$Context;-><init>(Lorg/xml/sax/helpers/NamespaceSupport;)V -Lorg/xml/sax/helpers/ParserAdapter$AttributeListAdapter;-><init>(Lorg/xml/sax/helpers/ParserAdapter;)V -Lsun/misc/ASCIICaseInsensitiveComparator;->CASE_INSENSITIVE_ORDER:Ljava/util/Comparator; -Lsun/misc/ASCIICaseInsensitiveComparator;->lowerCaseHashCode(Ljava/lang/String;)I -Lsun/misc/BASE64Decoder;-><init>()V -Lsun/misc/BASE64Decoder;->pem_convert_array:[B -Lsun/misc/BASE64Encoder;-><init>()V -Lsun/misc/BASE64Encoder;->pem_array:[C -Lsun/misc/CEFormatException;-><init>(Ljava/lang/String;)V -Lsun/misc/CEStreamExhausted;-><init>()V -Lsun/misc/CharacterDecoder;-><init>()V -Lsun/misc/CharacterEncoder;-><init>()V -Lsun/misc/CharacterEncoder;->encodeBuffer([B)Ljava/lang/String; -Lsun/misc/CharacterEncoder;->encodeBufferPrefix(Ljava/io/OutputStream;)V -Lsun/misc/CharacterEncoder;->pStream:Ljava/io/PrintStream; -Lsun/misc/Cleaner;->create(Ljava/lang/Object;Ljava/lang/Runnable;)Lsun/misc/Cleaner; -Lsun/misc/FloatingDecimal;->$assertionsDisabled:Z -Lsun/misc/FloatingDecimal;->getHexDigit(Ljava/lang/String;I)I -Lsun/misc/FloatingDecimal;->stripLeadingZeros(Ljava/lang/String;)Ljava/lang/String; -Lsun/misc/FormattedFloatingDecimal$Form;->COMPATIBLE:Lsun/misc/FormattedFloatingDecimal$Form; -Lsun/misc/FormattedFloatingDecimal$Form;->DECIMAL_FLOAT:Lsun/misc/FormattedFloatingDecimal$Form; -Lsun/misc/FormattedFloatingDecimal$Form;->SCIENTIFIC:Lsun/misc/FormattedFloatingDecimal$Form; -Lsun/misc/FormattedFloatingDecimal;->$assertionsDisabled:Z -Lsun/misc/FpUtils;->$assertionsDisabled:Z -Lsun/misc/FpUtils;->rawCopySign(DD)D -Lsun/misc/HexDumpEncoder;-><init>()V -Lsun/misc/HexDumpEncoder;->currentByte:I -Lsun/misc/HexDumpEncoder;->offset:I -Lsun/misc/HexDumpEncoder;->thisLine:[B -Lsun/misc/HexDumpEncoder;->thisLineLength:I -Lsun/misc/IOUtils;->readFully(Ljava/io/InputStream;IZ)[B -Lsun/misc/JarIndex;-><init>([Ljava/lang/String;)V -Lsun/misc/JarIndex;->write(Ljava/io/OutputStream;)V -Lsun/misc/MessageUtils;-><init>()V -Lsun/misc/MetaIndex;->forJar(Ljava/io/File;)Lsun/misc/MetaIndex; -Lsun/misc/MetaIndex;->registerDirectory(Ljava/io/File;)V -Lsun/misc/VM;->maxDirectMemory()J -Lsun/net/ftp/FtpClient;-><init>()V -Lsun/net/util/IPAddressUtil;->isIPv4LiteralAddress(Ljava/lang/String;)Z -Lsun/net/util/IPAddressUtil;->isIPv6LiteralAddress(Ljava/lang/String;)Z -Lsun/net/www/MessageHeader;-><init>()V -Lsun/net/www/MessageHeader;-><init>(Ljava/io/InputStream;)V -Lsun/net/www/MessageHeader;->add(Ljava/lang/String;Ljava/lang/String;)V -Lsun/net/www/MessageHeader;->findValue(Ljava/lang/String;)Ljava/lang/String; -Lsun/net/www/MessageHeader;->prepend(Ljava/lang/String;Ljava/lang/String;)V -Lsun/net/www/MessageHeader;->print(Ljava/io/PrintStream;)V -Lsun/net/www/MessageHeader;->set(Ljava/lang/String;Ljava/lang/String;)V -Lsun/net/www/ParseUtil;->decode(Ljava/lang/String;)Ljava/lang/String; -Lsun/net/www/ParseUtil;->encodePath(Ljava/lang/String;Z)Ljava/lang/String; -Lsun/net/www/ParseUtil;->fileToEncodedURL(Ljava/io/File;)Ljava/net/URL; -Lsun/net/www/URLConnection;-><init>(Ljava/net/URL;)V -Lsun/net/www/URLConnection;->setProperties(Lsun/net/www/MessageHeader;)V -Lsun/nio/ch/DirectBuffer;->address()J -Lsun/nio/ch/FileChannelImpl;->unmap0(JJ)I -Lsun/nio/ch/SelectorImpl;->publicSelectedKeys:Ljava/util/Set; -Lsun/nio/ch/SelectorImpl;->selectedKeys:Ljava/util/Set; -Lsun/nio/cs/HistoricallyNamedCharset;->historicalName()Ljava/lang/String; -Lsun/nio/cs/ThreadLocalCoders;->decoderFor(Ljava/lang/Object;)Ljava/nio/charset/CharsetDecoder; -Lsun/nio/fs/BasicFileAttributesHolder;->get()Ljava/nio/file/attribute/BasicFileAttributes; -Lsun/reflect/misc/ReflectUtil;->checkPackageAccess(Ljava/lang/Class;)V -Lsun/reflect/misc/ReflectUtil;->checkPackageAccess(Ljava/lang/String;)V -Lsun/reflect/misc/ReflectUtil;->isPackageAccessible(Ljava/lang/Class;)Z -Lsun/reflect/misc/ReflectUtil;->isSubclassOf(Ljava/lang/Class;Ljava/lang/Class;)Z -Lsun/reflect/Reflection;->ensureMemberAccess(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;I)V -Lsun/reflect/Reflection;->isSubclassOf(Ljava/lang/Class;Ljava/lang/Class;)Z -Lsun/security/action/GetBooleanAction;-><init>(Ljava/lang/String;)V -Lsun/security/action/GetIntegerAction;-><init>(Ljava/lang/String;I)V -Lsun/security/action/GetPropertyAction;-><init>(Ljava/lang/String;)V -Lsun/security/action/GetPropertyAction;-><init>(Ljava/lang/String;Ljava/lang/String;)V -Lsun/security/jca/GetInstance$Instance;->impl:Ljava/lang/Object; -Lsun/security/jca/GetInstance$Instance;->provider:Ljava/security/Provider; -Lsun/security/jca/GetInstance;->getInstance(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;)Lsun/security/jca/GetInstance$Instance; -Lsun/security/jca/GetInstance;->getInstance(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;)Lsun/security/jca/GetInstance$Instance; -Lsun/security/jca/GetInstance;->getInstance(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;Ljava/security/Provider;)Lsun/security/jca/GetInstance$Instance; -Lsun/security/jca/JCAUtil;->getSecureRandom()Ljava/security/SecureRandom; -Lsun/security/jca/ProviderConfig;->argument:Ljava/lang/String; -Lsun/security/jca/ProviderConfig;->CL_STRING:[Ljava/lang/Class; -Lsun/security/jca/ProviderConfig;->disableLoad()V -Lsun/security/jca/ProviderConfig;->hasArgument()Z -Lsun/security/jca/ProviderList;->getService(Ljava/lang/String;Ljava/lang/String;)Ljava/security/Provider$Service; -Lsun/security/jca/Providers;->getProviderList()Lsun/security/jca/ProviderList; -Lsun/security/jca/Providers;->startJarVerification()Ljava/lang/Object; -Lsun/security/jca/Providers;->stopJarVerification(Ljava/lang/Object;)V -Lsun/security/pkcs/ContentInfo;-><init>(Lsun/security/util/ObjectIdentifier;Lsun/security/util/DerValue;)V -Lsun/security/pkcs/ContentInfo;-><init>([B)V -Lsun/security/pkcs/ContentInfo;->DATA_OID:Lsun/security/util/ObjectIdentifier; -Lsun/security/pkcs/ContentInfo;->encode(Lsun/security/util/DerOutputStream;)V -Lsun/security/pkcs/ContentInfo;->getData()[B -Lsun/security/pkcs/ParsingException;-><init>(Ljava/lang/String;)V -Lsun/security/pkcs/PKCS7;-><init>([B)V -Lsun/security/pkcs/PKCS7;-><init>([Lsun/security/x509/AlgorithmId;Lsun/security/pkcs/ContentInfo;[Ljava/security/cert/X509Certificate;[Ljava/security/cert/X509CRL;[Lsun/security/pkcs/SignerInfo;)V -Lsun/security/pkcs/PKCS7;-><init>([Lsun/security/x509/AlgorithmId;Lsun/security/pkcs/ContentInfo;[Ljava/security/cert/X509Certificate;[Lsun/security/pkcs/SignerInfo;)V -Lsun/security/pkcs/PKCS7;->encodeSignedData(Ljava/io/OutputStream;)V -Lsun/security/pkcs/PKCS7;->getCertificates()[Ljava/security/cert/X509Certificate; -Lsun/security/pkcs/PKCS7;->getContentInfo()Lsun/security/pkcs/ContentInfo; -Lsun/security/pkcs/PKCS7;->getSignerInfos()[Lsun/security/pkcs/SignerInfo; -Lsun/security/pkcs/PKCS7;->verify(Lsun/security/pkcs/SignerInfo;[B)Lsun/security/pkcs/SignerInfo; -Lsun/security/pkcs/PKCS7;->verify([B)[Lsun/security/pkcs/SignerInfo; -Lsun/security/pkcs/PKCS8Key;-><init>()V -Lsun/security/pkcs/PKCS8Key;->algid:Lsun/security/x509/AlgorithmId; -Lsun/security/pkcs/PKCS8Key;->encodedKey:[B -Lsun/security/pkcs/PKCS8Key;->key:[B -Lsun/security/pkcs/PKCS9Attribute;-><init>(Ljava/lang/String;Ljava/lang/Object;)V -Lsun/security/pkcs/PKCS9Attribute;-><init>(Lsun/security/util/DerValue;)V -Lsun/security/pkcs/PKCS9Attribute;-><init>(Lsun/security/util/ObjectIdentifier;Ljava/lang/Object;)V -Lsun/security/pkcs/PKCS9Attribute;->CONTENT_TYPE_OID:Lsun/security/util/ObjectIdentifier; -Lsun/security/pkcs/PKCS9Attribute;->derEncode(Ljava/io/OutputStream;)V -Lsun/security/pkcs/PKCS9Attribute;->EMAIL_ADDRESS_OID:Lsun/security/util/ObjectIdentifier; -Lsun/security/pkcs/PKCS9Attribute;->getOID()Lsun/security/util/ObjectIdentifier; -Lsun/security/pkcs/PKCS9Attribute;->getValue()Ljava/lang/Object; -Lsun/security/pkcs/PKCS9Attribute;->MESSAGE_DIGEST_OID:Lsun/security/util/ObjectIdentifier; -Lsun/security/pkcs/PKCS9Attribute;->SIGNING_TIME_OID:Lsun/security/util/ObjectIdentifier; -Lsun/security/pkcs/PKCS9Attributes;-><init>(Lsun/security/util/DerInputStream;)V -Lsun/security/pkcs/PKCS9Attributes;-><init>(Lsun/security/util/DerInputStream;Z)V -Lsun/security/pkcs/PKCS9Attributes;-><init>([Lsun/security/pkcs/PKCS9Attribute;)V -Lsun/security/pkcs/PKCS9Attributes;->encode(BLjava/io/OutputStream;)V -Lsun/security/pkcs/PKCS9Attributes;->getAttribute(Ljava/lang/String;)Lsun/security/pkcs/PKCS9Attribute; -Lsun/security/pkcs/PKCS9Attributes;->getAttributeValue(Lsun/security/util/ObjectIdentifier;)Ljava/lang/Object; -Lsun/security/pkcs/PKCS9Attributes;->getDerEncoding()[B -Lsun/security/pkcs/SignerInfo;-><init>(Lsun/security/x509/X500Name;Ljava/math/BigInteger;Lsun/security/x509/AlgorithmId;Lsun/security/pkcs/PKCS9Attributes;Lsun/security/x509/AlgorithmId;[BLsun/security/pkcs/PKCS9Attributes;)V -Lsun/security/pkcs/SignerInfo;-><init>(Lsun/security/x509/X500Name;Ljava/math/BigInteger;Lsun/security/x509/AlgorithmId;Lsun/security/x509/AlgorithmId;[B)V -Lsun/security/pkcs/SignerInfo;->getCertificate(Lsun/security/pkcs/PKCS7;)Ljava/security/cert/X509Certificate; -Lsun/security/pkcs/SignerInfo;->getCertificateChain(Lsun/security/pkcs/PKCS7;)Ljava/util/ArrayList; -Lsun/security/pkcs/SignerInfo;->getDigestAlgorithmId()Lsun/security/x509/AlgorithmId; -Lsun/security/pkcs/SignerInfo;->getDigestEncryptionAlgorithmId()Lsun/security/x509/AlgorithmId; -Lsun/security/pkcs/SignerInfo;->getEncryptedDigest()[B -Lsun/security/provider/certpath/X509CertificatePair;->clearCache()V -Lsun/security/provider/certpath/X509CertPath;-><init>(Ljava/io/InputStream;)V -Lsun/security/provider/certpath/X509CertPath;-><init>(Ljava/io/InputStream;Ljava/lang/String;)V -Lsun/security/provider/certpath/X509CertPath;-><init>(Ljava/util/List;)V -Lsun/security/provider/certpath/X509CertPath;->certs:Ljava/util/List; -Lsun/security/provider/certpath/X509CertPath;->getEncodingsStatic()Ljava/util/Iterator; -Lsun/security/provider/X509Factory;->addToCache(Lsun/security/util/Cache;[BLjava/lang/Object;)V -Lsun/security/provider/X509Factory;->certCache:Lsun/security/util/Cache; -Lsun/security/provider/X509Factory;->crlCache:Lsun/security/util/Cache; -Lsun/security/provider/X509Factory;->getFromCache(Lsun/security/util/Cache;[B)Ljava/lang/Object; -Lsun/security/provider/X509Factory;->intern(Ljava/security/cert/X509Certificate;)Lsun/security/x509/X509CertImpl; -Lsun/security/provider/X509Factory;->intern(Ljava/security/cert/X509CRL;)Lsun/security/x509/X509CRLImpl; -Lsun/security/timestamp/TimestampToken;-><init>([B)V -Lsun/security/timestamp/TimestampToken;->getDate()Ljava/util/Date; -Lsun/security/timestamp/TimestampToken;->getHashAlgorithm()Lsun/security/x509/AlgorithmId; -Lsun/security/timestamp/TimestampToken;->getHashedMessage()[B -Lsun/security/timestamp/TimestampToken;->getNonce()Ljava/math/BigInteger; -Lsun/security/util/BitArray;-><init>(I[B)V -Lsun/security/util/BitArray;->toByteArray()[B -Lsun/security/util/Cache;-><init>()V -Lsun/security/util/Cache;->clear()V -Lsun/security/util/Cache;->get(Ljava/lang/Object;)Ljava/lang/Object; -Lsun/security/util/Cache;->newHardMemoryCache(I)Lsun/security/util/Cache; -Lsun/security/util/Cache;->put(Ljava/lang/Object;Ljava/lang/Object;)V -Lsun/security/util/Debug;->getInstance(Ljava/lang/String;)Lsun/security/util/Debug; -Lsun/security/util/Debug;->println()V -Lsun/security/util/Debug;->println(Ljava/lang/String;)V -Lsun/security/util/Debug;->toHexString(Ljava/math/BigInteger;)Ljava/lang/String; -Lsun/security/util/DerIndefLenConverter;-><init>()V -Lsun/security/util/DerIndefLenConverter;->convert([B)[B -Lsun/security/util/DerIndefLenConverter;->data:[B -Lsun/security/util/DerIndefLenConverter;->dataPos:I -Lsun/security/util/DerIndefLenConverter;->dataSize:I -Lsun/security/util/DerIndefLenConverter;->isIndefinite(I)Z -Lsun/security/util/DerIndefLenConverter;->newData:[B -Lsun/security/util/DerIndefLenConverter;->numOfTotalLenBytes:I -Lsun/security/util/DerIndefLenConverter;->parseLength()I -Lsun/security/util/DerIndefLenConverter;->parseTag()V -Lsun/security/util/DerIndefLenConverter;->parseValue(I)V -Lsun/security/util/DerIndefLenConverter;->writeLengthAndValue()V -Lsun/security/util/DerIndefLenConverter;->writeTag()V -Lsun/security/util/DerInputStream;-><init>([B)V -Lsun/security/util/DerInputStream;->available()I -Lsun/security/util/DerInputStream;->getBigInteger()Ljava/math/BigInteger; -Lsun/security/util/DerInputStream;->getBitString()[B -Lsun/security/util/DerInputStream;->getDerValue()Lsun/security/util/DerValue; -Lsun/security/util/DerInputStream;->getInteger()I -Lsun/security/util/DerInputStream;->getOctetString()[B -Lsun/security/util/DerInputStream;->getOID()Lsun/security/util/ObjectIdentifier; -Lsun/security/util/DerInputStream;->getSequence(I)[Lsun/security/util/DerValue; -Lsun/security/util/DerInputStream;->getSet(I)[Lsun/security/util/DerValue; -Lsun/security/util/DerInputStream;->getSet(IZ)[Lsun/security/util/DerValue; -Lsun/security/util/DerInputStream;->getUTCTime()Ljava/util/Date; -Lsun/security/util/DerInputStream;->getUTF8String()Ljava/lang/String; -Lsun/security/util/DerInputStream;->mark(I)V -Lsun/security/util/DerInputStream;->peekByte()I -Lsun/security/util/DerInputStream;->reset()V -Lsun/security/util/DerInputStream;->subStream(IZ)Lsun/security/util/DerInputStream; -Lsun/security/util/DerInputStream;->tag:B -Lsun/security/util/DerOutputStream;-><init>()V -Lsun/security/util/DerOutputStream;-><init>(I)V -Lsun/security/util/DerOutputStream;->putBitString([B)V -Lsun/security/util/DerOutputStream;->putBoolean(Z)V -Lsun/security/util/DerOutputStream;->putDerValue(Lsun/security/util/DerValue;)V -Lsun/security/util/DerOutputStream;->putIA5String(Ljava/lang/String;)V -Lsun/security/util/DerOutputStream;->putInteger(I)V -Lsun/security/util/DerOutputStream;->putInteger(Ljava/math/BigInteger;)V -Lsun/security/util/DerOutputStream;->putNull()V -Lsun/security/util/DerOutputStream;->putOctetString([B)V -Lsun/security/util/DerOutputStream;->putOID(Lsun/security/util/ObjectIdentifier;)V -Lsun/security/util/DerOutputStream;->putOrderedSetOf(B[Lsun/security/util/DerEncoder;)V -Lsun/security/util/DerOutputStream;->putPrintableString(Ljava/lang/String;)V -Lsun/security/util/DerOutputStream;->putSequence([Lsun/security/util/DerValue;)V -Lsun/security/util/DerOutputStream;->putUTCTime(Ljava/util/Date;)V -Lsun/security/util/DerOutputStream;->putUTF8String(Ljava/lang/String;)V -Lsun/security/util/DerOutputStream;->write(BLsun/security/util/DerOutputStream;)V -Lsun/security/util/DerOutputStream;->write(B[B)V -Lsun/security/util/DerValue;-><init>(B[B)V -Lsun/security/util/DerValue;-><init>(Ljava/io/InputStream;)V -Lsun/security/util/DerValue;-><init>(Ljava/lang/String;)V -Lsun/security/util/DerValue;-><init>([B)V -Lsun/security/util/DerValue;-><init>([BII)V -Lsun/security/util/DerValue;->buffer:Lsun/security/util/DerInputBuffer; -Lsun/security/util/DerValue;->createTag(BZB)B -Lsun/security/util/DerValue;->data:Lsun/security/util/DerInputStream; -Lsun/security/util/DerValue;->encode(Lsun/security/util/DerOutputStream;)V -Lsun/security/util/DerValue;->getAsString()Ljava/lang/String; -Lsun/security/util/DerValue;->getBigInteger()Ljava/math/BigInteger; -Lsun/security/util/DerValue;->getBitString()[B -Lsun/security/util/DerValue;->getData()Lsun/security/util/DerInputStream; -Lsun/security/util/DerValue;->getDataBytes()[B -Lsun/security/util/DerValue;->getOctetString()[B -Lsun/security/util/DerValue;->getOID()Lsun/security/util/ObjectIdentifier; -Lsun/security/util/DerValue;->getPositiveBigInteger()Ljava/math/BigInteger; -Lsun/security/util/DerValue;->getUnalignedBitString()Lsun/security/util/BitArray; -Lsun/security/util/DerValue;->isConstructed()Z -Lsun/security/util/DerValue;->isContextSpecific()Z -Lsun/security/util/DerValue;->isContextSpecific(B)Z -Lsun/security/util/DerValue;->isPrintableStringChar(C)Z -Lsun/security/util/DerValue;->resetTag(B)V -Lsun/security/util/DerValue;->tag:B -Lsun/security/util/DerValue;->toByteArray()[B -Lsun/security/util/DerValue;->toDerInputStream()Lsun/security/util/DerInputStream; -Lsun/security/util/ManifestDigester$Entry;->digest(Ljava/security/MessageDigest;)[B -Lsun/security/util/ManifestDigester$Entry;->digestWorkaround(Ljava/security/MessageDigest;)[B -Lsun/security/util/ManifestDigester;-><init>([B)V -Lsun/security/util/ManifestDigester;->get(Ljava/lang/String;Z)Lsun/security/util/ManifestDigester$Entry; -Lsun/security/util/ManifestDigester;->manifestDigest(Ljava/security/MessageDigest;)[B -Lsun/security/util/MemoryCache$HardCacheEntry;-><init>(Ljava/lang/Object;Ljava/lang/Object;J)V -Lsun/security/util/MemoryCache$SoftCacheEntry;-><init>(Ljava/lang/Object;Ljava/lang/Object;JLjava/lang/ref/ReferenceQueue;)V -Lsun/security/util/ObjectIdentifier;-><init>(Ljava/lang/String;)V -Lsun/security/util/ObjectIdentifier;-><init>([I)V -Lsun/security/util/ObjectIdentifier;->equals(Lsun/security/util/ObjectIdentifier;)Z -Lsun/security/util/ObjectIdentifier;->newInternal([I)Lsun/security/util/ObjectIdentifier; -Lsun/security/util/PropertyExpander;->expand(Ljava/lang/String;)Ljava/lang/String; -Lsun/security/util/ResourcesMgr;->getString(Ljava/lang/String;)Ljava/lang/String; -Lsun/security/util/SecurityConstants;->CREATE_CLASSLOADER_PERMISSION:Ljava/lang/RuntimePermission; -Lsun/security/util/SecurityConstants;->GET_CLASSLOADER_PERMISSION:Ljava/lang/RuntimePermission; -Lsun/security/util/SecurityConstants;->MODIFY_THREADGROUP_PERMISSION:Ljava/lang/RuntimePermission; -Lsun/security/util/SecurityConstants;->MODIFY_THREAD_PERMISSION:Ljava/lang/RuntimePermission; -Lsun/security/util/SignatureFileVerifier;->isBlockOrSF(Ljava/lang/String;)Z -Lsun/security/x509/AccessDescription;-><init>(Lsun/security/util/DerValue;)V -Lsun/security/x509/AccessDescription;->getAccessLocation()Lsun/security/x509/GeneralName; -Lsun/security/x509/AccessDescription;->getAccessMethod()Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/AlgorithmId;-><init>()V -Lsun/security/x509/AlgorithmId;-><init>(Lsun/security/util/ObjectIdentifier;)V -Lsun/security/x509/AlgorithmId;-><init>(Lsun/security/util/ObjectIdentifier;Ljava/security/AlgorithmParameters;)V -Lsun/security/x509/AlgorithmId;->derEncode(Ljava/io/OutputStream;)V -Lsun/security/x509/AlgorithmId;->DSA_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/AlgorithmId;->EC_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/AlgorithmId;->encode()[B -Lsun/security/x509/AlgorithmId;->encode(Lsun/security/util/DerOutputStream;)V -Lsun/security/x509/AlgorithmId;->equals(Lsun/security/x509/AlgorithmId;)Z -Lsun/security/x509/AlgorithmId;->getAlgorithmId(Ljava/lang/String;)Lsun/security/x509/AlgorithmId; -Lsun/security/x509/AlgorithmId;->getDigAlgFromSigAlg(Ljava/lang/String;)Ljava/lang/String; -Lsun/security/x509/AlgorithmId;->getEncAlgFromSigAlg(Ljava/lang/String;)Ljava/lang/String; -Lsun/security/x509/AlgorithmId;->getEncodedParams()[B -Lsun/security/x509/AlgorithmId;->getOID()Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/AlgorithmId;->getParameters()Ljava/security/AlgorithmParameters; -Lsun/security/x509/AlgorithmId;->MD2_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/AlgorithmId;->MD5_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/AlgorithmId;->params:Lsun/security/util/DerValue; -Lsun/security/x509/AlgorithmId;->parse(Lsun/security/util/DerValue;)Lsun/security/x509/AlgorithmId; -Lsun/security/x509/AlgorithmId;->RSAEncryption_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/AlgorithmId;->sha1WithRSAEncryption_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/AlgorithmId;->SHA256_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/AlgorithmId;->SHA384_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/AlgorithmId;->SHA512_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/AlgorithmId;->SHA_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/AttributeNameEnumeration;-><init>()V -Lsun/security/x509/AVA;-><init>(Lsun/security/util/ObjectIdentifier;Lsun/security/util/DerValue;)V -Lsun/security/x509/AVA;->getDerValue()Lsun/security/util/DerValue; -Lsun/security/x509/AVA;->getObjectIdentifier()Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/AVA;->getValueString()Ljava/lang/String; -Lsun/security/x509/AVA;->toRFC2253CanonicalString()Ljava/lang/String; -Lsun/security/x509/AVAComparator;->INSTANCE:Ljava/util/Comparator; -Lsun/security/x509/AVAKeyword;->getOID(Ljava/lang/String;ILjava/util/Map;)Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/AVAKeyword;->isCompliant(I)Z -Lsun/security/x509/AVAKeyword;->keyword:Ljava/lang/String; -Lsun/security/x509/AVAKeyword;->keywordMap:Ljava/util/Map; -Lsun/security/x509/AVAKeyword;->oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/AVAKeyword;->oidMap:Ljava/util/Map; -Lsun/security/x509/CertificateAlgorithmId;-><init>(Lsun/security/x509/AlgorithmId;)V -Lsun/security/x509/CertificateExtensions;-><init>()V -Lsun/security/x509/CertificateExtensions;-><init>(Lsun/security/util/DerInputStream;)V -Lsun/security/x509/CertificateExtensions;->encode(Ljava/io/OutputStream;Z)V -Lsun/security/x509/CertificateExtensions;->get(Ljava/lang/String;)Ljava/lang/Object; -Lsun/security/x509/CertificateExtensions;->set(Ljava/lang/String;Ljava/lang/Object;)V -Lsun/security/x509/CertificateIssuerName;-><init>(Lsun/security/x509/X500Name;)V -Lsun/security/x509/CertificateSerialNumber;-><init>(I)V -Lsun/security/x509/CertificateSerialNumber;-><init>(Ljava/math/BigInteger;)V -Lsun/security/x509/CertificateSubjectName;-><init>(Lsun/security/x509/X500Name;)V -Lsun/security/x509/CertificateSubjectName;->get(Ljava/lang/String;)Ljava/lang/Object; -Lsun/security/x509/CertificateValidity;-><init>(Ljava/util/Date;Ljava/util/Date;)V -Lsun/security/x509/CertificateVersion;-><init>(I)V -Lsun/security/x509/CertificateX509Key;-><init>(Ljava/security/PublicKey;)V -Lsun/security/x509/CRLDistributionPointsExtension;->encodeThis()V -Lsun/security/x509/CRLNumberExtension;-><init>(Ljava/lang/Boolean;Ljava/lang/Object;)V -Lsun/security/x509/CRLNumberExtension;->encodeThis()V -Lsun/security/x509/CRLNumberExtension;->get(Ljava/lang/String;)Ljava/lang/Object; -Lsun/security/x509/Extension;-><init>(Lsun/security/x509/Extension;)V -Lsun/security/x509/Extension;->encode(Lsun/security/util/DerOutputStream;)V -Lsun/security/x509/Extension;->getExtensionId()Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/GeneralName;-><init>(Lsun/security/x509/GeneralNameInterface;)V -Lsun/security/x509/GeneralName;->getName()Lsun/security/x509/GeneralNameInterface; -Lsun/security/x509/GeneralName;->getType()I -Lsun/security/x509/GeneralNames;-><init>()V -Lsun/security/x509/GeneralNames;-><init>(Lsun/security/util/DerValue;)V -Lsun/security/x509/GeneralNames;->add(Lsun/security/x509/GeneralName;)Lsun/security/x509/GeneralNames; -Lsun/security/x509/GeneralNames;->encode(Lsun/security/util/DerOutputStream;)V -Lsun/security/x509/GeneralNames;->isEmpty()Z -Lsun/security/x509/KeyIdentifier;-><init>(Ljava/security/PublicKey;)V -Lsun/security/x509/KeyIdentifier;->getIdentifier()[B -Lsun/security/x509/KeyIdentifier;->octetString:[B -Lsun/security/x509/KeyUsageExtension;-><init>([Z)V -Lsun/security/x509/KeyUsageExtension;->get(Ljava/lang/String;)Ljava/lang/Object; -Lsun/security/x509/NetscapeCertTypeExtension;-><init>([B)V -Lsun/security/x509/NetscapeCertTypeExtension;->get(Ljava/lang/String;)Ljava/lang/Object; -Lsun/security/x509/OIDMap$OIDInfo;->clazz:Ljava/lang/Class; -Lsun/security/x509/OIDMap;->getClass(Lsun/security/util/ObjectIdentifier;)Ljava/lang/Class; -Lsun/security/x509/OIDMap;->nameMap:Ljava/util/Map; -Lsun/security/x509/OIDMap;->oidMap:Ljava/util/Map; -Lsun/security/x509/PKIXExtensions;->CertificateIssuer_Id:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/SerialNumber;-><init>(Lsun/security/util/DerValue;)V -Lsun/security/x509/SubjectAlternativeNameExtension;->get(Ljava/lang/String;)Ljava/lang/Object; -Lsun/security/x509/SubjectKeyIdentifierExtension;-><init>([B)V -Lsun/security/x509/UniqueIdentity;-><init>(Lsun/security/util/DerInputStream;)V -Lsun/security/x509/UniqueIdentity;-><init>(Lsun/security/util/DerValue;)V -Lsun/security/x509/UniqueIdentity;->encode(Lsun/security/util/DerOutputStream;B)V -Lsun/security/x509/URIName;->getName()Ljava/lang/String; -Lsun/security/x509/URIName;->getScheme()Ljava/lang/String; -Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;)V -Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;Ljava/lang/String;)V -Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V -Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V -Lsun/security/x509/X500Name;-><init>(Lsun/security/util/DerInputStream;)V -Lsun/security/x509/X500Name;-><init>(Lsun/security/util/DerValue;)V -Lsun/security/x509/X500Name;-><init>([B)V -Lsun/security/x509/X500Name;->allAvas()Ljava/util/List; -Lsun/security/x509/X500Name;->asX500Name(Ljavax/security/auth/x500/X500Principal;)Lsun/security/x509/X500Name; -Lsun/security/x509/X500Name;->asX500Principal()Ljavax/security/auth/x500/X500Principal; -Lsun/security/x509/X500Name;->commonName_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/X500Name;->countryName_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/X500Name;->DNQUALIFIER_OID:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/X500Name;->DOMAIN_COMPONENT_OID:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/X500Name;->encode(Lsun/security/util/DerOutputStream;)V -Lsun/security/x509/X500Name;->GENERATIONQUALIFIER_OID:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/X500Name;->getCommonName()Ljava/lang/String; -Lsun/security/x509/X500Name;->GIVENNAME_OID:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/X500Name;->INITIALS_OID:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/X500Name;->ipAddress_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/X500Name;->isEmpty()Z -Lsun/security/x509/X500Name;->localityName_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/X500Name;->orgName_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/X500Name;->orgUnitName_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/X500Name;->SERIALNUMBER_OID:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/X500Name;->stateName_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/X500Name;->streetAddress_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/X500Name;->SURNAME_OID:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/X500Name;->title_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/X500Name;->userid_oid:Lsun/security/util/ObjectIdentifier; -Lsun/security/x509/X509CertImpl;-><init>(Lsun/security/util/DerValue;)V -Lsun/security/x509/X509CertImpl;-><init>(Lsun/security/x509/X509CertInfo;)V -Lsun/security/x509/X509CertImpl;-><init>([B)V -Lsun/security/x509/X509CertImpl;->algId:Lsun/security/x509/AlgorithmId; -Lsun/security/x509/X509CertImpl;->get(Ljava/lang/String;)Ljava/lang/Object; -Lsun/security/x509/X509CertImpl;->getEncodedInternal()[B -Lsun/security/x509/X509CertImpl;->parse(Lsun/security/util/DerValue;)V -Lsun/security/x509/X509CertImpl;->readOnly:Z -Lsun/security/x509/X509CertImpl;->sign(Ljava/security/PrivateKey;Ljava/lang/String;)V -Lsun/security/x509/X509CertImpl;->signature:[B -Lsun/security/x509/X509CertImpl;->signedCert:[B -Lsun/security/x509/X509CertInfo;-><init>()V -Lsun/security/x509/X509CertInfo;-><init>([B)V -Lsun/security/x509/X509CertInfo;->get(Ljava/lang/String;)Ljava/lang/Object; -Lsun/security/x509/X509CertInfo;->set(Ljava/lang/String;Ljava/lang/Object;)V -Lsun/security/x509/X509CRLEntryImpl;->getExtension(Lsun/security/util/ObjectIdentifier;)Lsun/security/x509/Extension; -Lsun/security/x509/X509CRLImpl;-><init>(Ljava/io/InputStream;)V -Lsun/security/x509/X509CRLImpl;-><init>(Lsun/security/util/DerValue;)V -Lsun/security/x509/X509CRLImpl;-><init>([B)V -Lsun/security/x509/X509CRLImpl;->getEncodedInternal()[B -Lsun/security/x509/X509Key;-><init>()V -Lsun/security/x509/X509Key;->algid:Lsun/security/x509/AlgorithmId; -Lsun/security/x509/X509Key;->encodedKey:[B -Lsun/security/x509/X509Key;->key:[B -Lsun/security/x509/X509Key;->parse(Lsun/security/util/DerValue;)Ljava/security/PublicKey; -Lsun/security/x509/X509Key;->unusedBits:I -Lsun/util/calendar/AbstractCalendar;->getDayOfWeekDateOnOrBefore(JI)J -Lsun/util/calendar/AbstractCalendar;->getTimeOfDayValue(Lsun/util/calendar/CalendarDate;)J -Lsun/util/calendar/BaseCalendar$Date;->getNormalizedYear()I -Lsun/util/calendar/BaseCalendar$Date;->setNormalizedYear(I)V -Lsun/util/calendar/CalendarDate;->getDayOfMonth()I -Lsun/util/calendar/CalendarDate;->getMonth()I -Lsun/util/calendar/CalendarDate;->getTimeOfDay()J -Lsun/util/calendar/CalendarDate;->getYear()I -Lsun/util/calendar/CalendarDate;->setDate(III)Lsun/util/calendar/CalendarDate; -Lsun/util/calendar/CalendarDate;->setDayOfMonth(I)Lsun/util/calendar/CalendarDate; -Lsun/util/calendar/CalendarDate;->setHours(I)Lsun/util/calendar/CalendarDate; -Lsun/util/calendar/CalendarDate;->setMillis(I)Lsun/util/calendar/CalendarDate; -Lsun/util/calendar/CalendarDate;->setMinutes(I)Lsun/util/calendar/CalendarDate; -Lsun/util/calendar/CalendarDate;->setSeconds(I)Lsun/util/calendar/CalendarDate; -Lsun/util/calendar/CalendarSystem;->forName(Ljava/lang/String;)Lsun/util/calendar/CalendarSystem; -Lsun/util/calendar/CalendarSystem;->getGregorianCalendar()Lsun/util/calendar/Gregorian; -Lsun/util/calendar/CalendarSystem;->getTime(Lsun/util/calendar/CalendarDate;)J -Lsun/util/calendar/CalendarSystem;->newCalendarDate(Ljava/util/TimeZone;)Lsun/util/calendar/CalendarDate; -Lsun/util/calendar/CalendarSystem;->validate(Lsun/util/calendar/CalendarDate;)Z -Lsun/util/calendar/CalendarUtils;->floorDivide(II)I -Lsun/util/calendar/CalendarUtils;->floorDivide(JJ)J -Lsun/util/calendar/CalendarUtils;->mod(II)I -Lsun/util/calendar/CalendarUtils;->mod(JJ)J -Lsun/util/calendar/Era;-><init>(Ljava/lang/String;Ljava/lang/String;JZ)V -Lsun/util/calendar/Era;->getAbbreviation()Ljava/lang/String; -Lsun/util/calendar/Era;->getName()Ljava/lang/String; -Lsun/util/calendar/Era;->getSinceDate()Lsun/util/calendar/CalendarDate; -Lsun/util/calendar/ImmutableGregorianDate;->unsupported()V -Lsun/util/calendar/LocalGregorianCalendar$Date;->getNormalizedYear()I -Lsun/util/calendar/LocalGregorianCalendar$Date;->setEra(Lsun/util/calendar/Era;)Lsun/util/calendar/LocalGregorianCalendar$Date; -Lsun/util/calendar/LocalGregorianCalendar$Date;->setNormalizedYear(I)V -Lsun/util/calendar/LocalGregorianCalendar$Date;->setYear(I)Lsun/util/calendar/LocalGregorianCalendar$Date; -Lsun/util/calendar/LocalGregorianCalendar;->newCalendarDate(Ljava/util/TimeZone;)Lsun/util/calendar/LocalGregorianCalendar$Date; -Lsun/util/calendar/LocalGregorianCalendar;->normalize(Lsun/util/calendar/CalendarDate;)Z -Lsun/util/calendar/LocalGregorianCalendar;->validate(Lsun/util/calendar/CalendarDate;)Z diff --git a/config/preloaded-classes b/config/preloaded-classes index 5940c45466fb..30959256c922 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -4117,6 +4117,10 @@ com.android.internal.util.StateMachine$SmHandler$StateInfo com.android.internal.util.VirtualRefBasePtr com.android.internal.util.XmlUtils com.android.internal.util.XmlUtils$WriteMapCallback +com.android.internal.util.function.NonaConsumer +com.android.internal.util.function.NonaFunction +com.android.internal.util.function.OctConsumer +com.android.internal.util.function.OctFunction com.android.internal.util.function.HeptConsumer com.android.internal.util.function.HeptFunction com.android.internal.util.function.HexConsumer diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 83fab7e5a45d..05bb9a1c2139 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -827,6 +827,8 @@ public class Activity extends ContextThemeWrapper /** The screen observation manager. Always access via {@link #getIntelligenceManager()}. */ @Nullable private IntelligenceManager mIntelligenceManager; + private final ArrayList<Application.ActivityLifecycleCallbacks> mActivityLifecycleCallbacks = + new ArrayList<Application.ActivityLifecycleCallbacks>(); static final class NonConfigurationInstances { Object activity; @@ -1065,6 +1067,288 @@ public class Activity extends ContextThemeWrapper } /** + * Register an {@link Application.ActivityLifecycleCallbacks} instance that receives + * lifecycle callbacks for only this Activity. + * <p> + * In relation to any + * {@link Application#registerActivityLifecycleCallbacks Application registered callbacks}, + * the callbacks registered here will always occur nested within those callbacks. This means: + * <ul> + * <li>Pre events will first be sent to Application registered callbacks, then to callbacks + * registered here.</li> + * <li>{@link Application.ActivityLifecycleCallbacks#onActivityCreated(Activity, Bundle)}, + * {@link Application.ActivityLifecycleCallbacks#onActivityStarted(Activity)}, and + * {@link Application.ActivityLifecycleCallbacks#onActivityResumed(Activity)} will + * be sent first to Application registered callbacks, then to callbacks registered here. + * For all other events, callbacks registered here will be sent first.</li> + * <li>Post events will first be sent to callbacks registered here, then to + * Application registered callbacks.</li> + * </ul> + * <p> + * If multiple callbacks are registered here, they receive events in a first in (up through + * {@link Application.ActivityLifecycleCallbacks#onActivityPostResumed}, last out + * ordering. + * <p> + * It is strongly recommended to register this in the constructor of your Activity to ensure + * you get all available callbacks. As this callback is associated with only this Activity, + * it is not usually necessary to {@link #unregisterActivityLifecycleCallbacks unregister} it + * unless you specifically do not want to receive further lifecycle callbacks. + * + * @param callback The callback instance to register + */ + public void registerActivityLifecycleCallbacks( + @NonNull Application.ActivityLifecycleCallbacks callback) { + synchronized (mActivityLifecycleCallbacks) { + mActivityLifecycleCallbacks.add(callback); + } + } + + /** + * Unregister an {@link Application.ActivityLifecycleCallbacks} previously registered + * with {@link #registerActivityLifecycleCallbacks}. It will not receive any further + * callbacks. + * + * @param callback The callback instance to unregister + * @see #registerActivityLifecycleCallbacks + */ + public void unregisterActivityLifecycleCallbacks( + @NonNull Application.ActivityLifecycleCallbacks callback) { + synchronized (mActivityLifecycleCallbacks) { + mActivityLifecycleCallbacks.remove(callback); + } + } + + private void dispatchActivityPreCreated(@Nullable Bundle savedInstanceState) { + getApplication().dispatchActivityPreCreated(this, savedInstanceState); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreCreated(this, + savedInstanceState); + } + } + } + + private void dispatchActivityCreated(@Nullable Bundle savedInstanceState) { + getApplication().dispatchActivityCreated(this, savedInstanceState); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityCreated(this, + savedInstanceState); + } + } + } + + private void dispatchActivityPostCreated(@Nullable Bundle savedInstanceState) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPostCreated(this, + savedInstanceState); + } + } + getApplication().dispatchActivityPostCreated(this, savedInstanceState); + } + + private void dispatchActivityPreStarted() { + getApplication().dispatchActivityPreStarted(this); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreStarted(this); + } + } + } + + private void dispatchActivityStarted() { + getApplication().dispatchActivityStarted(this); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityStarted(this); + } + } + } + + private void dispatchActivityPostStarted() { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]) + .onActivityPostStarted(this); + } + } + getApplication().dispatchActivityPostStarted(this); + } + + private void dispatchActivityPreResumed() { + getApplication().dispatchActivityPreResumed(this); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreResumed(this); + } + } + } + + private void dispatchActivityResumed() { + getApplication().dispatchActivityResumed(this); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityResumed(this); + } + } + } + + private void dispatchActivityPostResumed() { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPostResumed(this); + } + } + getApplication().dispatchActivityPostResumed(this); + } + + private void dispatchActivityPrePaused() { + getApplication().dispatchActivityPrePaused(this); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPrePaused(this); + } + } + } + + private void dispatchActivityPaused() { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPaused(this); + } + } + getApplication().dispatchActivityPaused(this); + } + + private void dispatchActivityPostPaused() { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPostPaused(this); + } + } + getApplication().dispatchActivityPostPaused(this); + } + + private void dispatchActivityPreStopped() { + getApplication().dispatchActivityPreStopped(this); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreStopped(this); + } + } + } + + private void dispatchActivityStopped() { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityStopped(this); + } + } + getApplication().dispatchActivityStopped(this); + } + + private void dispatchActivityPostStopped() { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]) + .onActivityPostStopped(this); + } + } + getApplication().dispatchActivityPostStopped(this); + } + + private void dispatchActivityPreSaveInstanceState(@NonNull Bundle outState) { + getApplication().dispatchActivityPreSaveInstanceState(this, outState); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]) + .onActivityPreSaveInstanceState(this, outState); + } + } + } + + private void dispatchActivitySaveInstanceState(@NonNull Bundle outState) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]) + .onActivitySaveInstanceState(this, outState); + } + } + getApplication().dispatchActivitySaveInstanceState(this, outState); + } + + private void dispatchActivityPostSaveInstanceState(@NonNull Bundle outState) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]) + .onActivityPostSaveInstanceState(this, outState); + } + } + getApplication().dispatchActivityPostSaveInstanceState(this, outState); + } + + private void dispatchActivityPreDestroyed() { + getApplication().dispatchActivityPreDestroyed(this); + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]) + .onActivityPreDestroyed(this); + } + } + } + + private void dispatchActivityDestroyed() { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityDestroyed(this); + } + } + getApplication().dispatchActivityDestroyed(this); + } + + private void dispatchActivityPostDestroyed() { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = callbacks.length - 1; i >= 0; i--) { + ((Application.ActivityLifecycleCallbacks) callbacks[i]) + .onActivityPostDestroyed(this); + } + } + getApplication().dispatchActivityPostDestroyed(this); + } + + private Object[] collectActivityLifecycleCallbacks() { + Object[] callbacks = null; + synchronized (mActivityLifecycleCallbacks) { + if (mActivityLifecycleCallbacks.size() > 0) { + callbacks = mActivityLifecycleCallbacks.toArray(); + } + } + return callbacks; + } + + /** * Called when the activity is starting. This is where most initialization * should go: calling {@link #setContentView(int)} to inflate the * activity's UI, using {@link #findViewById} to programmatically interact @@ -1119,7 +1403,7 @@ public class Activity extends ContextThemeWrapper ? mLastNonConfigurationInstances.fragments : null); } mFragments.dispatchCreate(); - getApplication().dispatchActivityCreated(this, savedInstanceState); + dispatchActivityCreated(savedInstanceState); if (mVoiceInteractor != null) { mVoiceInteractor.attachActivity(this); } @@ -1355,7 +1639,7 @@ public class Activity extends ContextThemeWrapper mFragments.doLoaderStart(); - getApplication().dispatchActivityStarted(this); + dispatchActivityStarted(); if (mAutoFillResetNeeded) { getAutofillManager().onVisibleForAutofill(); @@ -1426,7 +1710,7 @@ public class Activity extends ContextThemeWrapper @CallSuper protected void onResume() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this); - getApplication().dispatchActivityResumed(this); + dispatchActivityResumed(); mActivityTransitionState.onResume(this, isTopOfTask()); enableAutofillCompatibilityIfNeeded(); if (mAutoFillResetNeeded) { @@ -1642,13 +1926,13 @@ public class Activity extends ContextThemeWrapper * @param outState The bundle to save the state to. */ final void performSaveInstanceState(@NonNull Bundle outState) { - getApplication().dispatchActivityPreSaveInstanceState(this, outState); + dispatchActivityPreSaveInstanceState(outState); onSaveInstanceState(outState); saveManagedDialogs(outState); mActivityTransitionState.saveState(outState); storeHasCurrentPermissionRequest(outState); if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState); - getApplication().dispatchActivityPostSaveInstanceState(this, outState); + dispatchActivityPostSaveInstanceState(outState); } /** @@ -1662,13 +1946,13 @@ public class Activity extends ContextThemeWrapper */ final void performSaveInstanceState(@NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) { - getApplication().dispatchActivityPreSaveInstanceState(this, outState); + dispatchActivityPreSaveInstanceState(outState); onSaveInstanceState(outState, outPersistentState); saveManagedDialogs(outState); storeHasCurrentPermissionRequest(outState); if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState + ", " + outPersistentState); - getApplication().dispatchActivityPostSaveInstanceState(this, outState); + dispatchActivityPostSaveInstanceState(outState); } /** @@ -1731,7 +2015,7 @@ public class Activity extends ContextThemeWrapper outState.putBoolean(AUTOFILL_RESET_NEEDED, true); getAutofillManager().onSaveInstanceState(outState); } - getApplication().dispatchActivitySaveInstanceState(this, outState); + dispatchActivitySaveInstanceState(outState); } /** @@ -1831,7 +2115,7 @@ public class Activity extends ContextThemeWrapper @CallSuper protected void onPause() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this); - getApplication().dispatchActivityPaused(this); + dispatchActivityPaused(); if (mAutoFillResetNeeded) { if (!mAutoFillIgnoreFirstResumePause) { if (DEBUG_LIFECYCLE) Slog.v(TAG, "autofill notifyViewExited " + this); @@ -2015,7 +2299,7 @@ public class Activity extends ContextThemeWrapper if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this); if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false); mActivityTransitionState.onStop(); - getApplication().dispatchActivityStopped(this); + dispatchActivityStopped(); mTranslucentCallback = null; mCalled = true; @@ -2104,7 +2388,7 @@ public class Activity extends ContextThemeWrapper mActionBar.onDestroy(); } - getApplication().dispatchActivityDestroyed(this); + dispatchActivityDestroyed(); notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED); @@ -7284,7 +7568,7 @@ public class Activity extends ContextThemeWrapper @UnsupportedAppUsage final void performCreate(Bundle icicle, PersistableBundle persistentState) { - getApplication().dispatchActivityPreCreated(this, icicle); + dispatchActivityPreCreated(icicle); mCanEnterPictureInPicture = true; restoreHasCurrentPermissionRequest(icicle); if (persistentState != null) { @@ -7299,7 +7583,7 @@ public class Activity extends ContextThemeWrapper com.android.internal.R.styleable.Window_windowNoDisplay, false); mFragments.dispatchActivityCreated(); mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions()); - getApplication().dispatchActivityPostCreated(this, icicle); + dispatchActivityPostCreated(icicle); } final void performNewIntent(@NonNull Intent intent) { @@ -7308,7 +7592,7 @@ public class Activity extends ContextThemeWrapper } final void performStart(String reason) { - getApplication().dispatchActivityPreStarted(this); + dispatchActivityPreStarted(); mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions()); mFragments.noteStateNotSaved(); mCalled = false; @@ -7351,7 +7635,7 @@ public class Activity extends ContextThemeWrapper } mActivityTransitionState.enterReady(this); - getApplication().dispatchActivityPostStarted(this); + dispatchActivityPostStarted(); } /** @@ -7406,7 +7690,7 @@ public class Activity extends ContextThemeWrapper } final void performResume(boolean followedByPause, String reason) { - getApplication().dispatchActivityPreResumed(this); + dispatchActivityPreResumed(); performRestart(true /* start */, reason); mFragments.execPendingActions(); @@ -7456,11 +7740,11 @@ public class Activity extends ContextThemeWrapper "Activity " + mComponent.toShortString() + " did not call through to super.onPostResume()"); } - getApplication().dispatchActivityPostResumed(this); + dispatchActivityPostResumed(); } final void performPause() { - getApplication().dispatchActivityPrePaused(this); + dispatchActivityPrePaused(); mDoReportFullyDrawn = false; mFragments.dispatchPause(); mCalled = false; @@ -7473,7 +7757,7 @@ public class Activity extends ContextThemeWrapper "Activity " + mComponent.toShortString() + " did not call through to super.onPause()"); } - getApplication().dispatchActivityPostPaused(this); + dispatchActivityPostPaused(); } final void performUserLeaving() { @@ -7489,7 +7773,7 @@ public class Activity extends ContextThemeWrapper mCanEnterPictureInPicture = false; if (!mStopped) { - getApplication().dispatchActivityPreStopped(this); + dispatchActivityPreStopped(); if (mWindow != null) { mWindow.closeAllPanels(); } @@ -7524,13 +7808,13 @@ public class Activity extends ContextThemeWrapper } mStopped = true; - getApplication().dispatchActivityPostStopped(this); + dispatchActivityPostStopped(); } mResumed = false; } final void performDestroy() { - getApplication().dispatchActivityPreDestroyed(this); + dispatchActivityPreDestroyed(); mDestroyed = true; mWindow.destroy(); mFragments.dispatchDestroy(); @@ -7540,7 +7824,7 @@ public class Activity extends ContextThemeWrapper if (mVoiceInteractor != null) { mVoiceInteractor.detachActivity(); } - getApplication().dispatchActivityPostDestroyed(this); + dispatchActivityPostDestroyed(); } final void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode, diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 1edd7f5e429d..af3da0cbf5ee 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -243,6 +243,8 @@ public abstract class ActivityManagerInternal { public abstract void ensureBootCompleted(); public abstract void updateOomLevelsForDisplay(int displayId); public abstract boolean isActivityStartsLoggingEnabled(); + /** Returns true if the background activity starts is enabled. */ + public abstract boolean isBackgroundActivityStartsEnabled(); public abstract void reportCurKeyguardUsageEvent(boolean keyguardShowing); /** Input dispatch timeout to a window, start the ANR process. */ diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java index 56ccf6f4a76f..6fdf7c8b4fac 100644 --- a/core/java/android/app/ActivityTaskManager.java +++ b/core/java/android/app/ActivityTaskManager.java @@ -433,4 +433,18 @@ public class ActivityTaskManager { } return sb.toString(); } + + /** + * Clears launch params for the given package. + * @param packageNames the names of the packages of which the launch params are to be cleared + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) + public void clearLaunchParamsForPackages(List<String> packageNames) { + try { + getService().clearLaunchParamsForPackages(packageNames); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/AppDetailsActivity.java b/core/java/android/app/AppDetailsActivity.java index cd36e634f54b..b71af88e99a3 100644 --- a/core/java/android/app/AppDetailsActivity.java +++ b/core/java/android/app/AppDetailsActivity.java @@ -16,6 +16,7 @@ package android.app; +import android.annotation.TestApi; import android.content.Intent; import android.os.Bundle; @@ -24,7 +25,9 @@ import android.os.Bundle; * * @hide */ +@TestApi public class AppDetailsActivity extends Activity { + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 8a797dcaf449..7312b2c8163e 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -2981,4 +2981,13 @@ public class ApplicationPackageManager extends PackageManager { throw e.rethrowAsRuntimeException(); } } + + @Override + public void sendDeviceCustomizationReadyBroadcast() { + try { + mPM.sendDeviceCustomizationReadyBroadcast(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } } diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 09b77d5b8d0a..777a4949a132 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -445,4 +445,9 @@ interface IActivityTaskManager { void setPackageScreenCompatMode(in String packageName, int mode); boolean getPackageAskScreenCompat(in String packageName); void setPackageAskScreenCompat(in String packageName, boolean ask); + + /** + * Clears launch params for given packages. + */ + void clearLaunchParamsForPackages(in List<String> packageNames); } diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index 00547b4a5ce4..3a2038d40952 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -159,5 +159,5 @@ interface IWallpaperManager { /** * Called from SystemUI when it shows the AoD UI. */ - oneway void setInAmbientMode(boolean inAmbientMode, boolean animated); + oneway void setInAmbientMode(boolean inAmbientMode, long animationDuration); } diff --git a/core/java/android/app/Notification.aidl b/core/java/android/app/Notification.aidl index 9d8129ca601a..8a7156e971b4 100644 --- a/core/java/android/app/Notification.aidl +++ b/core/java/android/app/Notification.aidl @@ -17,3 +17,4 @@ package android.app; parcelable Notification; +parcelable Notification.Action;
\ No newline at end of file diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 75b56f31c887..16d35802a9f6 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1693,11 +1693,30 @@ public class Notification implements Parcelable } /** + * Throws an NPE if we are building a contextual action missing one of the fields + * necessary to display the action. + */ + private void checkContextualActionNullFields() { + if (mSemanticAction != SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) return; + + if (mIcon == null) { + throw new NullPointerException("Contextual Actions must contain a valid icon"); + } + + if (mIntent == null) { + throw new NullPointerException( + "Contextual Actions must contain a valid PendingIntent"); + } + } + + /** * Combine all of the options that have been set and return a new {@link Action} * object. * @return the built action */ public Action build() { + checkContextualActionNullFields(); + ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>(); RemoteInput[] previousDataInputs = (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS); @@ -4436,15 +4455,15 @@ public class Notification implements Parcelable return bitmap; } - private void bindProfileBadge(RemoteViews contentView) { + private void bindProfileBadge(RemoteViews contentView, StandardTemplateParams p) { Bitmap profileBadge = getProfileBadge(); if (profileBadge != null) { contentView.setImageViewBitmap(R.id.profile_badge, profileBadge); contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE); - if (isColorized()) { + if (isColorized(p)) { contentView.setDrawableTint(R.id.profile_badge, false, - getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP); + getPrimaryTextColor(p), PorterDuff.Mode.SRC_ATOP); } } } @@ -4507,16 +4526,6 @@ public class Notification implements Parcelable result); } - /** - * @param hasProgress whether the progress bar should be shown and set - * @param result - */ - private RemoteViews applyStandardTemplate(int resId, boolean hasProgress, - TemplateBindResult result) { - return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress) - .fillTextsFrom(this), result); - } - private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p, TemplateBindResult result) { RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId); @@ -4524,15 +4533,15 @@ public class Notification implements Parcelable resetStandardTemplate(contentView); final Bundle ex = mN.extras; - updateBackgroundColor(contentView); - bindNotificationHeader(contentView, p.ambient, p.headerTextSecondary); + updateBackgroundColor(contentView, p); + bindNotificationHeader(contentView, p); bindLargeIconAndReply(contentView, p, result); - boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex); + boolean showProgress = handleProgressBar(contentView, ex, p); if (p.title != null) { contentView.setViewVisibility(R.id.title, View.VISIBLE); contentView.setTextViewText(R.id.title, processTextSpans(p.title)); if (!p.ambient) { - setTextViewColorPrimary(contentView, R.id.title); + setTextViewColorPrimary(contentView, R.id.title, p); } contentView.setViewLayoutWidth(R.id.title, showProgress ? ViewGroup.LayoutParams.WRAP_CONTENT @@ -4543,7 +4552,7 @@ public class Notification implements Parcelable : com.android.internal.R.id.text; contentView.setTextViewText(textId, processTextSpans(p.text)); if (!p.ambient) { - setTextViewColorSecondary(contentView, textId); + setTextViewColorSecondary(contentView, textId, p); } contentView.setViewVisibility(textId, View.VISIBLE); } @@ -4560,8 +4569,9 @@ public class Notification implements Parcelable return text; } - private void setTextViewColorPrimary(RemoteViews contentView, int id) { - ensureColors(); + private void setTextViewColorPrimary(RemoteViews contentView, int id, + StandardTemplateParams p) { + ensureColors(p); contentView.setTextColor(id, mPrimaryTextColor); } @@ -4570,42 +4580,63 @@ public class Notification implements Parcelable } /** - * @return the primary text color + * Return the primary text color using the existing template params * @hide */ @VisibleForTesting public int getPrimaryTextColor() { - ensureColors(); + return getPrimaryTextColor(mParams); + } + + /** + * @param p the template params to inflate this with + * @return the primary text color + * @hide + */ + @VisibleForTesting + public int getPrimaryTextColor(StandardTemplateParams p) { + ensureColors(p); return mPrimaryTextColor; } /** - * @return the secondary text color + * Return the secondary text color using the existing template params * @hide */ @VisibleForTesting public int getSecondaryTextColor() { - ensureColors(); + return getSecondaryTextColor(mParams); + } + + /** + * @param p the template params to inflate this with + * @return the secondary text color + * @hide + */ + @VisibleForTesting + public int getSecondaryTextColor(StandardTemplateParams p) { + ensureColors(p); return mSecondaryTextColor; } - private void setTextViewColorSecondary(RemoteViews contentView, int id) { - ensureColors(); + private void setTextViewColorSecondary(RemoteViews contentView, int id, + StandardTemplateParams p) { + ensureColors(p); contentView.setTextColor(id, mSecondaryTextColor); } - private void ensureColors() { - int backgroundColor = getBackgroundColor(); + private void ensureColors(StandardTemplateParams p) { + int backgroundColor = getBackgroundColor(p); if (mPrimaryTextColor == COLOR_INVALID || mSecondaryTextColor == COLOR_INVALID || mTextColorsAreForBackground != backgroundColor) { mTextColorsAreForBackground = backgroundColor; - if (!hasForegroundColor() || !isColorized()) { + if (!hasForegroundColor() || !isColorized(p)) { mPrimaryTextColor = ContrastColorUtil.resolvePrimaryColor(mContext, backgroundColor, mInNightMode); mSecondaryTextColor = ContrastColorUtil.resolveSecondaryColor(mContext, backgroundColor, mInNightMode); - if (backgroundColor != COLOR_DEFAULT && isColorized()) { + if (backgroundColor != COLOR_DEFAULT && isColorized(p)) { mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast( mPrimaryTextColor, backgroundColor, 4.5); mSecondaryTextColor = ContrastColorUtil.findAlphaToMeetContrast( @@ -4673,10 +4704,11 @@ public class Notification implements Parcelable } } - private void updateBackgroundColor(RemoteViews contentView) { - if (isColorized()) { + private void updateBackgroundColor(RemoteViews contentView, + StandardTemplateParams p) { + if (isColorized(p)) { contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor", - getBackgroundColor()); + getBackgroundColor(p)); } else { // Clear it! contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource", @@ -4699,19 +4731,20 @@ public class Notification implements Parcelable remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight); } - private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) { + private boolean handleProgressBar(RemoteViews contentView, Bundle ex, + StandardTemplateParams p) { final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0); final int progress = ex.getInt(EXTRA_PROGRESS, 0); final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE); - if (hasProgress && (max != 0 || ind)) { + if (p.hasProgress && (max != 0 || ind)) { contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE); contentView.setProgressBar( R.id.progress, max, progress, ind); contentView.setProgressBackgroundTintList( R.id.progress, ColorStateList.valueOf(mContext.getColor( R.color.notification_progress_background_color))); - if (mN.color != COLOR_DEFAULT) { - ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor()); + if (getRawColor(p) != COLOR_DEFAULT) { + ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor(p)); contentView.setProgressTintList(R.id.progress, colorStateList); contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList); } @@ -4724,8 +4757,8 @@ public class Notification implements Parcelable private void bindLargeIconAndReply(RemoteViews contentView, StandardTemplateParams p, TemplateBindResult result) { - boolean largeIconShown = bindLargeIcon(contentView, p.hideLargeIcon || p.ambient); - boolean replyIconShown = bindReplyIcon(contentView, p.hideReplyIcon || p.ambient); + boolean largeIconShown = bindLargeIcon(contentView, p); + boolean replyIconShown = bindReplyIcon(contentView, p); contentView.setViewVisibility(R.id.right_icon_container, largeIconShown || replyIconShown ? View.VISIBLE : View.GONE); int marginEnd = calculateMarginEnd(largeIconShown, replyIconShown); @@ -4773,15 +4806,15 @@ public class Notification implements Parcelable * Bind the large icon. * @return if the largeIcon is visible */ - private boolean bindLargeIcon(RemoteViews contentView, boolean hideLargeIcon) { + private boolean bindLargeIcon(RemoteViews contentView, StandardTemplateParams p) { if (mN.mLargeIcon == null && mN.largeIcon != null) { mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon); } - boolean showLargeIcon = mN.mLargeIcon != null && !hideLargeIcon; + boolean showLargeIcon = mN.mLargeIcon != null && !p.hideLargeIcon && !p.ambient; if (showLargeIcon) { contentView.setViewVisibility(R.id.right_icon, View.VISIBLE); contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon); - processLargeLegacyIcon(mN.mLargeIcon, contentView); + processLargeLegacyIcon(mN.mLargeIcon, contentView, p); } return showLargeIcon; } @@ -4790,8 +4823,8 @@ public class Notification implements Parcelable * Bind the reply icon. * @return if the reply icon is visible */ - private boolean bindReplyIcon(RemoteViews contentView, boolean hideReplyIcon) { - boolean actionVisible = !hideReplyIcon; + private boolean bindReplyIcon(RemoteViews contentView, StandardTemplateParams p) { + boolean actionVisible = !p.hideReplyIcon && !p.ambient; Action action = null; if (actionVisible) { action = findReplyAction(); @@ -4801,7 +4834,7 @@ public class Notification implements Parcelable contentView.setViewVisibility(R.id.reply_icon_action, View.VISIBLE); contentView.setDrawableTint(R.id.reply_icon_action, false /* targetBackground */, - getNeutralColor(), + getNeutralColor(p), PorterDuff.Mode.SRC_ATOP); contentView.setOnClickPendingIntent(R.id.reply_icon_action, action.actionIntent); contentView.setRemoteInputs(R.id.reply_icon_action, action.mRemoteInputs); @@ -4828,41 +4861,41 @@ public class Notification implements Parcelable return null; } - private void bindNotificationHeader(RemoteViews contentView, boolean ambient, - CharSequence secondaryHeaderText) { - bindSmallIcon(contentView, ambient); - bindHeaderAppName(contentView, ambient); - if (!ambient) { + private void bindNotificationHeader(RemoteViews contentView, StandardTemplateParams p) { + bindSmallIcon(contentView, p); + bindHeaderAppName(contentView, p); + if (!p.ambient) { // Ambient view does not have these - bindHeaderText(contentView); - bindHeaderTextSecondary(contentView, secondaryHeaderText); - bindHeaderChronometerAndTime(contentView); - bindProfileBadge(contentView); + bindHeaderText(contentView, p); + bindHeaderTextSecondary(contentView, p); + bindHeaderChronometerAndTime(contentView, p); + bindProfileBadge(contentView, p); } - bindActivePermissions(contentView, ambient); - bindExpandButton(contentView); + bindActivePermissions(contentView, p); + bindExpandButton(contentView, p); mN.mUsesStandardHeader = true; } - private void bindActivePermissions(RemoteViews contentView, boolean ambient) { - int color = ambient ? resolveAmbientColor() : getNeutralColor(); + private void bindActivePermissions(RemoteViews contentView, StandardTemplateParams p) { + int color = p.ambient ? resolveAmbientColor(p) : getNeutralColor(p); contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP); contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP); contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP); } - private void bindExpandButton(RemoteViews contentView) { - int color = isColorized() ? getPrimaryTextColor() : getSecondaryTextColor(); + private void bindExpandButton(RemoteViews contentView, StandardTemplateParams p) { + int color = isColorized(p) ? getPrimaryTextColor(p) : getSecondaryTextColor(p); contentView.setDrawableTint(R.id.expand_button, false, color, PorterDuff.Mode.SRC_ATOP); contentView.setInt(R.id.notification_header, "setOriginalNotificationColor", color); } - private void bindHeaderChronometerAndTime(RemoteViews contentView) { + private void bindHeaderChronometerAndTime(RemoteViews contentView, + StandardTemplateParams p) { if (showsTimeOrChronometer()) { contentView.setViewVisibility(R.id.time_divider, View.VISIBLE); - setTextViewColorSecondary(contentView, R.id.time_divider); + setTextViewColorSecondary(contentView, R.id.time_divider, p); if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) { contentView.setViewVisibility(R.id.chronometer, View.VISIBLE); contentView.setLong(R.id.chronometer, "setBase", @@ -4870,11 +4903,11 @@ public class Notification implements Parcelable contentView.setBoolean(R.id.chronometer, "setStarted", true); boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN); contentView.setChronometerCountDown(R.id.chronometer, countsDown); - setTextViewColorSecondary(contentView, R.id.chronometer); + setTextViewColorSecondary(contentView, R.id.chronometer, p); } else { contentView.setViewVisibility(R.id.time, View.VISIBLE); contentView.setLong(R.id.time, "setTime", mN.when); - setTextViewColorSecondary(contentView, R.id.time); + setTextViewColorSecondary(contentView, R.id.time, p); } } else { // We still want a time to be set but gone, such that we can show and hide it @@ -4883,36 +4916,36 @@ public class Notification implements Parcelable } } - private void bindHeaderText(RemoteViews contentView) { - CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT); - if (headerText == null && mStyle != null && mStyle.mSummaryTextSet + private void bindHeaderText(RemoteViews contentView, StandardTemplateParams p) { + CharSequence summaryText = p.summaryText; + if (summaryText == null && mStyle != null && mStyle.mSummaryTextSet && mStyle.hasSummaryInHeader()) { - headerText = mStyle.mSummaryText; + summaryText = mStyle.mSummaryText; } - if (headerText == null + if (summaryText == null && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) { - headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT); + summaryText = mN.extras.getCharSequence(EXTRA_INFO_TEXT); } - if (headerText != null) { + if (summaryText != null) { // TODO: Remove the span entirely to only have the string with propper formating. contentView.setTextViewText(R.id.header_text, processTextSpans( - processLegacyText(headerText))); - setTextViewColorSecondary(contentView, R.id.header_text); + processLegacyText(summaryText))); + setTextViewColorSecondary(contentView, R.id.header_text, p); contentView.setViewVisibility(R.id.header_text, View.VISIBLE); contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE); - setTextViewColorSecondary(contentView, R.id.header_text_divider); + setTextViewColorSecondary(contentView, R.id.header_text_divider, p); } } - private void bindHeaderTextSecondary(RemoteViews contentView, CharSequence secondaryText) { - if (!TextUtils.isEmpty(secondaryText)) { + private void bindHeaderTextSecondary(RemoteViews contentView, StandardTemplateParams p) { + if (!TextUtils.isEmpty(p.headerTextSecondary)) { contentView.setTextViewText(R.id.header_text_secondary, processTextSpans( - processLegacyText(secondaryText))); - setTextViewColorSecondary(contentView, R.id.header_text_secondary); + processLegacyText(p.headerTextSecondary))); + setTextViewColorSecondary(contentView, R.id.header_text_secondary, p); contentView.setViewVisibility(R.id.header_text_secondary, View.VISIBLE); contentView.setViewVisibility(R.id.header_text_secondary_divider, View.VISIBLE); - setTextViewColorSecondary(contentView, R.id.header_text_secondary_divider); + setTextViewColorSecondary(contentView, R.id.header_text_secondary_divider, p); } } @@ -4950,23 +4983,27 @@ public class Notification implements Parcelable return String.valueOf(name); } - private void bindHeaderAppName(RemoteViews contentView, boolean ambient) { + private void bindHeaderAppName(RemoteViews contentView, StandardTemplateParams p) { contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName()); - if (isColorized() && !ambient) { - setTextViewColorPrimary(contentView, R.id.app_name_text); + if (isColorized(p)) { + setTextViewColorPrimary(contentView, R.id.app_name_text, p); } else { contentView.setTextColor(R.id.app_name_text, - ambient ? resolveAmbientColor() : getSecondaryTextColor()); + p.ambient ? resolveAmbientColor(p) : getSecondaryTextColor(p)); } } - private void bindSmallIcon(RemoteViews contentView, boolean ambient) { + private boolean isColorized(StandardTemplateParams p) { + return p.allowColorization && !p.ambient && mN.isColorized(); + } + + private void bindSmallIcon(RemoteViews contentView, StandardTemplateParams p) { if (mN.mSmallIcon == null && mN.icon != 0) { mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon); } contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon); contentView.setInt(R.id.icon, "setImageLevel", mN.iconLevel); - processSmallIconColor(mN.mSmallIcon, contentView, ambient); + processSmallIconColor(mN.mSmallIcon, contentView, p); } /** @@ -5041,8 +5078,7 @@ public class Notification implements Parcelable boolean actionHasValidInput = hasValidRemoteInput(action); validRemoteInput |= actionHasValidInput; - final RemoteViews button = generateActionButton(action, emphazisedMode, - p.ambient); + final RemoteViews button = generateActionButton(action, emphazisedMode, p); if (actionHasValidInput && !emphazisedMode) { // Clear the drawable button.setInt(R.id.action0, "setBackgroundResource", 0); @@ -5063,20 +5099,20 @@ public class Notification implements Parcelable View.VISIBLE); big.setTextViewText(R.id.notification_material_reply_text_1, processTextSpans(replyText[0])); - setTextViewColorSecondary(big, R.id.notification_material_reply_text_1); + setTextViewColorSecondary(big, R.id.notification_material_reply_text_1, p); big.setViewVisibility(R.id.notification_material_reply_progress, showSpinner ? View.VISIBLE : View.GONE); big.setProgressIndeterminateTintList( R.id.notification_material_reply_progress, ColorStateList.valueOf( - isColorized() ? getPrimaryTextColor() : resolveContrastColor())); + isColorized(p) ? getPrimaryTextColor(p) : resolveContrastColor(p))); if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1]) && p.maxRemoteInputHistory > 1) { big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE); big.setTextViewText(R.id.notification_material_reply_text_2, processTextSpans(replyText[1])); - setTextViewColorSecondary(big, R.id.notification_material_reply_text_2); + setTextViewColorSecondary(big, R.id.notification_material_reply_text_2, p); if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2]) && p.maxRemoteInputHistory > 2) { @@ -5084,7 +5120,7 @@ public class Notification implements Parcelable R.id.notification_material_reply_text_3, View.VISIBLE); big.setTextViewText(R.id.notification_material_reply_text_3, processTextSpans(replyText[2])); - setTextViewColorSecondary(big, R.id.notification_material_reply_text_3); + setTextViewColorSecondary(big, R.id.notification_material_reply_text_3, p); } } } @@ -5175,18 +5211,23 @@ public class Notification implements Parcelable * @hide */ public RemoteViews makeNotificationHeader(boolean ambient) { - Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED); - mN.extras.putBoolean(EXTRA_COLORIZED, false); + return makeNotificationHeader(mParams.reset().ambient(ambient).fillTextsFrom(this)); + } + + /** + * Construct a RemoteViews for the final notification header only. This will not be + * colorized. + * + * @param p the template params to inflate this with + */ + private RemoteViews makeNotificationHeader(StandardTemplateParams p) { + // Headers on their own are never colorized + p.disallowColorization(); RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(), - ambient ? R.layout.notification_template_ambient_header + p.ambient ? R.layout.notification_template_ambient_header : R.layout.notification_template_header); resetNotificationHeader(header); - bindNotificationHeader(header, ambient, null); - if (colorized != null) { - mN.extras.putBoolean(EXTRA_COLORIZED, colorized); - } else { - mN.extras.remove(EXTRA_COLORIZED); - } + bindNotificationHeader(header, p); return header; } @@ -5329,24 +5370,15 @@ public class Notification implements Parcelable * @hide */ public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) { - int color = mN.color; - mN.color = COLOR_DEFAULT; - CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT); - if (!useRegularSubtext || TextUtils.isEmpty(summary)) { - CharSequence newSummary = createSummaryText(); - if (!TextUtils.isEmpty(newSummary)) { - mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary); - } - } - - RemoteViews header = makeNotificationHeader(false /* ambient */); + StandardTemplateParams p = mParams.reset() + .forceDefaultColor() + .ambient(false) + .fillTextsFrom(this); + if (!useRegularSubtext || TextUtils.isEmpty(mParams.summaryText)) { + p.summaryText(createSummaryText()); + } + RemoteViews header = makeNotificationHeader(p); header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true); - if (summary != null) { - mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary); - } else { - mN.extras.remove(EXTRA_SUB_TEXT); - } - mN.color = color; return header; } @@ -5375,7 +5407,7 @@ public class Notification implements Parcelable } private RemoteViews generateActionButton(Action action, boolean emphazisedMode, - boolean ambient) { + StandardTemplateParams p) { final boolean tombstone = (action.actionIntent == null); RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(), emphazisedMode ? getEmphasizedActionLayoutResource() @@ -5392,7 +5424,7 @@ public class Notification implements Parcelable // change the background bgColor CharSequence title = action.title; ColorStateList[] outResultColor = null; - int background = resolveBackgroundColor(); + int background = resolveBackgroundColor(p); if (isLegacy()) { title = ContrastColorUtil.clearColorSpans(title); } else { @@ -5400,7 +5432,7 @@ public class Notification implements Parcelable title = ensureColorSpanContrast(title, background, outResultColor); } button.setTextViewText(R.id.action0, processTextSpans(title)); - setTextViewColorPrimary(button, R.id.action0); + setTextViewColorPrimary(button, R.id.action0, p); int rippleColor; boolean hasColorOverride = outResultColor != null && outResultColor[0] != null; if (hasColorOverride) { @@ -5411,11 +5443,12 @@ public class Notification implements Parcelable background, mInNightMode); button.setTextColor(R.id.action0, textColor); rippleColor = textColor; - } else if (mN.color != COLOR_DEFAULT && !isColorized() && mTintActionButtons) { - rippleColor = resolveContrastColor(); + } else if (getRawColor(p) != COLOR_DEFAULT && !isColorized(p) + && mTintActionButtons) { + rippleColor = resolveContrastColor(p); button.setTextColor(R.id.action0, rippleColor); } else { - rippleColor = getPrimaryTextColor(); + rippleColor = getPrimaryTextColor(p); } // We only want about 20% alpha for the ripple rippleColor = (rippleColor & 0x00ffffff) | 0x33000000; @@ -5427,13 +5460,15 @@ public class Notification implements Parcelable } else { button.setTextViewText(R.id.action0, processTextSpans( processLegacyText(action.title))); - if (isColorized() && !ambient) { - setTextViewColorPrimary(button, R.id.action0); - } else if (mN.color != COLOR_DEFAULT && mTintActionButtons) { + if (isColorized(p)) { + setTextViewColorPrimary(button, R.id.action0, p); + } else if (getRawColor(p) != COLOR_DEFAULT && mTintActionButtons) { button.setTextColor(R.id.action0, - ambient ? resolveAmbientColor() : resolveContrastColor()); + p.ambient ? resolveAmbientColor(p) : resolveContrastColor(p)); } } + button.setIntTag(R.id.action0, R.id.notification_action_index_tag, + mActions.indexOf(action)); return button; } @@ -5539,15 +5574,15 @@ public class Notification implements Parcelable * Apply any necessariy colors to the small icon */ private void processSmallIconColor(Icon smallIcon, RemoteViews contentView, - boolean ambient) { + StandardTemplateParams p) { boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon); int color; - if (ambient) { - color = resolveAmbientColor(); - } else if (isColorized()) { - color = getPrimaryTextColor(); + if (p.ambient) { + color = resolveAmbientColor(p); + } else if (isColorized(p)) { + color = getPrimaryTextColor(p); } else { - color = resolveContrastColor(); + color = resolveContrastColor(p); } if (colorable) { contentView.setDrawableTint(R.id.icon, false, color, @@ -5563,11 +5598,12 @@ public class Notification implements Parcelable * if it's grayscale). */ // TODO: also check bounds, transparency, that sort of thing. - private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) { + private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView, + StandardTemplateParams p) { if (largeIcon != null && isLegacy() && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) { // resolve color will fall back to the default when legacy - contentView.setDrawableTint(R.id.icon, false, resolveContrastColor(), + contentView.setDrawableTint(R.id.icon, false, resolveContrastColor(p), PorterDuff.Mode.SRC_ATOP); } } @@ -5578,29 +5614,43 @@ public class Notification implements Parcelable } } - int resolveContrastColor() { - if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) { + int resolveContrastColor(StandardTemplateParams p) { + int rawColor = getRawColor(p); + if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) { return mCachedContrastColor; } int color; int background = mContext.getColor( com.android.internal.R.color.notification_material_background_color); - if (mN.color == COLOR_DEFAULT) { - ensureColors(); + if (rawColor == COLOR_DEFAULT) { + ensureColors(p); color = ContrastColorUtil.resolveDefaultColor(mContext, background, mInNightMode); } else { - color = ContrastColorUtil.resolveContrastColor(mContext, mN.color, + color = ContrastColorUtil.resolveContrastColor(mContext, rawColor, background, mInNightMode); } if (Color.alpha(color) < 255) { // alpha doesn't go well for color filters, so let's blend it manually color = ContrastColorUtil.compositeColors(color, background); } - mCachedContrastColorIsFor = mN.color; + mCachedContrastColorIsFor = rawColor; return mCachedContrastColor = color; } + /** + * Return the raw color of this Notification, which doesn't necessarily satisfy contrast. + * + * @see #resolveContrastColor(StandardTemplateParams) for the contrasted color + * @param p the template params to inflate this with + */ + private int getRawColor(StandardTemplateParams p) { + if (p.forceDefaultColor) { + return COLOR_DEFAULT; + } + return mN.color; + } + int resolveNeutralColor() { if (mNeutralColor != COLOR_INVALID) { return mNeutralColor; @@ -5616,13 +5666,14 @@ public class Notification implements Parcelable return mNeutralColor; } - int resolveAmbientColor() { - if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) { + int resolveAmbientColor(StandardTemplateParams p) { + int rawColor = getRawColor(p); + if (mCachedAmbientColorIsFor == rawColor && mCachedAmbientColorIsFor != COLOR_INVALID) { return mCachedAmbientColor; } - final int contrasted = ContrastColorUtil.resolveAmbientColor(mContext, mN.color); + final int contrasted = ContrastColorUtil.resolveAmbientColor(mContext, rawColor); - mCachedAmbientColorIsFor = mN.color; + mCachedAmbientColorIsFor = rawColor; return mCachedAmbientColor = contrasted; } @@ -5854,9 +5905,9 @@ public class Notification implements Parcelable return R.layout.notification_material_action_tombstone; } - private int getBackgroundColor() { - if (isColorized()) { - return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color; + private int getBackgroundColor(StandardTemplateParams p) { + if (isColorized(p)) { + return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : getRawColor(p); } else { return COLOR_DEFAULT; } @@ -5864,10 +5915,11 @@ public class Notification implements Parcelable /** * Gets a neutral color that can be used for icons or similar that should not stand out. + * @param p the template params to inflate this with */ - private int getNeutralColor() { - if (isColorized()) { - return getSecondaryTextColor(); + private int getNeutralColor(StandardTemplateParams p) { + if (isColorized(p)) { + return getSecondaryTextColor(p); } else { return resolveNeutralColor(); } @@ -5875,9 +5927,10 @@ public class Notification implements Parcelable /** * Same as getBackgroundColor but also resolved the default color to the background. + * @param p the template params to inflate this with */ - private int resolveBackgroundColor() { - int backgroundColor = getBackgroundColor(); + private int resolveBackgroundColor(StandardTemplateParams p) { + int backgroundColor = getBackgroundColor(p); if (backgroundColor == COLOR_DEFAULT) { backgroundColor = mContext.getColor( com.android.internal.R.color.notification_material_background_color); @@ -5885,10 +5938,6 @@ public class Notification implements Parcelable return backgroundColor; } - private boolean isColorized() { - return mN.isColorized(); - } - private boolean shouldTintActionButtons() { return mTintActionButtons; } @@ -5914,7 +5963,7 @@ public class Notification implements Parcelable mBackgroundColor = backgroundColor; mForegroundColor = foregroundColor; mTextColorsAreForBackground = COLOR_INVALID; - ensureColors(); + ensureColors(mParams.reset().fillTextsFrom(this)); } /** @@ -6182,30 +6231,30 @@ public class Notification implements Parcelable } protected RemoteViews getStandardView(int layoutId) { - return getStandardView(layoutId, null); + StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder); + return getStandardView(layoutId, p, null); } + /** * Get the standard view for this style. * - * @param layoutId The layout id to use + * @param layoutId The layout id to use. + * @param p the params for this inflation. * @param result The result where template bind information is saved. * @return A remoteView for this style. * @hide */ - protected RemoteViews getStandardView(int layoutId, TemplateBindResult result) { + protected RemoteViews getStandardView(int layoutId, StandardTemplateParams p, + TemplateBindResult result) { checkBuilder(); - // Nasty. - CharSequence oldBuilderContentTitle = - mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE); if (mBigContentTitle != null) { - mBuilder.setContentTitle(mBigContentTitle); + p.title = mBigContentTitle; } - RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId, result); - - mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle); + RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId, p, + result); if (mBigContentTitle != null && mBigContentTitle.equals("")) { contentView.setViewVisibility(R.id.line1, View.GONE); @@ -6500,12 +6549,13 @@ public class Notification implements Parcelable mBuilder.mN.largeIcon = null; } + StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder); RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource(), - null /* result */); + p, null /* result */); if (mSummaryTextSet) { contentView.setTextViewText(R.id.text, mBuilder.processTextSpans( mBuilder.processLegacyText(mSummaryText))); - mBuilder.setTextViewColorSecondary(contentView, R.id.text); + mBuilder.setTextViewColorSecondary(contentView, R.id.text, p); contentView.setViewVisibility(R.id.text, View.VISIBLE); } mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon()); @@ -6698,24 +6748,24 @@ public class Notification implements Parcelable * @hide */ public RemoteViews makeBigContentView() { - - // Nasty - CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT); - mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null); - + StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder).text(null); TemplateBindResult result = new TemplateBindResult(); - RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource(), result); + RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource(), p, + result); contentView.setInt(R.id.big_text, "setImageEndMargin", result.getIconMarginEnd()); - mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text); - CharSequence bigTextText = mBuilder.processLegacyText(mBigText); if (TextUtils.isEmpty(bigTextText)) { // In case the bigtext is null / empty fall back to the normal text to avoid a weird // experience - bigTextText = mBuilder.processLegacyText(text); + bigTextText = mBuilder.processLegacyText( + mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT)); } - applyBigTextContentView(mBuilder, contentView, bigTextText); + contentView.setTextViewText(R.id.big_text, mBuilder.processTextSpans(bigTextText)); + mBuilder.setTextViewColorSecondary(contentView, R.id.big_text, p); + contentView.setViewVisibility(R.id.big_text, + TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE); + contentView.setBoolean(R.id.big_text, "setHasImage", mBuilder.mN.hasLargeIcon()); return contentView; } @@ -6733,14 +6783,6 @@ public class Notification implements Parcelable return !Objects.equals(String.valueOf(getBigText()), String.valueOf(newS.getBigText())); } - static void applyBigTextContentView(Builder builder, - RemoteViews contentView, CharSequence bigTextText) { - contentView.setTextViewText(R.id.big_text, builder.processTextSpans(bigTextText)); - builder.setTextViewColorSecondary(contentView, R.id.big_text); - contentView.setViewVisibility(R.id.big_text, - TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE); - contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon()); - } } /** @@ -7224,24 +7266,26 @@ public class Notification implements Parcelable isOneToOne = !isGroupConversation(); } TemplateBindResult bindResult = new TemplateBindResult(); + StandardTemplateParams p = mBuilder.mParams.reset().hasProgress(false).title( + conversationTitle).text(null) + .hideLargeIcon(hideRightIcons || isOneToOne) + .hideReplyIcon(hideRightIcons) + .headerTextSecondary(conversationTitle); RemoteViews contentView = mBuilder.applyStandardTemplateWithActions( mBuilder.getMessagingLayoutResource(), - mBuilder.mParams.reset().hasProgress(false).title(conversationTitle).text(null) - .hideLargeIcon(hideRightIcons || isOneToOne) - .hideReplyIcon(hideRightIcons) - .headerTextSecondary(conversationTitle), + p, bindResult); addExtras(mBuilder.mN.extras); // also update the end margin if there is an image contentView.setViewLayoutMarginEnd(R.id.notification_messaging, bindResult.getIconMarginEnd()); contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor", - mBuilder.isColorized() ? mBuilder.getPrimaryTextColor() - : mBuilder.resolveContrastColor()); + mBuilder.isColorized(p) ? mBuilder.getPrimaryTextColor(p) + : mBuilder.resolveContrastColor(p)); contentView.setInt(R.id.status_bar_latest_event_content, "setSenderTextColor", - mBuilder.getPrimaryTextColor()); + mBuilder.getPrimaryTextColor(p)); contentView.setInt(R.id.status_bar_latest_event_content, "setMessageTextColor", - mBuilder.getSecondaryTextColor()); + mBuilder.getSecondaryTextColor(p)); contentView.setBoolean(R.id.status_bar_latest_event_content, "setDisplayImagesAtEnd", displayImagesAtEnd); contentView.setIcon(R.id.status_bar_latest_event_content, "setAvatarReplacement", @@ -7705,15 +7749,9 @@ public class Notification implements Parcelable * @hide */ public RemoteViews makeBigContentView() { - // Remove the content text so it disappears unless you have a summary - // Nasty - CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT); - mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null); - + StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder).text(null); TemplateBindResult result = new TemplateBindResult(); - RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource(), result); - - mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText); + RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource(), p, result); int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3, R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6}; @@ -7760,7 +7798,7 @@ public class Notification implements Parcelable contentView.setViewVisibility(rowIds[i], View.VISIBLE); contentView.setTextViewText(rowIds[i], mBuilder.processTextSpans(mBuilder.processLegacyText(str))); - mBuilder.setTextViewColorSecondary(contentView, rowIds[i]); + mBuilder.setTextViewColorSecondary(contentView, rowIds[i], p); contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0); handleInboxImageMargin(contentView, rowIds[i], first, result.getIconMarginEnd()); @@ -7997,7 +8035,7 @@ public class Notification implements Parcelable } private void bindMediaActionButton(RemoteViews container, @IdRes int buttonId, - Action action, int color) { + Action action, StandardTemplateParams p) { final boolean tombstone = (action.actionIntent == null); container.setViewVisibility(buttonId, View.VISIBLE); container.setImageViewIcon(buttonId, action.getIcon()); @@ -8008,8 +8046,8 @@ public class Notification implements Parcelable Configuration currentConfig = resources.getConfiguration(); boolean inNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; - int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized() - ? color + int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized(p) + ? getActionColor(p) : ContrastColorUtil.resolveColor(mBuilder.mContext, Notification.COLOR_DEFAULT, inNightMode); @@ -8031,8 +8069,10 @@ public class Notification implements Parcelable } private RemoteViews makeMediaContentView() { + StandardTemplateParams p = mBuilder.mParams.reset().hasProgress(false).fillTextsFrom( + mBuilder); RemoteViews view = mBuilder.applyStandardTemplate( - R.layout.notification_template_material_media, false, /* hasProgress */ + R.layout.notification_template_material_media, p, null /* result */); final int numActions = mBuilder.mActions.size(); @@ -8047,7 +8087,7 @@ public class Notification implements Parcelable for (int i = 0; i < MAX_MEDIA_BUTTONS_IN_COMPACT; i++) { if (i < numActionsToShow) { final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]); - bindMediaActionButton(view, MEDIA_BUTTON_IDS[i], action, getActionColor()); + bindMediaActionButton(view, MEDIA_BUTTON_IDS[i], action, p); } else { view.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE); } @@ -8062,9 +8102,9 @@ public class Notification implements Parcelable return view; } - private int getActionColor() { - return mBuilder.isColorized() ? mBuilder.getPrimaryTextColor() - : mBuilder.resolveContrastColor(); + private int getActionColor(StandardTemplateParams p) { + return mBuilder.isColorized(p) ? mBuilder.getPrimaryTextColor(p) + : mBuilder.resolveContrastColor(p); } private RemoteViews makeMediaBigContentView() { @@ -8076,13 +8116,14 @@ public class Notification implements Parcelable if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) { return null; } + StandardTemplateParams p = mBuilder.mParams.reset().hasProgress(false).fillTextsFrom( + mBuilder); RemoteViews big = mBuilder.applyStandardTemplate( - R.layout.notification_template_material_big_media, false, null /* result */); + R.layout.notification_template_material_big_media, p , null /* result */); for (int i = 0; i < MAX_MEDIA_BUTTONS; i++) { if (i < actionCount) { - bindMediaActionButton(big, MEDIA_BUTTON_IDS[i], mBuilder.mActions.get(i), - getActionColor()); + bindMediaActionButton(big, MEDIA_BUTTON_IDS[i], mBuilder.mActions.get(i), p); } else { big.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE); } @@ -8338,7 +8379,7 @@ public class Notification implements Parcelable // Need to clone customContent before adding, because otherwise it can no longer be // parceled independently of remoteViews. customContent = customContent.clone(); - customContent.overrideTextColors(mBuilder.getPrimaryTextColor()); + customContent.overrideTextColors(mBuilder.getPrimaryTextColor(mBuilder.mParams)); remoteViews.removeAllViews(id); remoteViews.addView(id, customContent); remoteViews.setReapplyDisallowed(); @@ -9881,17 +9922,23 @@ public class Notification implements Parcelable CharSequence title; CharSequence text; CharSequence headerTextSecondary; + CharSequence summaryText; int maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES; boolean hideLargeIcon; boolean hideReplyIcon; + boolean allowColorization = true; + boolean forceDefaultColor = false; final StandardTemplateParams reset() { hasProgress = true; ambient = false; title = null; text = null; + summaryText = null; headerTextSecondary = null; maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES; + allowColorization = true; + forceDefaultColor = false; return this; } @@ -9910,6 +9957,11 @@ public class Notification implements Parcelable return this; } + final StandardTemplateParams summaryText(CharSequence text) { + this.summaryText = text; + return this; + } + final StandardTemplateParams headerTextSecondary(CharSequence text) { this.headerTextSecondary = text; return this; @@ -9925,6 +9977,16 @@ public class Notification implements Parcelable return this; } + final StandardTemplateParams disallowColorization() { + this.allowColorization = false; + return this; + } + + final StandardTemplateParams forceDefaultColor() { + this.forceDefaultColor = true; + return this; + } + final StandardTemplateParams ambient(boolean ambient) { Preconditions.checkState(title == null && text == null, "must set ambient before text"); this.ambient = ambient; @@ -9941,6 +10003,7 @@ public class Notification implements Parcelable text = extras.getCharSequence(EXTRA_TEXT); } this.text = b.processLegacyText(text, ambient); + this.summaryText = extras.getCharSequence(EXTRA_SUB_TEXT); return this; } diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java index 3ea3da25e2fc..f0f7d899ff07 100644 --- a/core/java/android/app/WallpaperInfo.java +++ b/core/java/android/app/WallpaperInfo.java @@ -16,6 +16,7 @@ package android.app; +import android.annotation.SystemApi; import android.app.slice.Slice; import android.content.ComponentName; import android.content.Context; @@ -330,7 +331,9 @@ public final class WallpaperInfo implements Parcelable { * @see WallpaperService.Engine#onAmbientModeChanged(boolean, boolean) * @see WallpaperService.Engine#isInAmbientMode() * @return {@code true} if wallpaper can draw when in ambient mode. + * @hide */ + @SystemApi public boolean supportsAmbientMode() { return mSupportsAmbientMode; } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 00c1863a1ef6..98d2a406fc0c 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -9992,4 +9992,111 @@ public class DevicePolicyManager { throw re.rethrowFromSystemServer(); } } + + /** + * Whitelists a package that is allowed to access cross profile calendar APIs. + * + * <p>Called by a profile owner of a managed profile. + * + * @param admin which {@link DeviceAdminReceiver} this request is associated with. + * @param packageName name of the package to be whitelisted. + * @throws SecurityException if {@code admin} is not a profile owner. + * + * @see #removeCrossProfileCalendarPackage(ComponentName, String) + * @see #getCrossProfileCalendarPackages(ComponentName) + */ + public void addCrossProfileCalendarPackage(@NonNull ComponentName admin, + @NonNull String packageName) { + throwIfParentInstance("addCrossProfileCalendarPackage"); + if (mService != null) { + try { + mService.addCrossProfileCalendarPackage(admin, packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Removes a package that was allowed to access cross profile calendar APIs + * from the whitelist. + * + * <p>Called by a profile owner of a managed profile. + * + * @param admin which {@link DeviceAdminReceiver} this request is associated with. + * @param packageName name of the package to be removed from the whitelist. + * @return {@code true} if the package is successfully removed from the whitelist, + * {@code false} otherwise. + * @throws SecurityException if {@code admin} is not a profile owner. + * + * @see #addCrossProfileCalendarPackage(ComponentName, String) + * @see #getCrossProfileCalendarPackages(ComponentName) + */ + public boolean removeCrossProfileCalendarPackage(@NonNull ComponentName admin, + @NonNull String packageName) { + throwIfParentInstance("removeCrossProfileCalendarPackage"); + if (mService != null) { + try { + return mService.removeCrossProfileCalendarPackage(admin, packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return false; + } + + /** + * Gets a set of package names that are whitelisted to access cross profile calendar APIs. + * + * <p>Called by a profile owner of a managed profile. + * + * @param admin which {@link DeviceAdminReceiver} this request is associated with. + * @return the set of names of packages that were previously whitelisted via + * {@link #addCrossProfileCalendarPackage(ComponentName, String)}, or an + * empty set if none have been whitelisted. + * @throws SecurityException if {@code admin} is not a profile owner. + * + * @see #addCrossProfileCalendarPackage(ComponentName, String) + * @see #removeCrossProfileCalendarPackage(ComponentName, String) + */ + public @NonNull Set<String> getCrossProfileCalendarPackages(@NonNull ComponentName admin) { + throwIfParentInstance("getCrossProfileCalendarPackages"); + if (mService != null) { + try { + return new ArraySet<>(mService.getCrossProfileCalendarPackages(admin)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return Collections.emptySet(); + } + + /** + * Returns if a package is whitelisted to access cross profile calendar APIs. + * + * <p>To query for a specific user, use + * {@link Context#createPackageContextAsUser(String, int, UserHandle)} to create a context for + * that user, and get a {@link DevicePolicyManager} from this context. + * + * @param packageName the name of the package + * @return {@code true} if the package is whitelisted to access cross profile calendar APIs. + * {@code false} otherwise. + * + * @see #addCrossProfileCalendarPackage(ComponentName, String) + * @see #removeCrossProfileCalendarPackage(ComponentName, String) + * @see #getCrossProfileCalendarPackages(ComponentName) + * @hide + */ + public @NonNull boolean isPackageAllowedToAccessCalendar(@NonNull String packageName) { + throwIfParentInstance("isPackageAllowedToAccessCalendar"); + if (mService != null) { + try { + return mService.isPackageAllowedToAccessCalendarForUser(packageName, + mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return false; + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 60f79d62873b..297676d397bf 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -422,4 +422,9 @@ interface IDevicePolicyManager { void grantDeviceIdsAccessToProfileOwner(in ComponentName who, int userId); void installUpdateFromFile(in ComponentName admin, in ParcelFileDescriptor updateFileDescriptor, in StartInstallingUpdateCallback listener); + + void addCrossProfileCalendarPackage(in ComponentName admin, String packageName); + boolean removeCrossProfileCalendarPackage(in ComponentName admin, String packageName); + List<String> getCrossProfileCalendarPackages(in ComponentName admin); + boolean isPackageAllowedToAccessCalendarForUser(String packageName, int userHandle); } diff --git a/core/java/android/app/backup/OWNERS b/core/java/android/app/backup/OWNERS index 1c9a43acfa65..673d85fe79c5 100644 --- a/core/java/android/app/backup/OWNERS +++ b/core/java/android/app/backup/OWNERS @@ -1,7 +1,6 @@ -artikz@google.com +anniemeng@google.com brufino@google.com bryanmawhinney@google.com ctate@google.com jorlow@google.com -mkarpinski@google.com diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java index 7988008f03c0..2174255a3619 100644 --- a/core/java/android/bluetooth/le/ScanRecord.java +++ b/core/java/android/bluetooth/le/ScanRecord.java @@ -116,6 +116,9 @@ public final class ScanRecord { */ @Nullable public byte[] getManufacturerSpecificData(int manufacturerId) { + if (mManufacturerSpecificData == null) { + return null; + } return mManufacturerSpecificData.get(manufacturerId); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index cec8ef59b961..ff57b0311d60 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -741,16 +741,22 @@ public abstract class Context { /** Return the name of this application's package. */ public abstract String getPackageName(); - /** @hide Return the name of the base context this context is derived from. */ + /** + * @hide Return the name of the base context this context is derived from. + * This is the same as {@link #getOpPackageName()} except in + * cases where system components are loaded into other app processes, in which + * case {@link #getOpPackageName()} will be the name of the primary package in + * that process (so that app ops uid verification will work with the name). + */ @UnsupportedAppUsage public abstract String getBasePackageName(); - /** @hide Return the package name that should be used for app ops calls from - * this context. This is the same as {@link #getBasePackageName()} except in - * cases where system components are loaded into other app processes, in which - * case this will be the name of the primary package in that process (so that app - * ops uid verification will work with the name). */ - @TestApi + /** + * Return the package name that should be used for {@link android.app.AppOpsManager} calls from + * this context, so that app ops manager's uid verification will work with the name. + * <p> + * This is not generally intended for third party application developers. + */ public abstract String getOpPackageName(); /** Return the full application info for this context's package. */ diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e7f0053721d1..6fd5061eee63 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4116,6 +4116,18 @@ public class Intent implements Parcelable, Cloneable { */ public static final String ACTION_DOCK_ACTIVE = "android.intent.action.DOCK_ACTIVE"; + /** + * Broadcast Action: Indicates that a new device customization has been + * downloaded and applied (packages installed, runtime resource overlays + * enabled, xml files copied, ...), and that it is time for components that + * need to for example clear their caches to do so now. + * + * @hide + */ + @SystemApi + public static final String ACTION_DEVICE_CUSTOMIZATION_READY = + "android.intent.action.DEVICE_CUSTOMIZATION_READY"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index d0eff2e0fda9..dbea821fab2b 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -676,4 +676,6 @@ interface IPackageManager { String getSystemTextClassifierPackageName(); boolean isPackageStateProtected(String packageName, int userId); + + void sendDeviceCustomizationReadyBroadcast(); } diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index ecdd810653ec..099d15ad61c2 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -22,6 +22,9 @@ import android.apex.ApexInfo; import android.os.Parcel; import android.os.Parcelable; +import java.util.ArrayList; +import java.util.List; + /** * Overall information about the contents of a package. This corresponds * to all of the information collected from AndroidManifest.xml. @@ -204,7 +207,10 @@ public class PackageInfo implements Parcelable { * {@link PackageManager#GET_PERMISSIONS} was set. This list includes * all permissions requested, even those that were not granted or known * by the system at install time. + * + * @deprecated Use {@link #usesPermissions} */ + @Deprecated public String[] requestedPermissions; /** @@ -214,10 +220,23 @@ public class PackageInfo implements Parcelable { * {@link PackageManager#GET_PERMISSIONS} was set. Each value matches * the corresponding entry in {@link #requestedPermissions}, and will have * the flag {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate. + * + * @deprecated Use {@link #usesPermissions} */ + @Deprecated public int[] requestedPermissionsFlags; /** + * Array of all {@link android.R.styleable#AndroidManifestUsesPermission + * <uses-permission>} tags included under <manifest>, + * or null if there were none. This is only filled in if the flag + * {@link PackageManager#GET_PERMISSIONS} was set. This list includes + * all permissions requested, even those that were not granted or known + * by the system at install time. + */ + public UsesPermissionInfo[] usesPermissions; + + /** * Flag for {@link #requestedPermissionsFlags}: the requested permission * is required for the application to run; the user can not optionally * disable it. Currently all permissions are required. @@ -456,6 +475,7 @@ public class PackageInfo implements Parcelable { dest.writeTypedArray(permissions, parcelableFlags); dest.writeStringArray(requestedPermissions); dest.writeIntArray(requestedPermissionsFlags); + dest.writeTypedArray(usesPermissions, parcelableFlags); dest.writeTypedArray(signatures, parcelableFlags); dest.writeTypedArray(configPreferences, parcelableFlags); dest.writeTypedArray(reqFeatures, parcelableFlags); @@ -520,6 +540,7 @@ public class PackageInfo implements Parcelable { permissions = source.createTypedArray(PermissionInfo.CREATOR); requestedPermissions = source.createStringArray(); requestedPermissionsFlags = source.createIntArray(); + usesPermissions = source.createTypedArray(UsesPermissionInfo.CREATOR); signatures = source.createTypedArray(Signature.CREATOR); configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR); reqFeatures = source.createTypedArray(FeatureInfo.CREATOR); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index a4b724ba48e7..6421dc5a3f68 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -6409,4 +6409,18 @@ public abstract class PackageManager { "isPackageStateProtected not implemented in subclass"); } + /** + * Notify to the rest of the system that a new device configuration has + * been prepared and that it is time to refresh caches. + * + * @see android.content.Intent#ACTION_DEVICE_CUSTOMIZATION_READY + * + * @hide + */ + @SystemApi + public void sendDeviceCustomizationReadyBroadcast() { + throw new UnsupportedOperationException( + "sendDeviceCustomizationReadyBroadcast not implemented in subclass"); + } + } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index d00c9a036a53..49189e53f385 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -785,18 +785,23 @@ public class PackageParser { pi.permissions[i] = generatePermissionInfo(p.permissions.get(i), flags); } } - N = p.requestedPermissions.size(); + N = p.usesPermissionInfos.size(); if (N > 0) { pi.requestedPermissions = new String[N]; pi.requestedPermissionsFlags = new int[N]; + pi.usesPermissions = new UsesPermissionInfo[N]; for (int i=0; i<N; i++) { - final String perm = p.requestedPermissions.get(i); + UsesPermissionInfo info = p.usesPermissionInfos.get(i); + final String perm = info.getPermission(); pi.requestedPermissions[i] = perm; + int permissionFlags = 0; // The notion of required permissions is deprecated but for compatibility. - pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED; + permissionFlags |= PackageInfo.REQUESTED_PERMISSION_REQUIRED; if (grantedPermissions != null && grantedPermissions.contains(perm)) { - pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED; + permissionFlags |= PackageInfo.REQUESTED_PERMISSION_GRANTED; } + pi.requestedPermissionsFlags[i] = permissionFlags; + pi.usesPermissions[i] = new UsesPermissionInfo(info, permissionFlags); } } } @@ -2114,12 +2119,12 @@ public class PackageParser { return null; } } else if (tagName.equals(TAG_USES_PERMISSION)) { - if (!parseUsesPermission(pkg, res, parser)) { + if (!parseUsesPermission(pkg, res, parser, outError)) { return null; } } else if (tagName.equals(TAG_USES_PERMISSION_SDK_M) || tagName.equals(TAG_USES_PERMISSION_SDK_23)) { - if (!parseUsesPermission(pkg, res, parser)) { + if (!parseUsesPermission(pkg, res, parser, outError)) { return null; } } else if (tagName.equals(TAG_USES_CONFIGURATION)) { @@ -2442,7 +2447,7 @@ public class PackageParser { newPermsMsg.append(' '); } newPermsMsg.append(npi.name); - pkg.requestedPermissions.add(npi.name); + addRequestedPermission(pkg, npi.name); pkg.implicitPermissions.add(npi.name); } } @@ -2463,7 +2468,7 @@ public class PackageParser { for (int in = 0; in < newPerms.size(); in++) { final String perm = newPerms.get(in); if (!pkg.requestedPermissions.contains(perm)) { - pkg.requestedPermissions.add(perm); + addRequestedPermission(pkg, perm); pkg.implicitPermissions.add(perm); } } @@ -2543,13 +2548,13 @@ public class PackageParser { } } else { if (FORCE_AUDIO_PACKAGES.contains(pkg.packageName)) { - pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO); + addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_AUDIO); } if (FORCE_VIDEO_PACKAGES.contains(pkg.packageName)) { - pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO); + addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_VIDEO); } if (FORCE_IMAGES_PACKAGES.contains(pkg.packageName)) { - pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES); + addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_IMAGES); } } @@ -2589,6 +2594,14 @@ public class PackageParser { } /** + * Helper method for adding a requested permission to a package outside of a uses-permission. + */ + private void addRequestedPermission(Package pkg, String permission) { + pkg.requestedPermissions.add(permission); + pkg.usesPermissionInfos.add(new UsesPermissionInfo(permission)); + } + + /** * Computes the targetSdkVersion to use at runtime. If the package is not * compatible with this platform, populates {@code outError[0]} with an * error message. @@ -2845,8 +2858,8 @@ public class PackageParser { return certSha256Digests; } - private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser) - throws XmlPullParserException, IOException { + private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser, + String[] outError) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestUsesPermission); @@ -2870,6 +2883,44 @@ public class PackageParser { final String requiredNotfeature = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredNotFeature, 0); + int dataSentOffDevice = sa.getInt( + com.android.internal.R.styleable.AndroidManifestUsesPermission_dataSentOffDevice, 0); + + int dataSharedWithThirdParty = sa.getInt( + com.android.internal.R.styleable.AndroidManifestUsesPermission_dataSharedWithThirdParty, 0); + + int dataUsedForMonetization = sa.getInt( + com.android.internal.R.styleable.AndroidManifestUsesPermission_dataUsedForMonetization, 0); + + int retentionWeeks = -1; + int retention; + + String rawRetention = sa.getString( + com.android.internal.R.styleable.AndroidManifestUsesPermission_dataRetentionTime); + + if (rawRetention == null) { + retention = UsesPermissionInfo.RETENTION_UNDEFINED; + } else if ("notRetained".equals(rawRetention)) { + retention = UsesPermissionInfo.RETENTION_NOT_RETAINED; + } else if ("userSelected".equals(rawRetention)) { + retention = UsesPermissionInfo.RETENTION_USER_SELECTED; + } else if ("unlimited".equals(rawRetention)) { + retention = UsesPermissionInfo.RETENTION_UNLIMITED; + } else { + // A number of weeks was specified + retention = UsesPermissionInfo.RETENTION_SPECIFIED; + retentionWeeks = sa.getInt( + com.android.internal.R.styleable.AndroidManifestUsesPermission_dataRetentionTime, + -1); + + if (retentionWeeks < 0) { + outError[0] = "Bad value provided for dataRetentionTime."; + mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; + XmlUtils.skipCurrentTag(parser); + sa.recycle(); + return false; + } + } sa.recycle(); XmlUtils.skipCurrentTag(parser); @@ -2902,6 +2953,10 @@ public class PackageParser { + parser.getPositionDescription()); } + UsesPermissionInfo info = new UsesPermissionInfo(name, dataSentOffDevice, + dataSharedWithThirdParty, dataUsedForMonetization, retention, retentionWeeks); + pkg.usesPermissionInfos.add(info); + return true; } @@ -3236,6 +3291,10 @@ public class PackageParser { perm.info.flags = sa.getInt( com.android.internal.R.styleable.AndroidManifestPermission_permissionFlags, 0); + perm.info.usageInfoRequired = sa.getInt( + com.android.internal.R.styleable.AndroidManifestPermission_usageInfoRequired, 0) + != 0; + sa.recycle(); if (perm.info.protectionLevel == -1) { @@ -6370,6 +6429,9 @@ public class PackageParser { @UnsupportedAppUsage public final ArrayList<String> requestedPermissions = new ArrayList<String>(); + public final ArrayList<UsesPermissionInfo> usesPermissionInfos = + new ArrayList<>(); + /** Permissions requested but not in the manifest. */ public final ArrayList<String> implicitPermissions = new ArrayList<>(); @@ -6900,6 +6962,7 @@ public class PackageParser { dest.readStringList(requestedPermissions); internStringArrayList(requestedPermissions); + dest.readParcelableList(usesPermissionInfos, boot); dest.readStringList(implicitPermissions); internStringArrayList(implicitPermissions); protectedBroadcasts = dest.createStringArrayList(); @@ -7066,6 +7129,7 @@ public class PackageParser { dest.writeParcelableList(instrumentation, flags); dest.writeStringList(requestedPermissions); + dest.writeParcelableList(usesPermissionInfos, flags); dest.writeStringList(implicitPermissions); dest.writeStringList(protectedBroadcasts); diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index e21c33ad3bc1..be6ed51e3c89 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -21,7 +21,6 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; -import static android.content.pm.PackageManager.MATCH_ALL; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS; @@ -130,9 +129,6 @@ public class PackageUserState { * </p> */ public boolean isMatch(ComponentInfo componentInfo, int flags) { - if ((flags & MATCH_ALL) != 0) { - return true; - } final boolean isSystemApp = componentInfo.applicationInfo.isSystemApp(); final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0; if (!isAvailable(flags) diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index 60c06a1e4d87..d9d6b5f87eac 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -308,6 +309,12 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { */ public CharSequence nonLocalizedDescription; + /** + * If {@code true} an application targeting {@link Build.VERSION_CODES#Q} <em>must</em> + * include permission data usage information in order to be able to be granted this permission. + */ + public boolean usageInfoRequired; + /** @hide */ public static int fixProtectionLevel(int level) { if (level == PROTECTION_SIGNATURE_OR_SYSTEM) { @@ -394,6 +401,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { descriptionRes = orig.descriptionRes; requestRes = orig.requestRes; nonLocalizedDescription = orig.nonLocalizedDescription; + usageInfoRequired = orig.usageInfoRequired; } /** @@ -458,6 +466,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { dest.writeInt(descriptionRes); dest.writeInt(requestRes); TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags); + dest.writeInt(usageInfoRequired ? 1 : 0); } /** @hide */ @@ -498,5 +507,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { descriptionRes = source.readInt(); requestRes = source.readInt(); nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); + usageInfoRequired = source.readInt() != 0; } } diff --git a/core/java/android/content/pm/UsesPermissionInfo.java b/core/java/android/content/pm/UsesPermissionInfo.java new file mode 100644 index 000000000000..d08548fa31a5 --- /dev/null +++ b/core/java/android/content/pm/UsesPermissionInfo.java @@ -0,0 +1,275 @@ +/* + * 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.content.pm; + +import android.annotation.IntDef; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.RetentionPolicy; +/** + * Information you can retrive about a particular application requested permission. This + * corresponds to information collected from the AndroidManifest.xml's <uses-permission> + * tags. + */ +public final class UsesPermissionInfo extends PackageItemInfo implements Parcelable { + + /** + * Flag for {@link #getFlags()}: the requested permission is currently granted to the + * application. + */ + public static final int FLAG_REQUESTED_PERMISSION_GRANTED = 1 << 1; + + /** @hide */ + @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_REQUESTED_PERMISSION_GRANTED}) + @java.lang.annotation.Retention(RetentionPolicy.SOURCE) + public @interface Flags {} + + /** An unset value for {@link #getDataSentOffDevice()}, + * {@link #getDataSharedWithThirdParty()}, and {@link #getDataUsedForMonetization()} + */ + public static final int USAGE_UNDEFINED = 0; + + /** + * A yes value for {@link #getDataSentOffDevice()}, {@link #getDataSharedWithThirdParty()}, + * and {@link #getDataUsedForMonetization()} corresponding to the <code>yes</code> value of + * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty}, + * and {@link android.R.attr#dataUsedForMonetization} attributes. + */ + public static final int USAGE_YES = 1; + + /** + * A user triggered only value for {@link #getDataSentOffDevice()}, + * {@link #getDataSharedWithThirdParty()}, and {@link #getDataUsedForMonetization()} + * corresponding to the <code>userTriggered</code> value of + * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty}, + * and {@link android.R.attr#dataUsedForMonetization} attributes. + */ + public static final int USAGE_USER_TRIGGERED = 2; + + /** + * A no value for {@link #getDataSentOffDevice()}, {@link #getDataSharedWithThirdParty()}, + * and {@link #getDataUsedForMonetization()} corresponding to the <code>no</code> value of + * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty}, + * and {@link android.R.attr#dataUsedForMonetization} attributes. + */ + public static final int USAGE_NO = 3; + + /** @hide */ + @IntDef(prefix = {"USAGE_"}, value = { + USAGE_UNDEFINED, + USAGE_YES, + USAGE_USER_TRIGGERED, + USAGE_NO}) + @java.lang.annotation.Retention(RetentionPolicy.SOURCE) + public @interface Usage {} + + /** + * An unset value for {@link #getDataRetention}. + */ + public static final int RETENTION_UNDEFINED = 0; + + /** + * A data not retained value for {@link #getDataRetention()} corresponding to the + * <code>notRetained</code> value of {@link android.R.attr#dataRetentionTime}. + */ + public static final int RETENTION_NOT_RETAINED = 1; + + /** + * A user selected value for {@link #getDataRetention()} corresponding to the + * <code>userSelected</code> value of {@link android.R.attr#dataRetentionTime}. + */ + public static final int RETENTION_USER_SELECTED = 2; + + /** + * An unlimited value for {@link #getDataRetention()} corresponding to the + * <code>unlimited</code> value of {@link android.R.attr#dataRetentionTime}. + */ + public static final int RETENTION_UNLIMITED = 3; + + /** + * A specified value for {@link #getDataRetention()} corresponding to providing the number of + * weeks data is retained in {@link android.R.attr#dataRetentionTime}. The number of weeks + * is available in {@link #getDataRetentionWeeks()}. + */ + public static final int RETENTION_SPECIFIED = 4; + + /** @hide */ + @IntDef(prefix = {"RETENTION_"}, value = { + RETENTION_UNDEFINED, + RETENTION_NOT_RETAINED, + RETENTION_USER_SELECTED, + RETENTION_UNLIMITED, + RETENTION_SPECIFIED}) + @java.lang.annotation.Retention(RetentionPolicy.SOURCE) + public @interface Retention {} + + private final String mPermission; + private final @Flags int mFlags; + private final @Usage int mDataSentOffDevice; + private final @Usage int mDataSharedWithThirdParty; + private final @Usage int mDataUsedForMonetization; + private final @Retention int mDataRetention; + private final int mDataRetentionWeeks; + + /** @hide */ + public UsesPermissionInfo(String permission) { + mPermission = permission; + mDataSentOffDevice = USAGE_UNDEFINED; + mDataSharedWithThirdParty = USAGE_UNDEFINED; + mDataUsedForMonetization = USAGE_UNDEFINED; + mDataRetention = RETENTION_UNDEFINED; + mDataRetentionWeeks = -1; + mFlags = 0; + } + + /** @hide */ + public UsesPermissionInfo(String permission, + @Usage int dataSentOffDevice, @Usage int dataSharedWithThirdParty, + @Usage int dataUsedForMonetization, @Retention int dataRetention, + int dataRetentionWeeks) { + mPermission = permission; + mDataSentOffDevice = dataSentOffDevice; + mDataSharedWithThirdParty = dataSharedWithThirdParty; + mDataUsedForMonetization = dataUsedForMonetization; + mDataRetention = dataRetention; + mDataRetentionWeeks = dataRetentionWeeks; + mFlags = 0; + } + + /** @hide */ + public UsesPermissionInfo(UsesPermissionInfo orig) { + this(orig, orig.mFlags); + } + + /** @hide */ + public UsesPermissionInfo(UsesPermissionInfo orig, int flags) { + super(orig); + mPermission = orig.mPermission; + mFlags = flags; + mDataSentOffDevice = orig.mDataSentOffDevice; + mDataSharedWithThirdParty = orig.mDataSharedWithThirdParty; + mDataUsedForMonetization = orig.mDataUsedForMonetization; + mDataRetention = orig.mDataRetention; + mDataRetentionWeeks = orig.mDataRetentionWeeks; + } + + /** + * The name of the requested permission. + */ + public String getPermission() { + return mPermission; + } + + public @Flags int getFlags() { + return mFlags; + } + + /** + * If the application sends the data guarded by this permission off the device. + * + * See {@link android.R.attr#dataSentOffDevice} + */ + public @Usage int getDataSentOffDevice() { + return mDataSentOffDevice; + } + + /** + * If the application or its services shares the data guarded by this permission with third + * parties. + * + * See {@link android.R.attr#dataSharedWithThirdParty} + */ + public @Usage int getDataSharedWithThirdParty() { + return mDataSharedWithThirdParty; + } + + /** + * If the application or its services use the data guarded by this permission for monetization + * purposes. + * + * See {@link android.R.attr#dataUsedForMonetization} + */ + public @Usage int getDataUsedForMonetization() { + return mDataUsedForMonetization; + } + + /** + * How long the application or its services store the data guarded by this permission. + * If set to {@link #RETENTION_SPECIFIED} {@link #getDataRetentionWeeks()} will contain the + * number of weeks the data is stored. + * + * See {@link android.R.attr#dataRetentionTime} + */ + public @Retention int getDataRetention() { + return mDataRetention; + } + + /** + * If {@link #getDataRetention()} is {@link #RETENTION_SPECIFIED} the number of weeks the + * application or its services store data guarded by this permission. + * + * @throws IllegalStateException if {@link #getDataRetention} is not + * {@link #RETENTION_SPECIFIED}. + */ + public int getDataRetentionWeeks() { + if (mDataRetention != RETENTION_SPECIFIED) { + throw new IllegalStateException("Data retention weeks not specified"); + } + return mDataRetentionWeeks; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(mPermission); + dest.writeInt(mFlags); + dest.writeInt(mDataSentOffDevice); + dest.writeInt(mDataSharedWithThirdParty); + dest.writeInt(mDataUsedForMonetization); + dest.writeInt(mDataRetention); + dest.writeInt(mDataRetentionWeeks); + } + + private UsesPermissionInfo(Parcel source) { + super(source); + mPermission = source.readString(); + mFlags = source.readInt(); + mDataSentOffDevice = source.readInt(); + mDataSharedWithThirdParty = source.readInt(); + mDataUsedForMonetization = source.readInt(); + mDataRetention = source.readInt(); + mDataRetentionWeeks = source.readInt(); + } + + public static final Creator<UsesPermissionInfo> CREATOR = + new Creator<UsesPermissionInfo>() { + @Override + public UsesPermissionInfo createFromParcel(Parcel source) { + return new UsesPermissionInfo(source); + } + @Override + public UsesPermissionInfo[] newArray(int size) { + return new UsesPermissionInfo[size]; + } + }; +} diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 9db1f922bd5d..9d61f028bc91 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -24,7 +24,7 @@ import android.util.SparseArray; import android.view.Display; import android.view.DisplayInfo; import android.view.Surface; -import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; /** * Display manager local system service interface. @@ -126,7 +126,7 @@ public abstract class DisplayManagerInternal { * Called by the window manager to perform traversals while holding a * surface flinger transaction. */ - public abstract void performTraversal(SurfaceControl.Transaction t); + public abstract void performTraversal(Transaction t); /** * Tells the display manager about properties of the display that depend on the windows on it. @@ -383,6 +383,6 @@ public abstract class DisplayManagerInternal { * update the position of its surfaces as part of the same transaction. */ public interface DisplayTransactionListener { - void onDisplayTransaction(); + void onDisplayTransaction(Transaction t); } } diff --git a/core/java/android/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java index df0d46be1354..f2c50b5cc464 100644 --- a/core/java/android/hardware/display/DisplayViewport.java +++ b/core/java/android/hardware/display/DisplayViewport.java @@ -19,6 +19,7 @@ package android.hardware.display; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; +import android.annotation.Nullable; import android.graphics.Rect; import android.text.TextUtils; @@ -71,6 +72,9 @@ public final class DisplayViewport { // The ID used to uniquely identify this display. public String uniqueId; + // The physical port that the associated display device is connected to. + public @Nullable Byte physicalPort; + public @ViewportType int type; public void copyFrom(DisplayViewport viewport) { @@ -82,6 +86,7 @@ public final class DisplayViewport { deviceWidth = viewport.deviceWidth; deviceHeight = viewport.deviceHeight; uniqueId = viewport.uniqueId; + physicalPort = viewport.physicalPort; type = viewport.type; } @@ -113,6 +118,7 @@ public final class DisplayViewport { && deviceWidth == other.deviceWidth && deviceHeight == other.deviceHeight && TextUtils.equals(uniqueId, other.uniqueId) + && physicalPort == other.physicalPort && type == other.type; } @@ -128,6 +134,7 @@ public final class DisplayViewport { result += prime * result + deviceWidth; result += prime * result + deviceHeight; result += prime * result + uniqueId.hashCode(); + result += prime * result + physicalPort; result += prime * result + type; return result; } @@ -139,6 +146,7 @@ public final class DisplayViewport { + ", valid=" + valid + ", displayId=" + displayId + ", uniqueId='" + uniqueId + "'" + + ", physicalPort=" + physicalPort + ", orientation=" + orientation + ", logicalFrame=" + logicalFrame + ", physicalFrame=" + physicalFrame diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 651caece01f9..2abcb4cd9379 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -1055,6 +1055,9 @@ public class Process { */ public static final native long getPss(int pid); + /** @hide */ + public static final native long[] getRss(int pid); + /** * Specifies the outcome of having started a process. * @hide diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java index 866bd9a17f41..acb9eac3572c 100644 --- a/core/java/android/os/StatsLogEventWrapper.java +++ b/core/java/android/os/StatsLogEventWrapper.java @@ -43,6 +43,7 @@ public final class StatsLogEventWrapper implements Parcelable { int mTag; long mElapsedTimeNs; long mWallClockTimeNs; + WorkSource mWorkSource = null; public StatsLogEventWrapper(int tag, long elapsedTimeNs, long wallClockTimeNs) { this.mTag = tag; @@ -71,6 +72,17 @@ public final class StatsLogEventWrapper implements Parcelable { }; /** + * Set work source if any. + */ + public void setWorkSource(WorkSource ws) { + if (ws.getWorkChains() == null || ws.getWorkChains().size() == 0) { + Slog.w(TAG, "Empty worksource!"); + return; + } + mWorkSource = ws; + } + + /** * Write a int value. */ public void writeInt(int val) { @@ -119,11 +131,6 @@ public final class StatsLogEventWrapper implements Parcelable { mValues.add(val ? 1 : 0); } - /** - * Writes the stored fields to a byte array. Will first write a new-line character to denote - * END_LIST before writing contents to byte array. - */ - public void writeToParcel(Parcel out, int flags) { if (DEBUG) { Slog.d(TAG, @@ -133,6 +140,34 @@ public final class StatsLogEventWrapper implements Parcelable { out.writeInt(mTag); out.writeLong(mElapsedTimeNs); out.writeLong(mWallClockTimeNs); + if (mWorkSource != null) { + ArrayList<android.os.WorkSource.WorkChain> workChains = mWorkSource.getWorkChains(); + // number of chains + out.writeInt(workChains.size()); + for (int i = 0; i < workChains.size(); i++) { + android.os.WorkSource.WorkChain wc = workChains.get(i); + if (wc.getSize() == 0) { + Slog.w(TAG, "Empty work chain."); + out.writeInt(0); + continue; + } + if (wc.getUids().length != wc.getTags().length + || wc.getUids().length != wc.getSize()) { + Slog.w(TAG, "Malformated work chain."); + out.writeInt(0); + continue; + } + // number of nodes + out.writeInt(wc.getSize()); + for (int j = 0; j < wc.getSize(); j++) { + out.writeInt(wc.getUids()[j]); + out.writeString(wc.getTags()[j] == null ? "" : wc.getTags()[j]); + } + } + } else { + // no chains + out.writeInt(0); + } out.writeInt(mTypes.size()); for (int i = 0; i < mTypes.size(); i++) { out.writeInt(mTypes.get(i)); diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 291891effe4e..e0e4fe29f48a 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -572,6 +572,34 @@ public final class MediaStore { public void setSecondaryDirectory(@Nullable String secondaryDirectory) { this.secondaryDirectory = secondaryDirectory; } + + /** + * Optionally set the Uri from where the file has been downloaded. This is used + * for files being added to {@link Downloads} table. + * + * @see DownloadColumns#DOWNLOAD_URI + */ + public void setDownloadUri(@Nullable Uri downloadUri) { + if (downloadUri == null) { + this.insertValues.remove(DownloadColumns.DOWNLOAD_URI); + } else { + this.insertValues.put(DownloadColumns.DOWNLOAD_URI, downloadUri.toString()); + } + } + + /** + * Optionally set the Uri indicating HTTP referer of the file. This is used for + * files being added to {@link Downloads} table. + * + * @see DownloadColumns#REFERER_URI + */ + public void setRefererUri(@Nullable Uri refererUri) { + if (refererUri == null) { + this.insertValues.remove(DownloadColumns.REFERER_URI); + } else { + this.insertValues.put(DownloadColumns.REFERER_URI, refererUri.toString()); + } + } } /** @@ -763,7 +791,7 @@ public final class MediaStore { * Type: BOOLEAN * * @see MediaStore#createPending(Context, PendingParams) - * @see MediaStore#QUERY_ARG_INCLUDE_PENDING + * @see MediaStore#PARAM_INCLUDE_PENDING */ public static final String IS_PENDING = "is_pending"; @@ -927,6 +955,12 @@ public final class MediaStore { * Constant for the {@link #MEDIA_TYPE} column indicating that file is a playlist file. */ public static final int MEDIA_TYPE_PLAYLIST = 4; + + /** + * Column indicating if the file is part of Downloads collection. + * @hide + */ + public static final String IS_DOWNLOAD = "is_download"; } } @@ -940,6 +974,80 @@ public final class MediaStore { public static final Point MICRO_SIZE = new Point(96, 96); } + /** Column fields for downloaded files used in {@link Downloads} table */ + public interface DownloadColumns extends MediaColumns { + /** + * Uri indicating where the file has been downloaded from. + * <p> + * Type: TEXT + */ + String DOWNLOAD_URI = "download_uri"; + + /** + * Uri indicating HTTP referer of {@link #DOWNLOAD_URI}. + * <p> + * Type: TEXT + */ + String REFERER_URI = "referer_uri"; + } + + /** + * Container for downloaded files. + * + * <p> + * Querying for downloads from this table will return files contributed via + * {@link PendingSession} and also ones which were downloaded using + * {@link android.app.DownloadManager} APIs. + */ + public static final class Downloads implements DownloadColumns { + private Downloads() {} + + /** + * The content:// style URI for the internal storage. + */ + public static final Uri INTERNAL_CONTENT_URI = + getContentUri("internal"); + + /** + * The content:// style URI for the "primary" external storage + * volume. + */ + public static final Uri EXTERNAL_CONTENT_URI = + getContentUri("external"); + + /** + * Get the content:// style URI for the downloads table on the + * given volume. + * + * @param volumeName the name of the volume to get the URI for + * @return the URI to the image media table on the given volume + */ + public static Uri getContentUri(String volumeName) { + return AUTHORITY_URI.buildUpon().appendPath(volumeName) + .appendPath("downloads").build(); + } + + /** @hide */ + public static Uri getContentUriForPath(@NonNull String path) { + return getContentUri(getVolumeNameForPath(path)); + } + } + + private static String getVolumeNameForPath(@NonNull String path) { + final StorageManager sm = AppGlobals.getInitialApplication() + .getSystemService(StorageManager.class); + final StorageVolume sv = sm.getStorageVolume(new File(path)); + if (sv != null) { + if (sv.isPrimary()) { + return VOLUME_EXTERNAL; + } else { + return sv.getUuid(); + } + } else { + return VOLUME_INTERNAL; + } + } + /** * This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended * to be accessed elsewhere. @@ -1671,18 +1779,7 @@ public final class MediaStore { * access this path. */ public static @Nullable Uri getContentUriForPath(@NonNull String path) { - final StorageManager sm = AppGlobals.getInitialApplication() - .getSystemService(StorageManager.class); - final StorageVolume sv = sm.getStorageVolume(new File(path)); - if (sv != null) { - if (sv.isPrimary()) { - return EXTERNAL_CONTENT_URI; - } else { - return getContentUri(sv.getUuid()); - } - } else { - return INTERNAL_CONTENT_URI; - } + return getContentUri(getVolumeNameForPath(path)); } /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4369ea2a6693..d7729037bfad 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7967,6 +7967,14 @@ public final class Settings { "managed_profile_contact_remote_search"; /** + * Whether parent profile can access remote calendar data in managed profile. + * + * @hide + */ + public static final String CROSS_PROFILE_CALENDAR_ENABLED = + "cross_profile_calendar_enabled"; + + /** * Whether or not the automatic storage manager is enabled and should run on the device. * * @hide @@ -11035,6 +11043,16 @@ public final class Settings { = "activity_starts_logging_enabled"; /** + * Feature flag to enable or disable the background activity starts. + * When disabled, apps aren't allowed to start activities unless they're in the foreground. + * Type: int (0 for false, 1 for true) + * Default: 1 + * @hide + */ + public static final String BACKGROUND_ACTIVITY_STARTS_ENABLED = + "background_activity_starts_enabled"; + + /** * @hide * @see com.android.server.appbinding.AppBindingConstants */ @@ -11600,7 +11618,7 @@ public final class Settings { /** * Whether or not show hidden launcher icon apps feature is enabled. * Type: int (0 for false, 1 for true) - * Default: 0 + * Default: 1 * @hide */ public static final String SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED = diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index ab94f432968c..1ddc099efa49 100644 --- a/core/java/android/service/notification/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -16,6 +16,7 @@ package android.service.notification; +import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.content.pm.ParceledListSlice; @@ -50,4 +51,5 @@ oneway interface INotificationListener void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded); void onNotificationDirectReply(String key); void onSuggestedReplySent(String key, in CharSequence reply, int source); + void onActionClicked(String key, in Notification.Action action, int source); } diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java index 68da83f9e7d8..c850a4e0f815 100644 --- a/core/java/android/service/notification/NotificationAssistantService.java +++ b/core/java/android/service/notification/NotificationAssistantService.java @@ -19,9 +19,11 @@ package android.service.notification; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; +import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.app.Notification; import android.app.NotificationChannel; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; @@ -189,11 +191,20 @@ public abstract class NotificationAssistantService extends NotificationListenerS * Implement this to know when a suggested reply is sent. * @param key the notification key * @param reply the reply that is just sent - * @param source the source of the reply, e.g. SOURCE_FROM_APP + * @param source the source that provided the reply, e.g. SOURCE_FROM_APP */ public void onSuggestedReplySent(String key, CharSequence reply, @Source int source) {} /** + * Implement this to know when an action is clicked. + * @param key the notification key + * @param action the action that is just clicked + * @param source the source that provided the action, e.g. SOURCE_FROM_APP + */ + public void onActionClicked(String key, @Nullable Notification.Action action, int source) { + } + + /** * Updates a notification. N.B. this won’t cause * an existing notification to alert, but might allow a future update to * this notification to alert. @@ -317,6 +328,15 @@ public abstract class NotificationAssistantService extends NotificationListenerS args.argi2 = source; mHandler.obtainMessage(MyHandler.MSG_ON_SUGGESTED_REPLY_SENT, args).sendToTarget(); } + + @Override + public void onActionClicked(String key, Notification.Action action, int source) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = key; + args.arg2 = action; + args.argi2 = source; + mHandler.obtainMessage(MyHandler.MSG_ON_ACTION_CLICKED, args).sendToTarget(); + } } private final class MyHandler extends Handler { @@ -326,6 +346,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS public static final int MSG_ON_NOTIFICATION_EXPANSION_CHANGED = 4; public static final int MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT = 5; public static final int MSG_ON_SUGGESTED_REPLY_SENT = 6; + public static final int MSG_ON_ACTION_CLICKED = 7; public MyHandler(Looper looper) { super(looper, null, false); @@ -395,6 +416,15 @@ public abstract class NotificationAssistantService extends NotificationListenerS onSuggestedReplySent(key, reply, source); break; } + case MSG_ON_ACTION_CLICKED: { + SomeArgs args = (SomeArgs) msg.obj; + String key = (String) args.arg1; + Notification.Action action = (Notification.Action) args.arg2; + int source = args.argi2; + args.recycle(); + onActionClicked(key, action, source); + break; + } } } } diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 756a7c6a54b0..1fe97b79fd69 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1382,6 +1382,11 @@ public abstract class NotificationListenerService extends Service { } @Override + public void onActionClicked(String key, Notification.Action action, int source) { + // no-op in the listener + } + + @Override public void onNotificationChannelModification(String pkgName, UserHandle user, NotificationChannel channel, @ChannelOrGroupModificationTypes int modificationType) { diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl index dccce406e32c..ebce4846c16b 100644 --- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl +++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl @@ -27,7 +27,7 @@ oneway interface IWallpaperEngine { void setDesiredSize(int width, int height); void setDisplayPadding(in Rect padding); void setVisibility(boolean visible); - void setInAmbientMode(boolean inAmbientDisplay, boolean animated); + void setInAmbientMode(boolean inAmbientDisplay, long animationDuration); void dispatchPointer(in MotionEvent event); void dispatchWallpaperCommand(String action, int x, int y, int z, in Bundle extras); diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index f6bb762a0bef..a095b0d8b239 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -19,6 +19,7 @@ package android.service.wallpaper; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.app.Service; import android.app.WallpaperColors; @@ -56,6 +57,7 @@ import android.view.SurfaceHolder; import android.view.View; import android.view.ViewGroup; import android.view.WindowInsets; +import android.view.InsetsState; import android.view.WindowManager; import android.view.WindowManagerGlobal; @@ -184,6 +186,7 @@ public abstract class WallpaperService extends Service { final DisplayCutout.ParcelableWrapper mDisplayCutout = new DisplayCutout.ParcelableWrapper(); DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT; + final InsetsState mInsetsState = new InsetsState(); final MergedConfiguration mMergedConfiguration = new MergedConfiguration(); final WindowManager.LayoutParams mLayout @@ -440,7 +443,9 @@ public abstract class WallpaperService extends Service { /** * Returns true if this engine is running in ambient mode -- that is, * it is being shown in low power mode, on always on display. + * @hide */ + @SystemApi public boolean isInAmbientMode() { return mIsInAmbientMode; } @@ -566,14 +571,16 @@ public abstract class WallpaperService extends Service { * Called when the device enters or exits ambient mode. * * @param inAmbientMode {@code true} if in ambient mode. - * @param animated {@code true} if you'll have the opportunity of animating your transition - * {@code false} when the wallpaper should present its ambient version - * immediately. + * @param animationDuration How long the transition animation to change the ambient state + * should run, in milliseconds. If 0 is passed as the argument + * here, the state should be switched immediately. * * @see #isInAmbientMode() * @see WallpaperInfo#supportsAmbientMode() + * @hide */ - public void onAmbientModeChanged(boolean inAmbientMode, boolean animated) { + @SystemApi + public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) { } /** @@ -803,9 +810,11 @@ public abstract class WallpaperService extends Service { mLayout.windowAnimations = com.android.internal.R.style.Animation_Wallpaper; mInputChannel = new InputChannel(); + if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE, mDisplay.getDisplayId(), mWinFrame, mContentInsets, mStableInsets, - mOutsets, mDisplayCutout, mInputChannel) < 0) { + mOutsets, mDisplayCutout, mInputChannel, + mInsetsState) < 0) { Log.w(TAG, "Failed to add window while updating wallpaper surface."); return; } @@ -831,7 +840,8 @@ public abstract class WallpaperService extends Service { mWindow, mWindow.mSeq, mLayout, mWidth, mHeight, View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets, mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame, - mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface); + mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface, + mInsetsState); if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface + ", frame=" + mWinFrame); @@ -1044,19 +1054,19 @@ public abstract class WallpaperService extends Service { * message sent from handler. * * @param inAmbientMode {@code true} if in ambient mode. - * @param animated {@code true} if the transition will be animated. + * @param animationDuration For how long the transition will last, in ms. * @hide */ @VisibleForTesting - public void doAmbientModeChanged(boolean inAmbientMode, boolean animated) { + public void doAmbientModeChanged(boolean inAmbientMode, long animationDuration) { if (!mDestroyed) { if (DEBUG) { Log.v(TAG, "onAmbientModeChanged(" + inAmbientMode + ", " - + animated + "): " + this); + + animationDuration + "): " + this); } mIsInAmbientMode = inAmbientMode; if (mCreated) { - onAmbientModeChanged(inAmbientMode, animated); + onAmbientModeChanged(inAmbientMode, animationDuration); } } } @@ -1315,10 +1325,10 @@ public abstract class WallpaperService extends Service { } @Override - public void setInAmbientMode(boolean inAmbientDisplay, boolean animated) + public void setInAmbientMode(boolean inAmbientDisplay, long animationDuration) throws RemoteException { - Message msg = mCaller.obtainMessageII(DO_IN_AMBIENT_MODE, inAmbientDisplay ? 1 : 0, - animated ? 1 : 0); + Message msg = mCaller.obtainMessageIO(DO_IN_AMBIENT_MODE, inAmbientDisplay ? 1 : 0, + animationDuration); mCaller.sendMessage(msg); } @@ -1389,7 +1399,7 @@ public abstract class WallpaperService extends Service { return; } case DO_IN_AMBIENT_MODE: { - mEngine.doAmbientModeChanged(message.arg1 != 0, message.arg2 != 0); + mEngine.doAmbientModeChanged(message.arg1 != 0, (Long) message.obj); return; } case MSG_UPDATE_SURFACE: diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java index c8228326b8e4..de182daaeb53 100644 --- a/core/java/android/transition/ChangeBounds.java +++ b/core/java/android/transition/ChangeBounds.java @@ -32,6 +32,7 @@ import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.os.Build; import android.util.AttributeSet; import android.util.Property; import android.view.View; @@ -109,7 +110,7 @@ public class ChangeBounds extends Transition { } }; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private static final Property<View, PointF> BOTTOM_RIGHT_ONLY_PROPERTY = new Property<View, PointF>(PointF.class, "bottomRight") { @Override @@ -144,7 +145,7 @@ public class ChangeBounds extends Transition { } }; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private static final Property<View, PointF> POSITION_PROPERTY = new Property<View, PointF>(PointF.class, "position") { @Override diff --git a/core/java/android/transition/Scene.java b/core/java/android/transition/Scene.java index 7e499f29122a..b1fc17a4ecd1 100644 --- a/core/java/android/transition/Scene.java +++ b/core/java/android/transition/Scene.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.content.Context; +import android.os.Build; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; @@ -38,9 +39,9 @@ public final class Scene { private int mLayoutId = -1; private ViewGroup mSceneRoot; private View mLayout; // alternative to layoutId - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) Runnable mEnterAction; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) Runnable mExitAction; /** @@ -200,7 +201,7 @@ public final class Scene { * * @param sceneRoot The view on which the current scene is being set */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) static void setCurrentScene(@NonNull View sceneRoot, @Nullable Scene scene) { sceneRoot.setTagInternal(com.android.internal.R.id.current_scene, scene); } diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index 4b8b7f304b0f..af41b6942a5e 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -24,6 +24,7 @@ import android.view.DragEvent; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.DisplayCutout; +import android.view.InsetsState; import com.android.internal.os.IResultReceiver; import android.util.MergedConfiguration; @@ -53,6 +54,12 @@ oneway interface IWindow { in MergedConfiguration newMergedConfiguration, in Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId, in DisplayCutout.ParcelableWrapper displayCutout); + + /** + * Called when the window insets configuration has changed. + */ + void insetsChanged(in InsetsState insetsState); + void moved(int newX, int newY); void dispatchAppVisibility(boolean visible); void dispatchGetNewSurface(); diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index bedfa9ff133c..97625869209d 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -28,6 +28,7 @@ import android.view.IWindow; import android.view.IWindowId; import android.view.MotionEvent; import android.view.WindowManager; +import android.view.InsetsState; import android.view.Surface; import android.view.SurfaceControl; @@ -40,10 +41,11 @@ interface IWindowSession { int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs, in int viewVisibility, in int layerStackId, out Rect outFrame, out Rect outContentInsets, out Rect outStableInsets, out Rect outOutsets, - out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel); + out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel, + out InsetsState insetsState); int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs, in int viewVisibility, in int layerStackId, out Rect outContentInsets, - out Rect outStableInsets); + out Rect outStableInsets, out InsetsState insetsState); void remove(IWindow window); /** @@ -86,6 +88,7 @@ interface IWindowSession { * config for window, if it is now becoming visible and the merged configuration has changed * since it was last displayed. * @param outSurface Object in which is placed the new display surface. + * @param insetsState The current insets state in the system. * * @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_SHOW_FOCUS}, * {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}. @@ -96,7 +99,8 @@ interface IWindowSession { out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets, out Rect outOutsets, out Rect outBackdropFrame, out DisplayCutout.ParcelableWrapper displayCutout, - out MergedConfiguration outMergedConfiguration, out Surface outSurface); + out MergedConfiguration outMergedConfiguration, out Surface outSurface, + out InsetsState insetsState); /* * Notify the window manager that an application is relaunching and diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java new file mode 100644 index 000000000000..7841d0417a2b --- /dev/null +++ b/core/java/android/view/InsetsController.java @@ -0,0 +1,55 @@ +/* + * 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; + +import android.graphics.Rect; + +import java.io.PrintWriter; + +/** + * Implements {@link WindowInsetsController} on the client. + */ +class InsetsController { + + private final InsetsState mState = new InsetsState(); + private final Rect mFrame = new Rect(); + + void onFrameChanged(Rect frame) { + mFrame.set(frame); + } + + public InsetsState getState() { + return mState; + } + + public void setState(InsetsState state) { + mState.set(state); + } + + /** + * @see InsetsState#calculateInsets + */ + WindowInsets calculateInsets(boolean isScreenRound, + boolean alwaysConsumeNavBar, DisplayCutout cutout) { + return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout); + } + + void dump(String prefix, PrintWriter pw) { + pw.println(prefix); pw.println("InsetsController:"); + mState.dump(prefix + " ", pw); + } +} diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java new file mode 100644 index 000000000000..0cb8ad72f102 --- /dev/null +++ b/core/java/android/view/InsetsSource.java @@ -0,0 +1,159 @@ +/* + * 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; + +import android.graphics.Insets; +import android.graphics.Rect; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.InsetsState.InternalInsetType; + +import java.io.PrintWriter; + +/** + * Represents the state of a single window generating insets for clients. + * @hide + */ +public class InsetsSource implements Parcelable { + + private final @InternalInsetType int mType; + + /** Frame of the source in screen coordinate space */ + private final Rect mFrame; + private boolean mVisible; + + private final Rect mTmpFrame = new Rect(); + + public InsetsSource(@InternalInsetType int type) { + mType = type; + mFrame = new Rect(); + } + + public InsetsSource(InsetsSource other) { + mType = other.mType; + mFrame = new Rect(other.mFrame); + mVisible = other.mVisible; + } + + public void setFrame(Rect frame) { + mFrame.set(frame); + } + + public void setVisible(boolean visible) { + mVisible = visible; + } + + public @InternalInsetType int getType() { + return mType; + } + + public Rect getFrame() { + return mFrame; + } + + /** + * Calculates the insets this source will cause to a client window. + * + * @param relativeFrame The frame to calculate the insets relative to. + * @param ignoreVisibility If true, always reports back insets even if source isn't visible. + * @return The resulting insets. + */ + public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) { + if (!ignoreVisibility && !mVisible) { + return Insets.NONE; + } + if (!mTmpFrame.setIntersect(mFrame, relativeFrame)) { + return Insets.NONE; + } + + // Intersecting at top/bottom + if (mTmpFrame.width() == relativeFrame.width()) { + if (mTmpFrame.top == relativeFrame.top) { + return Insets.of(0, mTmpFrame.height(), 0, 0); + } else { + return Insets.of(0, 0, 0, mTmpFrame.height()); + } + } + // Intersecting at left/right + else if (mTmpFrame.height() == relativeFrame.height()) { + if (mTmpFrame.left == relativeFrame.left) { + return Insets.of(mTmpFrame.width(), 0, 0, 0); + } else { + return Insets.of(0, 0, mTmpFrame.width(), 0); + } + } else { + return Insets.NONE; + } + } + + public void dump(String prefix, PrintWriter pw) { + pw.print(prefix); + pw.print("InsetsSource type="); pw.print(InsetsState.typeToString(mType)); + pw.print(" frame="); pw.print(mFrame.toShortString()); + pw.print(" visible="); pw.print(mVisible); + pw.println(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + InsetsSource that = (InsetsSource) o; + + if (mType != that.mType) return false; + if (mVisible != that.mVisible) return false; + return mFrame.equals(that.mFrame); + } + + @Override + public int hashCode() { + int result = mType; + result = 31 * result + mFrame.hashCode(); + result = 31 * result + (mVisible ? 1 : 0); + return result; + } + + public InsetsSource(Parcel in) { + mType = in.readInt(); + mFrame = in.readParcelable(null /* loader */); + mVisible = in.readBoolean(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mType); + dest.writeParcelable(mFrame, 0 /* flags*/); + dest.writeBoolean(mVisible); + } + + public static final Creator<InsetsSource> CREATOR = new Creator<InsetsSource>() { + + public InsetsSource createFromParcel(Parcel in) { + return new InsetsSource(in); + } + + public InsetsSource[] newArray(int size) { + return new InsetsSource[size]; + } + }; +} diff --git a/core/java/android/view/InsetsState.aidl b/core/java/android/view/InsetsState.aidl new file mode 100644 index 000000000000..d02ddd15a8c9 --- /dev/null +++ b/core/java/android/view/InsetsState.aidl @@ -0,0 +1,19 @@ +/** + * 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 android.view; + +parcelable InsetsState; diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java new file mode 100644 index 000000000000..9895adcad23a --- /dev/null +++ b/core/java/android/view/InsetsState.java @@ -0,0 +1,240 @@ +/* + * 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; + +import android.annotation.IntDef; +import android.graphics.Insets; +import android.graphics.Rect; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArrayMap; + +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Holder for state of system windows that cause window insets for all other windows in the system. + * @hide + */ +public class InsetsState implements Parcelable { + + /** + * Internal representation of inset source types. This is different from the public API in + * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows + * at the same time. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "TYPE", value = { + TYPE_TOP_BAR, + TYPE_SIDE_BAR_1, + TYPE_SIDE_BAR_2, + TYPE_SIDE_BAR_3, + TYPE_IME + }) + public @interface InternalInsetType {} + + static final int FIRST_TYPE = 0; + + /** Top bar. Can be status bar or caption in freeform windowing mode. */ + public static final int TYPE_TOP_BAR = FIRST_TYPE; + + /** + * Up to 3 side bars that appear on left/right/bottom. On phones there is only one side bar + * (the navigation bar, see {@link #TYPE_NAVIGATION_BAR}), but other form factors might have + * multiple, like Android Auto. + */ + public static final int TYPE_SIDE_BAR_1 = 1; + public static final int TYPE_SIDE_BAR_2 = 2; + public static final int TYPE_SIDE_BAR_3 = 3; + + /** Input method window. */ + public static final int TYPE_IME = 4; + static final int LAST_TYPE = TYPE_IME; + + // Derived types + + /** First side bar is navigation bar. */ + public static final int TYPE_NAVIGATION_BAR = TYPE_SIDE_BAR_1; + + /** A shelf is the same as the navigation bar. */ + public static final int TYPE_SHELF = TYPE_NAVIGATION_BAR; + + private final ArrayMap<Integer, InsetsSource> mSources = new ArrayMap<>(); + + public InsetsState() { + } + + /** + * Calculates {@link WindowInsets} based on the current source configuration. + * + * @param frame The frame to calculate the insets relative to. + * @return The calculated insets. + */ + public WindowInsets calculateInsets(Rect frame, boolean isScreenRound, + boolean alwaysConsumeNavBar, DisplayCutout cutout) { + Insets systemInsets = Insets.NONE; + Insets maxInsets = Insets.NONE; + final Rect relativeFrame = new Rect(frame); + final Rect relativeFrameMax = new Rect(frame); + for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { + InsetsSource source = mSources.get(type); + if (source == null) { + continue; + } + systemInsets = processSource(source, systemInsets, relativeFrame, + false /* ignoreVisibility */); + + // IME won't be reported in max insets as the size depends on the EditorInfo of the IME + // target. + if (source.getType() != TYPE_IME) { + maxInsets = processSource(source, maxInsets, relativeFrameMax, + true /* ignoreVisibility */); + } + } + return new WindowInsets(new Rect(systemInsets), null, new Rect(maxInsets), isScreenRound, + alwaysConsumeNavBar, cutout); + } + + private Insets processSource(InsetsSource source, Insets insets, Rect relativeFrame, + boolean ignoreVisibility) { + Insets currentInsets = source.calculateInsets(relativeFrame, ignoreVisibility); + insets = Insets.add(currentInsets, insets); + relativeFrame.inset(insets); + return insets; + } + + public InsetsSource getSource(@InternalInsetType int type) { + return mSources.computeIfAbsent(type, InsetsSource::new); + } + + /** + * Modifies the state of this class to exclude a certain type to make it ready for dispatching + * to the client. + * + * @param type The {@link InternalInsetType} of the source to remove + */ + public void removeSource(int type) { + mSources.remove(type); + } + + public void set(InsetsState other) { + set(other, false /* copySources */); + } + + public void set(InsetsState other, boolean copySources) { + mSources.clear(); + if (copySources) { + for (int i = 0; i < other.mSources.size(); i++) { + InsetsSource source = other.mSources.valueAt(i); + mSources.put(source.getType(), new InsetsSource(source)); + } + } else { + mSources.putAll(other.mSources); + } + } + + public void dump(String prefix, PrintWriter pw) { + pw.println(prefix + "InsetsState"); + for (int i = mSources.size() - 1; i >= 0; i--) { + mSources.valueAt(i).dump(prefix + " ", pw); + } + } + + static String typeToString(int type) { + switch (type) { + case TYPE_TOP_BAR: + return "TYPE_TOP_BAR"; + case TYPE_SIDE_BAR_1: + return "TYPE_SIDE_BAR_1"; + case TYPE_SIDE_BAR_2: + return "TYPE_SIDE_BAR_2"; + case TYPE_SIDE_BAR_3: + return "TYPE_SIDE_BAR_3"; + case TYPE_IME: + return "TYPE_IME"; + default: + return "TYPE_UNKNOWN"; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + + InsetsState state = (InsetsState) o; + + if (mSources.size() != state.mSources.size()) { + return false; + } + for (int i = mSources.size() - 1; i >= 0; i--) { + InsetsSource source = mSources.valueAt(i); + InsetsSource otherSource = state.mSources.get(source.getType()); + if (otherSource == null) { + return false; + } + if (!otherSource.equals(source)) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + return mSources.hashCode(); + } + + public InsetsState(Parcel in) { + readFromParcel(in); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mSources.size()); + for (int i = 0; i < mSources.size(); i++) { + dest.writeParcelable(mSources.valueAt(i), 0 /* flags */); + } + } + + public static final Creator<InsetsState> CREATOR = new Creator<InsetsState>() { + + public InsetsState createFromParcel(Parcel in) { + return new InsetsState(in); + } + + public InsetsState[] newArray(int size) { + return new InsetsState[size]; + } + }; + + public void readFromParcel(Parcel in) { + mSources.clear(); + final int size = in.readInt(); + for (int i = 0; i < size; i++) { + final InsetsSource source = in.readParcelable(null /* loader */); + mSources.put(source.getType(), source); + } + } +} + diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 0739516e4e96..8b39cc72af8f 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -1862,12 +1862,12 @@ public class KeyEvent extends InputEvent implements Parcelable { } /** - * Whether this key is a media key, which can be send to apps that are - * interested in media key events. + * Returns whether this key can be handled by + * {@link android.media.session.MediaSession.Callback}. * * @hide */ - public static final boolean isMediaKey(int keyCode) { + public static final boolean isMediaSessionKey(int keyCode) { switch (keyCode) { case KeyEvent.KEYCODE_MEDIA_PLAY: case KeyEvent.KEYCODE_MEDIA_PAUSE: diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index a7a5024cd2a6..46f396a0b66b 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -375,9 +375,13 @@ public class SurfaceControl implements Parcelable { * Construct a new {@link SurfaceControl} with the set parameters. */ public SurfaceControl build() { - if (mWidth <= 0 || mHeight <= 0) { + if (mWidth < 0 || mHeight < 0) { throw new IllegalArgumentException( - "width and height must be set"); + "width and height must be positive or unset"); + } + if ((mWidth > 0 || mHeight > 0) && (isColorLayerSet() || isContainerLayerSet())) { + throw new IllegalArgumentException( + "Only buffer layers can set a valid buffer size."); } return new SurfaceControl(mSession, mName, mWidth, mHeight, mFormat, mFlags, mParent, mWindowType, mOwnerUid); @@ -399,8 +403,8 @@ public class SurfaceControl implements Parcelable { * @param width The buffer width in pixels. * @param height The buffer height in pixels. */ - public Builder setSize(int width, int height) { - if (width <= 0 || height <= 0) { + public Builder setBufferSize(int width, int height) { + if (width < 0 || height < 0) { throw new IllegalArgumentException( "width and height must be positive"); } @@ -533,6 +537,10 @@ public class SurfaceControl implements Parcelable { return this; } + private boolean isColorLayerSet() { + return (mFlags & FX_SURFACE_DIM) == FX_SURFACE_DIM; + } + /** * Indicates whether a 'ContainerLayer' is to be constructed. * @@ -550,6 +558,10 @@ public class SurfaceControl implements Parcelable { return this; } + private boolean isContainerLayerSet() { + return (mFlags & FX_SURFACE_CONTAINER) == FX_SURFACE_CONTAINER; + } + /** * Set 'Surface creation flags' such as {@link HIDDEN}, {@link SECURE}. * @@ -869,10 +881,10 @@ public class SurfaceControl implements Parcelable { } } - public void setSize(int w, int h) { + public void setBufferSize(int w, int h) { checkNotReleased(); synchronized(SurfaceControl.class) { - sGlobalTransaction.setSize(this, w, h); + sGlobalTransaction.setBufferSize(this, w, h); } } @@ -1427,7 +1439,7 @@ public class SurfaceControl implements Parcelable { } @UnsupportedAppUsage - public Transaction setSize(SurfaceControl sc, int w, int h) { + public Transaction setBufferSize(SurfaceControl sc, int w, int h) { sc.checkNotReleased(); mResizedSurfaces.put(sc, new Point(w, h)); nativeSetSize(mNativeObject, sc.mNativeObject, w, h); diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 2b68ec0edcbe..3c4ce8f7cfad 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -557,7 +557,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb name, (mSurfaceFlags & SurfaceControl.OPAQUE) != 0, new SurfaceControl.Builder(mSurfaceSession) - .setSize(mSurfaceWidth, mSurfaceHeight) + .setBufferSize(mSurfaceWidth, mSurfaceHeight) .setFormat(mFormat) .setFlags(mSurfaceFlags)); } else if (mSurfaceControl == null) { @@ -595,10 +595,14 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth, 0.0f, 0.0f, mScreenRect.height() / (float) mSurfaceHeight); + // Set a window crop when creating the surface or changing its size to + // crop the buffer to the surface size since the buffer producer may + // use SCALING_MODE_SCALE and submit a larger size than the surface + // size. + mSurfaceControl.setWindowCrop(mSurfaceWidth, mSurfaceHeight); } if (sizeChanged && !creating) { - mSurfaceControl.setSize(mSurfaceWidth, mSurfaceHeight); - mSurfaceControl.setWindowCrop(mSurfaceWidth, mSurfaceHeight); + mSurfaceControl.setBufferSize(mSurfaceWidth, mSurfaceHeight); } } finally { SurfaceControl.closeTransaction(); @@ -1133,6 +1137,8 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb mBackgroundControl = b.setName("Background for -" + name) .setFormat(OPAQUE) + // Unset the buffer size of the background color layer. + .setBufferSize(0, 0) .setColorLayer(true) .build(); mOpaque = opaque; @@ -1158,9 +1164,9 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb } @Override - public void setSize(int w, int h) { - super.setSize(w, h); - mBackgroundControl.setSize(w, h); + public void setBufferSize(int w, int h) { + super.setBufferSize(w, h); + // The background surface is a color layer so we do not set a size. } @Override diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0eaef5aa3c53..2767505aece3 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -21525,10 +21525,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Same as setFrame, but public and hidden. For use in {@link android.transition.ChangeBounds}. - * @hide + * Assign a size and position to this view. + * + * This method is meant to be used in animations only as it applies this position and size + * for the view only temporary and it can be changed back at any time by the layout. + * + * @param left Left position, relative to parent + * @param top Top position, relative to parent + * @param right Right position, relative to parent + * @param bottom Bottom position, relative to parent + * + * @see #setLeft(int), #setRight(int), #setTop(int), #setBottom(int) */ - @UnsupportedAppUsage public void setLeftTopRightBottom(int left, int top, int right, int bottom) { setFrame(left, top, right, bottom); } @@ -24741,7 +24749,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final SurfaceSession session = new SurfaceSession(root.mSurface); final SurfaceControl surfaceControl = new SurfaceControl.Builder(session) .setName("drag surface") - .setSize(shadowSize.x, shadowSize.y) + .setBufferSize(shadowSize.x, shadowSize.y) .setFormat(PixelFormat.TRANSLUCENT) .build(); final Surface surface = new Surface(); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 484c6f38e962..937e23813cec 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -161,6 +161,19 @@ public final class ViewRootImpl implements ViewParent, private static final boolean MT_RENDERER_AVAILABLE = true; /** + * If set to true, the view system will switch from using rectangles retrieved from window to + * dispatch to the view hierarchy to using {@link InsetsController}, that derives the insets + * directly from the full configuration, enabling richer information about the insets state, as + * well as new APIs to control it frame-by-frame, and synchronize animations with it. + * <p> + * Only switch this to true once the new insets system is productionized and the old APIs are + * fully migrated over. + */ + private static final String USE_NEW_INSETS_PROPERTY = "persist.wm.new_insets"; + private static final boolean USE_NEW_INSETS = + SystemProperties.getBoolean(USE_NEW_INSETS_PROPERTY, false); + + /** * Set this system property to true to force the view hierarchy to render * at 60 Hz. This can be used to measure the potential framerate. */ @@ -432,6 +445,8 @@ public final class ViewRootImpl implements ViewParent, boolean mAdded; boolean mAddedTouchMode; + final Rect mTmpFrame = new Rect(); + // These are accessed by multiple threads. final Rect mWinFrame; // frame given by window manager. @@ -444,6 +459,7 @@ public final class ViewRootImpl implements ViewParent, final DisplayCutout.ParcelableWrapper mPendingDisplayCutout = new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT); boolean mPendingAlwaysConsumeNavBar; + private InsetsState mPendingInsets = new InsetsState(); final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets = new ViewTreeObserver.InternalInsetsInfo(); @@ -531,6 +547,8 @@ public final class ViewRootImpl implements ViewParent, InputEventConsistencyVerifier.isInstrumentationEnabled() ? new InputEventConsistencyVerifier(this, 0) : null; + private final InsetsController mInsetsController = new InsetsController(); + static final class SystemUiVisibilityInfo { int seq; int globalVisibility; @@ -797,9 +815,11 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, - getHostVisibility(), mDisplay.getDisplayId(), mWinFrame, + getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, - mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); + mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel, + mInsetsController.getState()); + setFrame(mTmpFrame); } catch (RemoteException e) { mAdded = false; mView = null; @@ -826,6 +846,7 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mAlwaysConsumeNavBar = (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0; mPendingAlwaysConsumeNavBar = mAttachInfo.mAlwaysConsumeNavBar; + mPendingInsets = mInsetsController.getState(); if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow); if (res < WindowManagerGlobal.ADD_OKAY) { mAttachInfo.mRootView = null; @@ -1473,31 +1494,22 @@ public final class ViewRootImpl implements ViewParent, mBoundsSurfaceControl = new SurfaceControl.Builder(mSurfaceSession) .setName("Bounds for - " + getTitle().toString()) - .setSize(mWidth, mHeight) .build(); - setBoundsSurfaceSizeAndCrop(); + setBoundsSurfaceCrop(); mTransaction.setLayer(mBoundsSurfaceControl, zOrderLayer) .show(mBoundsSurfaceControl) .apply(); mBoundsSurface.copyFrom(mBoundsSurfaceControl); } - private void setBoundsSurfaceSizeAndCrop() { + private void setBoundsSurfaceCrop() { // mWinFrame is already adjusted for surface insets. So offset it and use it as // the cropping bounds. mTempBoundsRect.set(mWinFrame); mTempBoundsRect.offsetTo(mWindowAttributes.surfaceInsets.left, mWindowAttributes.surfaceInsets.top); mTransaction.setWindowCrop(mBoundsSurfaceControl, mTempBoundsRect); - - // Expand the bounds by the surface insets to get the size of surface. - mTempBoundsRect.inset(-mWindowAttributes.surfaceInsets.left, - -mWindowAttributes.surfaceInsets.top, - -mWindowAttributes.surfaceInsets.right, - -mWindowAttributes.surfaceInsets.bottom); - mTransaction.setSize(mBoundsSurfaceControl, mTempBoundsRect.width(), - mTempBoundsRect.height()); } /** @@ -1506,7 +1518,7 @@ public final class ViewRootImpl implements ViewParent, */ private void updateBoundsSurface() { if (mBoundsSurfaceControl != null && mSurface.isValid()) { - setBoundsSurfaceSizeAndCrop(); + setBoundsSurfaceCrop(); mTransaction.deferTransactionUntilSurface(mBoundsSurfaceControl, mSurface, mSurface.getNextFrameNumber()) .apply(); @@ -1780,7 +1792,8 @@ public final class ViewRootImpl implements ViewParent, Rect stableInsets = mDispatchStableInsets; DisplayCutout displayCutout = mDispatchDisplayCutout; // For dispatch we preserve old logic, but for direct requests from Views we allow to - // immediately use pending insets. + // immediately use pending insets. This is such that getRootWindowInsets returns the + // result from the layout hint before we ran a traversal shortly after adding a window. if (!forceConstruct && (!mPendingContentInsets.equals(contentInsets) || !mPendingStableInsets.equals(stableInsets) || @@ -1797,10 +1810,16 @@ public final class ViewRootImpl implements ViewParent, } contentInsets = ensureInsetsNonNegative(contentInsets, "content"); stableInsets = ensureInsetsNonNegative(stableInsets, "stable"); - mLastWindowInsets = new WindowInsets(contentInsets, - null /* windowDecorInsets */, stableInsets, - mContext.getResources().getConfiguration().isScreenRound(), - mAttachInfo.mAlwaysConsumeNavBar, displayCutout); + if (USE_NEW_INSETS) { + mLastWindowInsets = mInsetsController.calculateInsets( + mContext.getResources().getConfiguration().isScreenRound(), + mAttachInfo.mAlwaysConsumeNavBar, displayCutout); + } else { + mLastWindowInsets = new WindowInsets(contentInsets, + null /* windowDecorInsets */, stableInsets, + mContext.getResources().getConfiguration().isScreenRound(), + mAttachInfo.mAlwaysConsumeNavBar, displayCutout); + } } return mLastWindowInsets; } @@ -2000,6 +2019,9 @@ public final class ViewRootImpl implements ViewParent, if (mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar) { insetsChanged = true; } + if (!mPendingInsets.equals(mInsetsController.getState())) { + insetsChanged = true; + } if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { windowSizeMayChange = true; @@ -2193,6 +2215,8 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mStableInsets); final boolean cutoutChanged = !mPendingDisplayCutout.equals( mAttachInfo.mDisplayCutout); + final boolean insetsStateChanged = !mPendingInsets.equals( + mInsetsController.getState()); final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets); final boolean surfaceSizeChanged = (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0; @@ -2230,6 +2254,10 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mAlwaysConsumeNavBar = mPendingAlwaysConsumeNavBar; contentInsetsChanged = true; } + if (insetsStateChanged) { + mInsetsController.setState(mPendingInsets); + contentInsetsChanged = true; + } if (contentInsetsChanged || mLastSystemUiVisibility != mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested || mLastOverscanRequested != mAttachInfo.mOverscanRequested @@ -2675,7 +2703,6 @@ public final class ViewRootImpl implements ViewParent, } private void maybeHandleWindowMove(Rect frame) { - // TODO: Well, we are checking whether the frame has changed similarly // to how this is done for the insets. This is however incorrect since // the insets and the frame are translated. For example, the old frame @@ -4180,6 +4207,7 @@ public final class ViewRootImpl implements ViewParent, private final static int MSG_UPDATE_POINTER_ICON = 27; private final static int MSG_POINTER_CAPTURE_CHANGED = 28; private final static int MSG_DRAW_FINISHED = 29; + private final static int MSG_INSETS_CHANGED = 30; final class ViewRootHandler extends Handler { @Override @@ -4235,6 +4263,8 @@ public final class ViewRootImpl implements ViewParent, return "MSG_POINTER_CAPTURE_CHANGED"; case MSG_DRAW_FINISHED: return "MSG_DRAW_FINISHED"; + case MSG_INSETS_CHANGED: + return "MSG_INSETS_CHANGED"; } return super.getMessageName(message); } @@ -4315,7 +4345,7 @@ public final class ViewRootImpl implements ViewParent, || !mPendingVisibleInsets.equals(args.arg3) || !mPendingOutsets.equals(args.arg7); - mWinFrame.set((Rect) args.arg1); + setFrame((Rect) args.arg1); mPendingOverscanInsets.set((Rect) args.arg5); mPendingContentInsets.set((Rect) args.arg2); mPendingStableInsets.set((Rect) args.arg6); @@ -4338,16 +4368,25 @@ public final class ViewRootImpl implements ViewParent, requestLayout(); } break; + case MSG_INSETS_CHANGED: + mPendingInsets = (InsetsState) msg.obj; + + // TODO: Full traversal not needed here + if (USE_NEW_INSETS) { + requestLayout(); + } + break; case MSG_WINDOW_MOVED: if (mAdded) { final int w = mWinFrame.width(); final int h = mWinFrame.height(); final int l = msg.arg1; final int t = msg.arg2; - mWinFrame.left = l; - mWinFrame.right = l + w; - mWinFrame.top = t; - mWinFrame.bottom = t + h; + mTmpFrame.left = l; + mTmpFrame.right = l + w; + mTmpFrame.top = t; + mTmpFrame.bottom = t + h; + setFrame(mTmpFrame); mPendingBackDropFrame.set(mWinFrame); maybeHandleWindowMove(mWinFrame); @@ -6733,9 +6772,9 @@ public final class ViewRootImpl implements ViewParent, (int) (mView.getMeasuredWidth() * appScale + 0.5f), (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber, - mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets, + mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets, mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout, - mPendingMergedConfiguration, mSurface); + mPendingMergedConfiguration, mSurface, mPendingInsets); mPendingAlwaysConsumeNavBar = (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0; @@ -6745,15 +6784,22 @@ public final class ViewRootImpl implements ViewParent, } if (mTranslator != null) { - mTranslator.translateRectInScreenToAppWinFrame(mWinFrame); + mTranslator.translateRectInScreenToAppWinFrame(mTmpFrame); mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets); mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets); mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets); mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets); } + setFrame(mTmpFrame); + return relayoutResult; } + private void setFrame(Rect frame) { + mWinFrame.set(frame); + mInsetsController.onFrameChanged(frame); + } + /** * {@inheritDoc} */ @@ -6856,6 +6902,8 @@ public final class ViewRootImpl implements ViewParent, mChoreographer.dump(prefix, writer); + mInsetsController.dump(prefix, writer); + writer.print(prefix); writer.println("View Hierarchy:"); dumpViewHierarchy(innerPrefix, writer, mView); } @@ -7064,6 +7112,10 @@ public final class ViewRootImpl implements ViewParent, mHandler.sendMessage(msg); } + private void dispatchInsetsChanged(InsetsState insetsState) { + mHandler.obtainMessage(MSG_INSETS_CHANGED, insetsState).sendToTarget(); + } + public void dispatchMoved(int newX, int newY) { if (DEBUG_LAYOUT) Log.v(mTag, "Window moved " + this + ": newX=" + newX + " newY=" + newY); if (mTranslator != null) { @@ -8127,6 +8179,14 @@ public final class ViewRootImpl implements ViewParent, } @Override + public void insetsChanged(InsetsState insetsState) { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchInsetsChanged(insetsState); + } + } + + @Override public void moved(int newX, int newY) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java index f4c25c3831be..7d027574198d 100644 --- a/core/java/android/widget/Magnifier.java +++ b/core/java/android/widget/Magnifier.java @@ -837,7 +837,7 @@ public final class Magnifier { mSurfaceSession = new SurfaceSession(parentSurface); mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession) .setFormat(PixelFormat.TRANSLUCENT) - .setSize(mSurfaceWidth, mSurfaceHeight) + .setBufferSize(mSurfaceWidth, mSurfaceHeight) .setName("magnifier surface") .setFlags(SurfaceControl.HIDDEN) .build(); diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index c0979fe13de4..7b39efed0c3a 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -161,6 +161,7 @@ public class RemoteViews implements Parcelable, Filter { private static final int LAYOUT_PARAM_ACTION_TAG = 19; private static final int OVERRIDE_TEXT_COLORS_TAG = 20; private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21; + private static final int SET_INT_TAG_TAG = 22; /** * Application that hosts the remote views. @@ -274,6 +275,15 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Sets an integer tag to the view. + * + * @hide + */ + public void setIntTag(int viewId, int key, int tag) { + addAction(new SetIntTagAction(viewId, key, tag)); + } + + /** * Set that it is disallowed to reapply another remoteview with the same layout as this view. * This should be done if an action is destroying the view tree of the base layout. * @@ -2122,6 +2132,43 @@ public class RemoteViews implements Parcelable, Filter { } } + private class SetIntTagAction extends Action { + private final int mViewId; + private final int mKey; + private final int mTag; + + SetIntTagAction(int viewId, int key, int tag) { + mViewId = viewId; + mKey = key; + mTag = tag; + } + + SetIntTagAction(Parcel parcel) { + mViewId = parcel.readInt(); + mKey = parcel.readInt(); + mTag = parcel.readInt(); + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mViewId); + dest.writeInt(mKey); + dest.writeInt(mTag); + } + + @Override + public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + final View target = root.findViewById(mViewId); + if (target == null) return; + + target.setTagInternal(mKey, mTag); + } + + @Override + public int getActionTag() { + return SET_INT_TAG_TAG; + } + } + /** * Create a new RemoteViews object that will display the views contained * in the specified layout file. @@ -2326,6 +2373,8 @@ public class RemoteViews implements Parcelable, Filter { return new OverrideTextColorsAction(parcel); case SET_RIPPLE_DRAWABLE_COLOR_TAG: return new SetRippleDrawableColor(parcel); + case SET_INT_TAG_TAG: + return new SetIntTagAction(parcel); default: throw new ActionException("Tag " + tag + " not found"); } diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java index b9d53c1b5884..d78bfac1f878 100644 --- a/core/java/com/android/internal/os/SomeArgs.java +++ b/core/java/com/android/internal/os/SomeArgs.java @@ -120,6 +120,8 @@ public final class SomeArgs { arg5 = null; arg6 = null; arg7 = null; + arg8 = null; + arg9 = null; argi1 = 0; argi2 = 0; argi3 = 0; diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 3b7ce0a22b18..031377ebe27b 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -1891,7 +1891,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return true; } // These are all the recognized media key codes in - // KeyEvent.isMediaKey() + // KeyEvent.isMediaSessionKey() case KeyEvent.KEYCODE_MEDIA_PLAY: case KeyEvent.KEYCODE_MEDIA_PAUSE: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: @@ -1992,7 +1992,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return true; } // These are all the recognized media key codes in - // KeyEvent.isMediaKey() + // KeyEvent.isMediaSessionKey() case KeyEvent.KEYCODE_MEDIA_PLAY: case KeyEvent.KEYCODE_MEDIA_PAUSE: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 69ba07090284..b7ffb5768a31 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -16,6 +16,7 @@ package com.android.internal.statusbar; +import android.app.Notification; import android.content.ComponentName; import android.graphics.Rect; import android.os.Bundle; @@ -55,7 +56,7 @@ interface IStatusBarService // Mark current notifications as "seen" and stop ringing, vibrating, blinking. void clearNotificationEffects(); void onNotificationClick(String key, in NotificationVisibility nv); - void onNotificationActionClick(String key, int actionIndex, in NotificationVisibility nv); + void onNotificationActionClick(String key, int actionIndex, in Notification.Action action, in NotificationVisibility nv, boolean generatedByAssistant); void onNotificationError(String pkg, String tag, int id, int uid, int initialPid, String message, int userId); void onClearAllNotifications(int userId); diff --git a/services/core/java/com/android/server/wm/RootWindowContainerListener.java b/core/java/com/android/internal/util/function/NonaConsumer.java index f413e3f7c2ea..3e7ce2b405a7 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainerListener.java +++ b/core/java/com/android/internal/util/function/NonaConsumer.java @@ -14,13 +14,15 @@ * limitations under the License. */ -package com.android.server.wm; +package com.android.internal.util.function; + +import java.util.function.Consumer; /** - * Interface used by the creator of {@link RootWindowContainerController} to notify the changes to - * the display container in activity manager. + * A 9-argument {@link Consumer} + * + * @hide */ -public interface RootWindowContainerListener extends WindowContainerListener { - /** Called when the z-order of display is changed. */ - void onChildPositionChanged(DisplayWindowController childController, int position); +public interface NonaConsumer<A, B, C, D, E, F, G, H, I> { + void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i); } diff --git a/core/java/com/android/internal/util/function/NonaFunction.java b/core/java/com/android/internal/util/function/NonaFunction.java new file mode 100644 index 000000000000..560b4f157ee1 --- /dev/null +++ b/core/java/com/android/internal/util/function/NonaFunction.java @@ -0,0 +1,28 @@ +/* + * 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.internal.util.function; + +import java.util.function.Function; + +/** + * A 9-argument {@link Function} + * + * @hide + */ +public interface NonaFunction<A, B, C, D, E, F, G, H, I, R> { + R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i); +} diff --git a/core/java/com/android/internal/util/function/NonaPredicate.java b/core/java/com/android/internal/util/function/NonaPredicate.java new file mode 100644 index 000000000000..c1e6f377e7ae --- /dev/null +++ b/core/java/com/android/internal/util/function/NonaPredicate.java @@ -0,0 +1,28 @@ +/* + * 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.internal.util.function; + +import java.util.function.Predicate; + +/** + * A 9-argument {@link Predicate} + * + * @hide + */ +public interface NonaPredicate<A, B, C, D, E, F, G, H, I> { + boolean test(A a, B b, C c, D d, E e, F f, G g, H h, I i); +} diff --git a/core/java/com/android/internal/util/function/OctConsumer.java b/core/java/com/android/internal/util/function/OctConsumer.java new file mode 100644 index 000000000000..83ee30530c26 --- /dev/null +++ b/core/java/com/android/internal/util/function/OctConsumer.java @@ -0,0 +1,28 @@ +/* + * 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.internal.util.function; + +import java.util.function.Consumer; + +/** + * A 8-argument {@link Consumer} + * + * @hide + */ +public interface OctConsumer<A, B, C, D, E, F, G, H> { + void accept(A a, B b, C c, D d, E e, F f, G g, H h); +} diff --git a/core/java/com/android/internal/util/function/OctFunction.java b/core/java/com/android/internal/util/function/OctFunction.java new file mode 100644 index 000000000000..cb16624725b7 --- /dev/null +++ b/core/java/com/android/internal/util/function/OctFunction.java @@ -0,0 +1,28 @@ +/* + * 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.internal.util.function; + +import java.util.function.Function; + +/** + * A 8-argument {@link Function} + * + * @hide + */ +public interface OctFunction<A, B, C, D, E, F, G, H, R> { + R apply(A a, B b, C c, D d, E e, F f, G g, H h); +} diff --git a/core/java/com/android/internal/util/function/OctPredicate.java b/core/java/com/android/internal/util/function/OctPredicate.java new file mode 100644 index 000000000000..7f36d6acc066 --- /dev/null +++ b/core/java/com/android/internal/util/function/OctPredicate.java @@ -0,0 +1,28 @@ +/* + * 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.internal.util.function; + +import java.util.function.Predicate; + +/** + * A 8-argument {@link Predicate} + * + * @hide + */ +public interface OctPredicate<A, B, C, D, E, F, G, H> { + boolean test(A a, B b, C c, D d, E e, F f, G g, H h); +} diff --git a/core/java/com/android/internal/util/function/pooled/OmniFunction.java b/core/java/com/android/internal/util/function/pooled/OmniFunction.java index 4ffe44194958..d74e715605bb 100755 --- a/core/java/com/android/internal/util/function/pooled/OmniFunction.java +++ b/core/java/com/android/internal/util/function/pooled/OmniFunction.java @@ -22,6 +22,10 @@ import com.android.internal.util.function.HeptConsumer; import com.android.internal.util.function.HeptFunction; import com.android.internal.util.function.HexConsumer; import com.android.internal.util.function.HexFunction; +import com.android.internal.util.function.NonaConsumer; +import com.android.internal.util.function.NonaFunction; +import com.android.internal.util.function.OctConsumer; +import com.android.internal.util.function.OctFunction; import com.android.internal.util.function.QuadConsumer; import com.android.internal.util.function.QuadFunction; import com.android.internal.util.function.QuintConsumer; @@ -39,61 +43,62 @@ import java.util.function.Function; * * @hide */ -abstract class OmniFunction<A, B, C, D, E, F, G, R> implements +abstract class OmniFunction<A, B, C, D, E, F, G, H, I, R> implements PooledFunction<A, R>, BiFunction<A, B, R>, TriFunction<A, B, C, R>, QuadFunction<A, B, C, D, R>, QuintFunction<A, B, C, D, E, R>, HexFunction<A, B, C, D, E, F, R>, HeptFunction<A, B, C, D, E, F, G, R>, + OctFunction<A, B, C, D, E, F, G, H, R>, NonaFunction<A, B, C, D, E, F, G, H, I, R>, PooledConsumer<A>, BiConsumer<A, B>, TriConsumer<A, B, C>, QuadConsumer<A, B, C, D>, QuintConsumer<A, B, C, D, E>, HexConsumer<A, B, C, D, E, F>, - HeptConsumer<A, B, C, D, E, F, G>, - PooledPredicate<A>, BiPredicate<A, B>, + HeptConsumer<A, B, C, D, E, F, G>, OctConsumer<A, B, C, D, E, F, G, H>, + NonaConsumer<A, B, C, D, E, F, G, H, I>, PooledPredicate<A>, BiPredicate<A, B>, PooledSupplier<R>, PooledRunnable, ThrowingRunnable, ThrowingSupplier<R>, PooledSupplier.OfInt, PooledSupplier.OfLong, PooledSupplier.OfDouble { - abstract R invoke(A a, B b, C c, D d, E e, F f, G g); + abstract R invoke(A a, B b, C c, D d, E e, F f, G g, H h, I i); @Override public R apply(A o, B o2) { - return invoke(o, o2, null, null, null, null, null); + return invoke(o, o2, null, null, null, null, null, null, null); } @Override public R apply(A o) { - return invoke(o, null, null, null, null, null, null); + return invoke(o, null, null, null, null, null, null, null, null); } - public abstract <V> OmniFunction<A, B, C, D, E, F, G, V> andThen( + public abstract <V> OmniFunction<A, B, C, D, E, F, G, H, I, V> andThen( Function<? super R, ? extends V> after); - public abstract OmniFunction<A, B, C, D, E, F, G, R> negate(); + public abstract OmniFunction<A, B, C, D, E, F, G, H, I, R> negate(); @Override public void accept(A o, B o2) { - invoke(o, o2, null, null, null, null, null); + invoke(o, o2, null, null, null, null, null, null, null); } @Override public void accept(A o) { - invoke(o, null, null, null, null, null, null); + invoke(o, null, null, null, null, null, null, null, null); } @Override public void run() { - invoke(null, null, null, null, null, null, null); + invoke(null, null, null, null, null, null, null, null, null); } @Override public R get() { - return invoke(null, null, null, null, null, null, null); + return invoke(null, null, null, null, null, null, null, null, null); } @Override public boolean test(A o, B o2) { - return (Boolean) invoke(o, o2, null, null, null, null, null); + return (Boolean) invoke(o, o2, null, null, null, null, null, null, null); } @Override public boolean test(A o) { - return (Boolean) invoke(o, null, null, null, null, null, null); + return (Boolean) invoke(o, null, null, null, null, null, null, null, null); } @Override @@ -108,52 +113,72 @@ abstract class OmniFunction<A, B, C, D, E, F, G, R> implements @Override public R apply(A a, B b, C c) { - return invoke(a, b, c, null, null, null, null); + return invoke(a, b, c, null, null, null, null, null, null); } @Override public void accept(A a, B b, C c) { - invoke(a, b, c, null, null, null, null); + invoke(a, b, c, null, null, null, null, null, null); } @Override public R apply(A a, B b, C c, D d) { - return invoke(a, b, c, d, null, null, null); + return invoke(a, b, c, d, null, null, null, null, null); } @Override public R apply(A a, B b, C c, D d, E e) { - return invoke(a, b, c, d, e, null, null); + return invoke(a, b, c, d, e, null, null, null, null); } @Override public R apply(A a, B b, C c, D d, E e, F f) { - return invoke(a, b, c, d, e, f, null); + return invoke(a, b, c, d, e, f, null, null, null); } @Override public R apply(A a, B b, C c, D d, E e, F f, G g) { - return invoke(a, b, c, d, e, f, g); + return invoke(a, b, c, d, e, f, g, null, null); + } + + @Override + public R apply(A a, B b, C c, D d, E e, F f, G g, H h) { + return invoke(a, b, c, d, e, f, g, h, null); + } + + @Override + public R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i) { + return invoke(a, b, c, d, e, f, g, h, i); } @Override public void accept(A a, B b, C c, D d) { - invoke(a, b, c, d, null, null, null); + invoke(a, b, c, d, null, null, null, null, null); } @Override public void accept(A a, B b, C c, D d, E e) { - invoke(a, b, c, d, e, null, null); + invoke(a, b, c, d, e, null, null, null, null); } @Override public void accept(A a, B b, C c, D d, E e, F f) { - invoke(a, b, c, d, e, f, null); + invoke(a, b, c, d, e, f, null, null, null); } @Override public void accept(A a, B b, C c, D d, E e, F f, G g) { - invoke(a, b, c, d, e, f, g); + invoke(a, b, c, d, e, f, g, null, null); + } + + @Override + public void accept(A a, B b, C c, D d, E e, F f, G g, H h) { + invoke(a, b, c, d, e, f, g, h, null); + } + + @Override + public void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i) { + invoke(a, b, c, d, e, f, g, h, i); } @Override @@ -167,5 +192,5 @@ abstract class OmniFunction<A, B, C, D, E, F, G, R> implements } @Override - public abstract OmniFunction<A, B, C, D, E, F, G, R> recycleOnUse(); + public abstract OmniFunction<A, B, C, D, E, F, G, H, I, R> recycleOnUse(); } diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java index af3c7527c432..c00932e7a8aa 100755 --- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java +++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java @@ -25,6 +25,10 @@ import com.android.internal.util.function.HeptConsumer; import com.android.internal.util.function.HeptFunction; import com.android.internal.util.function.HexConsumer; import com.android.internal.util.function.HexFunction; +import com.android.internal.util.function.NonaConsumer; +import com.android.internal.util.function.NonaFunction; +import com.android.internal.util.function.OctConsumer; +import com.android.internal.util.function.OctFunction; import com.android.internal.util.function.QuadConsumer; import com.android.internal.util.function.QuadFunction; import com.android.internal.util.function.QuintConsumer; @@ -176,7 +180,8 @@ public interface PooledLambda { Consumer<? super A> function, A arg1) { return acquire(PooledLambdaImpl.sPool, - function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null); + function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null, + null); } /** @@ -192,7 +197,8 @@ public interface PooledLambda { Predicate<? super A> function, A arg1) { return acquire(PooledLambdaImpl.sPool, - function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null); + function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null, null, + null); } /** @@ -208,7 +214,8 @@ public interface PooledLambda { Function<? super A, ? extends R> function, A arg1) { return acquire(PooledLambdaImpl.sPool, - function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null); + function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null, null, + null); } /** @@ -238,7 +245,8 @@ public interface PooledLambda { A arg1) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null); + function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null, + null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -257,7 +265,8 @@ public interface PooledLambda { BiConsumer<? super A, ? super B> function, A arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null); + function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null, + null); } /** @@ -274,7 +283,8 @@ public interface PooledLambda { BiPredicate<? super A, ? super B> function, A arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null); + function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null, + null); } /** @@ -291,7 +301,8 @@ public interface PooledLambda { BiFunction<? super A, ? super B, ? extends R> function, A arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null); + function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null, + null); } /** @@ -308,7 +319,8 @@ public interface PooledLambda { BiConsumer<? super A, ? super B> function, ArgumentPlaceholder<A> arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null); + function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null, + null); } /** @@ -325,7 +337,8 @@ public interface PooledLambda { BiPredicate<? super A, ? super B> function, ArgumentPlaceholder<A> arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null); + function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null, + null); } /** @@ -342,7 +355,8 @@ public interface PooledLambda { BiFunction<? super A, ? super B, ? extends R> function, ArgumentPlaceholder<A> arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null); + function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null, + null); } /** @@ -359,7 +373,8 @@ public interface PooledLambda { BiConsumer<? super A, ? super B> function, A arg1, ArgumentPlaceholder<B> arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null); + function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null, + null); } /** @@ -376,7 +391,8 @@ public interface PooledLambda { BiPredicate<? super A, ? super B> function, A arg1, ArgumentPlaceholder<B> arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null); + function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null, + null); } /** @@ -393,7 +409,8 @@ public interface PooledLambda { BiFunction<? super A, ? super B, ? extends R> function, A arg1, ArgumentPlaceholder<B> arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null); + function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null, + null); } /** @@ -424,7 +441,8 @@ public interface PooledLambda { A arg1, B arg2) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null); + function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null, + null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -444,7 +462,8 @@ public interface PooledLambda { TriConsumer<? super A, ? super B, ? super C> function, A arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null); + function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null, + null); } /** @@ -462,7 +481,8 @@ public interface PooledLambda { TriFunction<? super A, ? super B, ? super C, ? extends R> function, A arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null); + function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null, + null); } /** @@ -480,7 +500,8 @@ public interface PooledLambda { TriConsumer<? super A, ? super B, ? super C> function, ArgumentPlaceholder<A> arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null); + function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null, + null); } /** @@ -498,7 +519,8 @@ public interface PooledLambda { TriFunction<? super A, ? super B, ? super C, ? extends R> function, ArgumentPlaceholder<A> arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null); + function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null, + null); } /** @@ -516,7 +538,8 @@ public interface PooledLambda { TriConsumer<? super A, ? super B, ? super C> function, A arg1, ArgumentPlaceholder<B> arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null); + function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null, + null); } /** @@ -534,7 +557,8 @@ public interface PooledLambda { TriFunction<? super A, ? super B, ? super C, ? extends R> function, A arg1, ArgumentPlaceholder<B> arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null); + function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null, + null); } /** @@ -552,7 +576,8 @@ public interface PooledLambda { TriConsumer<? super A, ? super B, ? super C> function, A arg1, B arg2, ArgumentPlaceholder<C> arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null); + function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null, + null); } /** @@ -570,7 +595,8 @@ public interface PooledLambda { TriFunction<? super A, ? super B, ? super C, ? extends R> function, A arg1, B arg2, ArgumentPlaceholder<C> arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null); + function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null, + null); } /** @@ -602,7 +628,8 @@ public interface PooledLambda { A arg1, B arg2, C arg3) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null); + function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null, + null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -623,7 +650,8 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, A arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -642,7 +670,8 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, A arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -661,7 +690,8 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -680,7 +710,8 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -699,7 +730,8 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -718,7 +750,8 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -737,7 +770,8 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -756,7 +790,8 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -775,7 +810,8 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -794,7 +830,8 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null, + null); } /** @@ -827,7 +864,8 @@ public interface PooledLambda { A arg1, B arg2, C arg3, D arg4) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null); + function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null, + null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -849,7 +887,8 @@ public interface PooledLambda { QuintConsumer<? super A, ? super B, ? super C, ? super D, ? super E> function, A arg1, B arg2, C arg3, D arg4, E arg5) { return acquire(PooledLambdaImpl.sPool, - function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null); + function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null, + null); } /** @@ -869,7 +908,8 @@ public interface PooledLambda { QuintFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5) { return acquire(PooledLambdaImpl.sPool, - function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null); + function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null, null, + null); } /** @@ -904,7 +944,8 @@ public interface PooledLambda { A arg1, B arg2, C arg3, D arg4, E arg5) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null); + function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null, + null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -927,7 +968,8 @@ public interface PooledLambda { HexConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) { return acquire(PooledLambdaImpl.sPool, - function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null); + function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null, + null); } /** @@ -948,7 +990,8 @@ public interface PooledLambda { HexFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, ? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) { return acquire(PooledLambdaImpl.sPool, - function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null); + function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null, null, + null); } /** @@ -984,7 +1027,8 @@ public interface PooledLambda { A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null); + function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null, + null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -1008,7 +1052,8 @@ public interface PooledLambda { HeptConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, ? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) { return acquire(PooledLambdaImpl.sPool, - function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null, + null); } /** @@ -1031,7 +1076,8 @@ public interface PooledLambda { ? super G, ? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) { return acquire(PooledLambdaImpl.sPool, - function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null, + null); } /** @@ -1068,7 +1114,195 @@ public interface PooledLambda { ? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null, + null); + return Message.obtain().setCallback(callback.recycleOnUse()); + } + } + + /** + * {@link PooledRunnable} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @param arg5 parameter supplied to {@code function} on call + * @param arg6 parameter supplied to {@code function} on call + * @param arg7 parameter supplied to {@code function} on call + * @param arg8 parameter supplied to {@code function} on call + * @return a {@link PooledRunnable}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) } + */ + static <A, B, C, D, E, F, G, H> PooledRunnable obtainRunnable( + OctConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, ? super G, + ? super H> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, + H arg8) { + return acquire(PooledLambdaImpl.sPool, + function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, + null); + } + + /** + * {@link PooledSupplier} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @param arg5 parameter supplied to {@code function} on call + * @param arg6 parameter supplied to {@code function} on call + * @param arg7 parameter supplied to {@code function} on call + * @param arg8 parameter supplied to {@code function} on call + * @return a {@link PooledSupplier}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) } + */ + static <A, B, C, D, E, F, G, H, R> PooledSupplier<R> obtainSupplier( + OctFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, + ? super G, ? super H, ? extends R> function, + A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8) { + return acquire(PooledLambdaImpl.sPool, + function, 8, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, + null); + } + + /** + * Factory of {@link Message}s that contain an + * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its + * {@link Message#getCallback internal callback}. + * + * The callback is equivalent to one obtainable via + * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)} + * + * Note that using this method with {@link android.os.Handler#handleMessage} + * is more efficient than the alternative of {@link android.os.Handler#post} + * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points + * when obtaining {@link Message} and {@link PooledRunnable} from pools separately + * + * You may optionally set a {@link Message#what} for the message if you want to be + * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise + * there's no need to do so + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @param arg5 parameter supplied to {@code function} on call + * @param arg6 parameter supplied to {@code function} on call + * @param arg7 parameter supplied to {@code function} on call + * @param arg8 parameter supplied to {@code function} on call + * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6, + * arg7, arg8) } when handled + */ + static <A, B, C, D, E, F, G, H> Message obtainMessage( + OctConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, ? super G, + ? super H> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, + H arg8) { + synchronized (Message.sPoolSync) { + PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, + function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, + null); + return Message.obtain().setCallback(callback.recycleOnUse()); + } + } + + /** + * {@link PooledRunnable} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @param arg5 parameter supplied to {@code function} on call + * @param arg6 parameter supplied to {@code function} on call + * @param arg7 parameter supplied to {@code function} on call + * @param arg8 parameter supplied to {@code function} on call + * @param arg9 parameter supplied to {@code function} on call + * @return a {@link PooledRunnable}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) } + */ + static <A, B, C, D, E, F, G, H, I> PooledRunnable obtainRunnable( + NonaConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, + ? super G, ? super H, ? super I> function, A arg1, B arg2, C arg3, D arg4, + E arg5, F arg6, G arg7, H arg8, I arg9) { + return acquire(PooledLambdaImpl.sPool, + function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, + arg9); + } + + /** + * {@link PooledSupplier} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @param arg5 parameter supplied to {@code function} on call + * @param arg6 parameter supplied to {@code function} on call + * @param arg7 parameter supplied to {@code function} on call + * @param arg8 parameter supplied to {@code function} on call + * @param arg9 parameter supplied to {@code function} on call + * @return a {@link PooledSupplier}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) } + */ + static <A, B, C, D, E, F, G, H, I, R> PooledSupplier<R> obtainSupplier( + NonaFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, + ? super G, ? super H, ? super I, ? extends R> function, + A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9) { + return acquire(PooledLambdaImpl.sPool, + function, 9, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, + arg9); + } + + /** + * Factory of {@link Message}s that contain an + * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its + * {@link Message#getCallback internal callback}. + * + * The callback is equivalent to one obtainable via + * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)} + * + * Note that using this method with {@link android.os.Handler#handleMessage} + * is more efficient than the alternative of {@link android.os.Handler#post} + * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points + * when obtaining {@link Message} and {@link PooledRunnable} from pools separately + * + * You may optionally set a {@link Message#what} for the message if you want to be + * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise + * there's no need to do so + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @param arg5 parameter supplied to {@code function} on call + * @param arg6 parameter supplied to {@code function} on call + * @param arg7 parameter supplied to {@code function} on call + * @param arg8 parameter supplied to {@code function} on call + * @param arg9 parameter supplied to {@code function} on call + * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6, + * arg7, arg8, arg9) } when handled + */ + static <A, B, C, D, E, F, G, H, I> Message obtainMessage( + NonaConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, + ? super G, ? super H, ? super I> function, A arg1, B arg2, C arg3, D arg4, + E arg5, F arg6, G arg7, H arg8, I arg9) { + synchronized (Message.sPoolSync) { + PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, + function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, + arg9); return Message.obtain().setCallback(callback.recycleOnUse()); } } diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java index eea1e5f0ac5c..6be626a5134a 100755 --- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java +++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java @@ -30,6 +30,12 @@ import com.android.internal.util.function.HeptPredicate; import com.android.internal.util.function.HexConsumer; import com.android.internal.util.function.HexFunction; import com.android.internal.util.function.HexPredicate; +import com.android.internal.util.function.NonaConsumer; +import com.android.internal.util.function.NonaFunction; +import com.android.internal.util.function.NonaPredicate; +import com.android.internal.util.function.OctConsumer; +import com.android.internal.util.function.OctFunction; +import com.android.internal.util.function.OctPredicate; import com.android.internal.util.function.QuadConsumer; import com.android.internal.util.function.QuadFunction; import com.android.internal.util.function.QuadPredicate; @@ -54,12 +60,12 @@ import java.util.function.Supplier; * @hide */ final class PooledLambdaImpl<R> extends OmniFunction<Object, - Object, Object, Object, Object, Object, Object, R> { + Object, Object, Object, Object, Object, Object, Object, Object, R> { private static final boolean DEBUG = false; private static final String LOG_TAG = "PooledLambdaImpl"; - private static final int MAX_ARGS = 7; + private static final int MAX_ARGS = 9; private static final int MAX_POOL_SIZE = 50; @@ -125,7 +131,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, /** * Bit schema: - * AAAAAAABCDEEEEEEFFFFFF + * AAAAAAAAABCDEEEEEEFFFFFF * * Where: * A - whether {@link #mArgs arg} at corresponding index was specified at @@ -161,17 +167,19 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, } @Override - R invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { + R invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, + Object a8, Object a9) { checkNotRecycled(); if (DEBUG) { Log.i(LOG_TAG, this + ".invoke(" + commaSeparateFirstN( - new Object[] { a1, a2, a3, a4, a5, a6, a7 }, + new Object[] { a1, a2, a3, a4, a5, a6, a7, a8, a9 }, LambdaType.decodeArgCount(getFlags(MASK_EXPOSED_AS))) + ")"); } - final boolean notUsed = fillInArg(a1) && fillInArg(a2) && fillInArg(a3) - && fillInArg(a4) && fillInArg(a5) && fillInArg(a6) && fillInArg(a7); + final boolean notUsed = fillInArg(a1) && fillInArg(a2) && fillInArg(a3) && fillInArg(a4) + && fillInArg(a5) && fillInArg(a6) && fillInArg(a7) && fillInArg(a8) + && fillInArg(a9); int argCount = LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE)); if (argCount != LambdaType.MASK_ARG_COUNT) { for (int i = 0; i < argCount; i++) { @@ -335,7 +343,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, popArg(2), popArg(3), popArg(4), popArg(5)); } } - } + } break; case 7: { switch (returnType) { @@ -356,7 +364,49 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, popArg(5), popArg(6)); } } - } + } break; + + case 8: { + switch (returnType) { + case LambdaType.ReturnType.VOID: { + ((OctConsumer) mFunc).accept(popArg(0), popArg(1), + popArg(2), popArg(3), popArg(4), + popArg(5), popArg(6), popArg(7)); + return null; + } + case LambdaType.ReturnType.BOOLEAN: { + return (R) (Object) ((OctPredicate) mFunc).test(popArg(0), + popArg(1), popArg(2), popArg(3), + popArg(4), popArg(5), popArg(6), popArg(7)); + } + case LambdaType.ReturnType.OBJECT: { + return (R) ((OctFunction) mFunc).apply(popArg(0), popArg(1), + popArg(2), popArg(3), popArg(4), + popArg(5), popArg(6), popArg(7)); + } + } + } break; + + case 9: { + switch (returnType) { + case LambdaType.ReturnType.VOID: { + ((NonaConsumer) mFunc).accept(popArg(0), popArg(1), + popArg(2), popArg(3), popArg(4), popArg(5), + popArg(6), popArg(7), popArg(8)); + return null; + } + case LambdaType.ReturnType.BOOLEAN: { + return (R) (Object) ((NonaPredicate) mFunc).test(popArg(0), + popArg(1), popArg(2), popArg(3), popArg(4), + popArg(5), popArg(6), popArg(7), popArg(8)); + } + case LambdaType.ReturnType.OBJECT: { + return (R) ((NonaFunction) mFunc).apply(popArg(0), popArg(1), + popArg(2), popArg(3), popArg(4), popArg(5), + popArg(6), popArg(7), popArg(8)); + } + } + } break; } throw new IllegalStateException("Unknown function type: " + LambdaType.toString(funcType)); } @@ -419,8 +469,8 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, * Internal non-typesafe factory method for {@link PooledLambdaImpl} */ static <E extends PooledLambda> E acquire(Pool pool, Object func, - int fNumArgs, int numPlaceholders, int fReturnType, - Object a, Object b, Object c, Object d, Object e, Object f, Object g) { + int fNumArgs, int numPlaceholders, int fReturnType, Object a, Object b, Object c, + Object d, Object e, Object f, Object g, Object h, Object i) { PooledLambdaImpl r = acquire(pool); if (DEBUG) { Log.i(LOG_TAG, @@ -436,6 +486,8 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, + ", e = " + e + ", f = " + f + ", g = " + g + + ", h = " + h + + ", i = " + i + ")"); } r.mFunc = func; @@ -449,6 +501,8 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, setIfInBounds(r.mArgs, 4, e); setIfInBounds(r.mArgs, 5, f); setIfInBounds(r.mArgs, 6, g); + setIfInBounds(r.mArgs, 7, h); + setIfInBounds(r.mArgs, 8, i); return (E) r; } @@ -474,13 +528,14 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, } @Override - public OmniFunction<Object, Object, Object, Object, Object, Object, Object, R> negate() { + public OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object, + R> negate() { throw new UnsupportedOperationException(); } @Override - public <V> OmniFunction<Object, Object, Object, Object, Object, Object, Object, V> andThen( - Function<? super R, ? extends V> after) { + public <V> OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object, + V> andThen(Function<? super R, ? extends V> after) { throw new UnsupportedOperationException(); } @@ -500,7 +555,8 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, } @Override - public OmniFunction<Object, Object, Object, Object, Object, Object, Object, R> recycleOnUse() { + public OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object, + R> recycleOnUse() { if (DEBUG) Log.i(LOG_TAG, this + ".recycleOnUse()"); mFlags |= FLAG_RECYCLE_ON_USE; return this; @@ -584,6 +640,8 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, case 5: return "Quint"; case 6: return "Hex"; case 7: return "Hept"; + case 8: return "Oct"; + case 9: return "Nona"; default: throw new IllegalArgumentException("" + argCount); } } diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index 137ca7f2ac27..36fe4fc5af49 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -27,6 +27,7 @@ import android.view.DragEvent; import android.view.IWindow; import android.view.IWindowSession; import android.view.PointerIcon; +import android.view.InsetsState; import com.android.internal.os.IResultReceiver; @@ -53,6 +54,10 @@ public class BaseIWindow extends IWindow.Stub { } @Override + public void insetsChanged(InsetsState insetsState) { + } + + @Override public void moved(int newX, int newY) { } diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index 7635a727ae85..b7e656bc9278 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -814,7 +814,14 @@ public class ResolverDrawerLayout extends ViewGroup { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp.alwaysShow && child.getVisibility() != GONE) { - measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); + if (lp.maxHeight != -1) { + final int remainingHeight = heightSize - heightUsed; + measureChildWithMargins(child, widthSpec, widthPadding, + MeasureSpec.makeMeasureSpec(lp.maxHeight, MeasureSpec.AT_MOST), + lp.maxHeight > remainingHeight ? lp.maxHeight - remainingHeight : 0); + } else { + measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); + } heightUsed += child.getMeasuredHeight(); } } @@ -824,9 +831,17 @@ public class ResolverDrawerLayout extends ViewGroup { // And now the rest. for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (!lp.alwaysShow && child.getVisibility() != GONE) { - measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); + if (lp.maxHeight != -1) { + final int remainingHeight = heightSize - heightUsed; + measureChildWithMargins(child, widthSpec, widthPadding, + MeasureSpec.makeMeasureSpec(lp.maxHeight, MeasureSpec.AT_MOST), + lp.maxHeight > remainingHeight ? lp.maxHeight - remainingHeight : 0); + } else { + measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); + } heightUsed += child.getMeasuredHeight(); } } @@ -938,6 +953,7 @@ public class ResolverDrawerLayout extends ViewGroup { public boolean alwaysShow; public boolean ignoreOffset; public boolean hasNestedScrollIndicator; + public int maxHeight; public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); @@ -953,6 +969,8 @@ public class ResolverDrawerLayout extends ViewGroup { hasNestedScrollIndicator = a.getBoolean( R.styleable.ResolverDrawerLayout_LayoutParams_layout_hasNestedScrollIndicator, false); + maxHeight = a.getDimensionPixelSize( + R.styleable.ResolverDrawerLayout_LayoutParams_layout_maxHeight, -1); a.recycle(); } @@ -965,6 +983,7 @@ public class ResolverDrawerLayout extends ViewGroup { this.alwaysShow = source.alwaysShow; this.ignoreOffset = source.ignoreOffset; this.hasNestedScrollIndicator = source.hasNestedScrollIndicator; + this.maxHeight = source.maxHeight; } public LayoutParams(MarginLayoutParams source) { diff --git a/core/jni/android_hardware_display_DisplayViewport.cpp b/core/jni/android_hardware_display_DisplayViewport.cpp index 05f6556bfb35..e74aafe61e00 100644 --- a/core/jni/android_hardware_display_DisplayViewport.cpp +++ b/core/jni/android_hardware_display_DisplayViewport.cpp @@ -40,6 +40,7 @@ static struct { jfieldID deviceWidth; jfieldID deviceHeight; jfieldID uniqueId; + jfieldID physicalPort; jfieldID type; } gDisplayViewportClassInfo; @@ -54,6 +55,9 @@ static struct { status_t android_hardware_display_DisplayViewport_toNative(JNIEnv* env, jobject viewportObj, DisplayViewport* viewport) { + static const jclass byteClass = FindClassOrDie(env, "java/lang/Byte"); + static const jmethodID byteValue = env->GetMethodID(byteClass, "byteValue", "()B"); + viewport->displayId = env->GetIntField(viewportObj, gDisplayViewportClassInfo.displayId); viewport->orientation = env->GetIntField(viewportObj, gDisplayViewportClassInfo.orientation); viewport->deviceWidth = env->GetIntField(viewportObj, gDisplayViewportClassInfo.deviceWidth); @@ -65,6 +69,12 @@ status_t android_hardware_display_DisplayViewport_toNative(JNIEnv* env, jobject viewport->uniqueId = ScopedUtfChars(env, uniqueId).c_str(); } + viewport->physicalPort = std::nullopt; + jobject physicalPort = env->GetObjectField(viewportObj, gDisplayViewportClassInfo.physicalPort); + if (physicalPort != nullptr) { + viewport->physicalPort = std::make_optional(env->CallByteMethod(physicalPort, byteValue)); + } + viewport->type = static_cast<ViewportType>(env->GetIntField(viewportObj, gDisplayViewportClassInfo.type)); @@ -112,6 +122,9 @@ int register_android_hardware_display_DisplayViewport(JNIEnv* env) { gDisplayViewportClassInfo.uniqueId = GetFieldIDOrDie(env, gDisplayViewportClassInfo.clazz, "uniqueId", "Ljava/lang/String;"); + gDisplayViewportClassInfo.physicalPort = GetFieldIDOrDie(env, + gDisplayViewportClassInfo.clazz, "physicalPort", "Ljava/lang/Byte;"); + gDisplayViewportClassInfo.type = GetFieldIDOrDie(env, gDisplayViewportClassInfo.clazz, "type", "I"); diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 2c0158a03434..adab8e2bae39 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -2032,6 +2032,35 @@ static jint android_media_AudioSystem_get_FCC_8(JNIEnv *env, jobject thiz) { return FCC_8; } +static jint +android_media_AudioSystem_setAssistantUid(JNIEnv *env, jobject thiz, jint uid) +{ + status_t status = AudioSystem::setAssistantUid(uid); + return (jint)nativeToJavaStatus(status); +} + +static jint +android_media_AudioSystem_setA11yServicesUids(JNIEnv *env, jobject thiz, jintArray uids) { + std::vector<uid_t> nativeUidsVector; + + if (uids != nullptr) { + jsize len = env->GetArrayLength(uids); + + if (len > 0) { + int *nativeUids = nullptr; + nativeUids = env->GetIntArrayElements(uids, 0); + if (nativeUids != nullptr) { + for (size_t i = 0; i < len; i++) { + nativeUidsVector.push_back(nativeUids[i]); + } + env->ReleaseIntArrayElements(uids, nativeUids, 0); + } + } + } + status_t status = AudioSystem::setA11yServicesUids(nativeUidsVector); + return (jint)nativeToJavaStatus(status); +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gMethods[] = { @@ -2092,6 +2121,8 @@ static const JNINativeMethod gMethods[] = { {"getMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_AudioSystem_getMicrophones}, {"getSurroundFormats", "(Ljava/util/Map;Z)I", (void *)android_media_AudioSystem_getSurroundFormats}, {"setSurroundFormatEnabled", "(IZ)I", (void *)android_media_AudioSystem_setSurroundFormatEnabled}, + {"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid}, + {"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids}, }; static const JNINativeMethod gEventHandlerMethods[] = { diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 49d500754290..fa1da4bfbf3a 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -33,8 +33,6 @@ #include <iomanip> #include <string> -#include <android-base/stringprintf.h> -#include <android-base/unique_fd.h> #include <debuggerd/client.h> #include <log/log.h> #include <utils/misc.h> @@ -50,10 +48,6 @@ namespace android { -static inline UniqueFile MakeUniqueFile(const char* path, const char* mode) { - return UniqueFile(fopen(path, mode), safeFclose); -} - enum { HEAP_UNKNOWN, HEAP_DALVIK, diff --git a/core/jni/android_os_Debug.h b/core/jni/android_os_Debug.h index 81270ca994bb..c7b731bdb615 100644 --- a/core/jni/android_os_Debug.h +++ b/core/jni/android_os_Debug.h @@ -19,6 +19,8 @@ #include <memory> #include <stdio.h> +#include <android-base/stringprintf.h> +#include <android-base/unique_fd.h> namespace android { @@ -27,6 +29,11 @@ inline void safeFclose(FILE* fp) { } using UniqueFile = std::unique_ptr<FILE, decltype(&safeFclose)>; + +inline UniqueFile MakeUniqueFile(const char* path, const char* mode) { + return UniqueFile(fopen(path, mode), safeFclose); +} + UniqueFile OpenSmapsOrRollup(int pid); } // namespace android diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 4c7defbf7358..377e65c33dd0 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -1128,6 +1128,39 @@ static jlong android_os_Process_getPss(JNIEnv* env, jobject clazz, jint pid) return pss * 1024; } +static jlongArray android_os_Process_getRss(JNIEnv* env, jobject clazz, jint pid) +{ + // total, file, anon, swap + jlong rss[4] = {0, 0, 0, 0}; + std::string status_path = + android::base::StringPrintf("/proc/%d/status", pid); + UniqueFile file = MakeUniqueFile(status_path.c_str(), "re"); + + char line[256]; + while (fgets(line, sizeof(line), file.get())) { + jlong v; + if ( sscanf(line, "VmRSS: %" SCNd64 " kB", &v) == 1) { + rss[0] = v; + } else if ( sscanf(line, "RssFile: %" SCNd64 " kB", &v) == 1) { + rss[1] = v; + } else if ( sscanf(line, "RssAnon: %" SCNd64 " kB", &v) == 1) { + rss[2] = v; + } else if ( sscanf(line, "VmSwap: %" SCNd64 " kB", &v) == 1) { + rss[3] = v; + } + } + + jlongArray rssArray = env->NewLongArray(4); + if (rssArray == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return NULL; + } + + env->SetLongArrayRegion(rssArray, 0, 4, rss); + + return rssArray; +} + jintArray android_os_Process_getPidsForCommands(JNIEnv* env, jobject clazz, jobjectArray commandNames) { @@ -1253,6 +1286,7 @@ static const JNINativeMethod methods[] = { {"parseProcLine", "([BII[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_parseProcLine}, {"getElapsedCpuTime", "()J", (void*)android_os_Process_getElapsedCpuTime}, {"getPss", "(I)J", (void*)android_os_Process_getPss}, + {"getRss", "(I)[J", (void*)android_os_Process_getRss}, {"getPidsForCommands", "([Ljava/lang/String;)[I", (void*)android_os_Process_getPidsForCommands}, //{"setApplicationObject", "(Landroid/os/IBinder;)V", (void*)android_os_Process_setApplicationObject}, {"killProcessGroup", "(II)I", (void*)android_os_Process_killProcessGroup}, diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index e89b5933fdc8..752624b0a0be 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -468,6 +468,10 @@ static jboolean android_view_RenderNode_getAllowForceDark(jlong renderNodePtr) { return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getAllowForceDark(); } +static jlong android_view_RenderNode_getUniqueId(jlong renderNodePtr) { + return reinterpret_cast<RenderNode*>(renderNodePtr)->uniqueId(); +} + // ---------------------------------------------------------------------------- // RenderProperties - Animations // ---------------------------------------------------------------------------- @@ -694,6 +698,7 @@ static const JNINativeMethod gMethods[] = { { "nGetHeight", "(J)I", (void*) android_view_RenderNode_getHeight }, { "nSetAllowForceDark", "(JZ)Z", (void*) android_view_RenderNode_setAllowForceDark }, { "nGetAllowForceDark", "(J)Z", (void*) android_view_RenderNode_getAllowForceDark }, + { "nGetUniqueId", "(J)J", (void*) android_view_RenderNode_getUniqueId }, }; int register_android_view_RenderNode(JNIEnv* env) { diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index 99f096d57dba..679a1d254cf1 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -156,7 +156,7 @@ message DisplayContentProto { optional int32 rotation = 11; optional ScreenRotationAnimationProto screen_rotation_animation = 12; optional DisplayFramesProto display_frames = 13; - optional int32 surface_size = 14; + optional int32 surface_size = 14 [deprecated=true]; optional string focused_app = 15; optional AppTransitionProto app_transition = 16; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 8c5b6f4765fb..8b66be310abc 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -620,6 +620,8 @@ <protected-broadcast android:name="android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL" /> + <protected-broadcast android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY" /> + <!-- ====================================================================== --> <!-- RUNTIME PERMISSIONS --> <!-- ====================================================================== --> @@ -4204,6 +4206,22 @@ <permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS" android:protectionLevel="signature|appop" /> + <!-- @SystemApi Allows requesting the framework broadcast the + {@link Intent#ACTION_DEVICE_CUSTOMIZATION_READY} intent. + @hide --> + <permission android:name="android.permission.SEND_DEVICE_CUSTOMIZATION_READY" + android:protectionLevel="signature|privileged" /> + + <!-- @SystemApi Permission that protects the {@link Intent#ACTION_DEVICE_CUSTOMIZATION_READY} + intent. + @hide --> + <permission android:name="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY" + android:protectionLevel="signature|preinstalled" /> + <!-- @SystemApi Allows wallpaper to be rendered in ambient mode. + @hide --> + <permission android:name="android.permission.AMBIENT_WALLPAPER" + android:protectionLevel="signature|preinstalled" /> + <application android:process="system" android:persistent="true" android:hasCode="false" diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 6c4861b34429..918070cbf829 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -7935,7 +7935,9 @@ wallpaper. --> <attr name="showMetadataInPreview" format="boolean" /> - <!-- Wallpapers optimized and capable of drawing in ambient mode will return true. --> + <!-- Wallpapers optimized and capable of drawing in ambient mode will return true. + This feature requires the android.permission.AMBIENT_WALLPAPER permission. + @hide @SystemApi --> <attr name="supportsAmbientMode" format="boolean" /> <!-- Uri that specifies a settings Slice for this wallpaper. --> @@ -8803,6 +8805,7 @@ <attr name="layout_ignoreOffset" format="boolean" /> <attr name="layout_gravity" /> <attr name="layout_hasNestedScrollIndicator" format="boolean" /> + <attr name="layout_maxHeight" /> </declare-styleable> <!-- @hide --> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 18d1d5dbdbe3..089c59f3a09f 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1601,6 +1601,10 @@ <attr name="request" /> <attr name="protectionLevel" /> <attr name="permissionFlags" /> + <!-- If {@code true} applications that target Q <em>must</em> specify the permission usage + attributes in their {@code uses-permission} elements or the permission will not be + granted. --> + <attr name="usageInfoRequired" format="boolean" /> </declare-styleable> <!-- The <code>permission-group</code> tag declares a logical grouping of @@ -1700,6 +1704,81 @@ requested. If it does support the feature, it will be as if the manifest didn't request it at all. --> <attr name="requiredNotFeature" format="string" /> + + <!-- Specify if the app uploads data, or derived data, guarded by this permission. + + If the permission is defined with {@link android.R.attr#usageInfoRequired} + {@code true} this <em>must</em> be specified by apps that target Android Q or the + permission will not be granted, it will be as if the manifest didn't request it at all. + --> + <attr name="dataSentOffDevice"> + <!-- The application may send data, or derived data, guarded by this permission off of the + device. --> + <enum name="yes" value="1" /> + <!-- The application may send data, or derived data, guarded by this permission off of the + device, however it will only do so when explicitly triggered by a user action. --> + <enum name="userTriggered" value="2" /> + <!-- The application does not send data, or derived data, guarded by this permission off + of the device. --> + <enum name="no" value="3" /> + </attr> + + <!-- Specify if the application or its related off-device services provide data, + or derived data, guarded by this permission to third parties outside of the developer's + organization that do not qualify as data processors. + + If the permission is defined with {@link android.R.attr#usageInfoRequired} + {@code true} this <em>must</em> be specified by apps that target Android Q or the + permission will not be granted, it will be as if the manifest didn't request it at all. + --> + <attr name="dataSharedWithThirdParty"> + <!-- The application or its services may provide data, or derived data, guarded by this + permission to third party organizations. --> + <enum name="yes" value="1" /> + <!-- The application or its services may provide data, or derived data, guarded by this + permission to third party organizations, however it will only do so when explicitly + triggered by a user action. --> + <enum name="userTriggered" value="2" /> + <!-- The application or its services does not provide data, or derived data, guarded by + this permission to third party organizations. --> + <enum name="no" value="3" /> + </attr> + + <!-- Specify if the application or its related off-device services use data, + or derived data, guarded by this permission for monetization purposes. + + For example, if the data is sold to another party or used for targeting advertisements + this must be set to {@code yes}. + + If the permission is defined with {@link android.R.attr#usageInfoRequired} + {@code true} this <em>must</em> be specified by apps that target Android Q or the + permission will not be granted, it will be as if the manifest didn't request it at all. + --> + <attr name="dataUsedForMonetization"> + <!-- The application or its services may use data, or derived data, guarded by this + permission for monetization purposes. --> + <enum name="yes" value="1" /> + <!-- The application or its services may use data, or derived data, guarded by this + permission for monetization purposes, however it will only do so when explicity + triggered by a user action. --> + <enum name="userTriggered" value="2" /> + <!-- The application or its services does not use data, or derived data, guarded by + this permission for monetization purposes. --> + <enum name="no" value="3" /> + </attr> + + <!-- Specify how long the application or its related off-device services store + data, or derived data, guarded by this permission. + + This can be one of "notRetained", "userSelected", "unlimited", or a number + representing the number of weeks the data is retained. + + If the permission is defined with {@link android.R.attr#usageInfoRequired} + {@code true} this <em>must</em> be specified by apps that target Android Q or the + permission will not be granted, it will be as if the manifest didn't request it at all. + --> + <attr name="dataRetentionTime" format="string" /> + </declare-styleable> <!-- The <code>uses-configuration</code> tag specifies diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index 64e5bc0dbc50..bbe3ff995ac3 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -190,4 +190,7 @@ <!-- A tag used to save the view added to a transition overlay --> <item type="id" name="transition_overlay_view_tag" /> + + <!-- A tag used to save the notification action object --> + <item type="id" name="notification_action_index_tag" /> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 63cac5172f8a..feefcad3f56f 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2909,6 +2909,7 @@ <public name="opticalInsetRight" /> <public name="opticalInsetBottom" /> <public name="forceDarkAllowed" /> + <!-- @hide @SystemApi --> <public name="supportsAmbientMode" /> <!-- @hide For use by platform and tools only. Developers should not specify this value. --> <public name="usesNonSdkApi" /> @@ -2922,6 +2923,11 @@ <public name="importantForContentCapture" /> <public name="supportsMultipleDisplays" /> <public name="useAppZygote" /> + <public name="usageInfoRequired" /> + <public name="dataSentOffDevice" /> + <public name="dataSharedWithThirdParty" /> + <public name="dataUsedForMonetization" /> + <public name="dataRetentionTime" /> </public-group> <public-group type="drawable" first-id="0x010800b4"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 9264f90f8901..e251e27a5496 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2683,6 +2683,7 @@ <java-symbol type="id" name="smart_reply_container" /> <java-symbol type="id" name="remote_input_tag" /> <java-symbol type="id" name="pending_intent_tag" /> + <java-symbol type="id" name="notification_action_index_tag" /> <java-symbol type="attr" name="seekBarDialogPreferenceStyle" /> <java-symbol type="string" name="ext_media_status_removed" /> diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java index e1cb911eaf72..e89a4d3bf94f 100644 --- a/core/tests/coretests/src/android/app/NotificationTest.java +++ b/core/tests/coretests/src/android/app/NotificationTest.java @@ -106,9 +106,9 @@ public class NotificationTest { int backgroundColor = 0xff585868; int initialForegroundColor = 0xff505868; builder.setColorPalette(backgroundColor, initialForegroundColor); - int primaryTextColor = builder.getPrimaryTextColor(); + int primaryTextColor = builder.getPrimaryTextColor(builder.mParams); assertTrue(satisfiesTextContrast(primaryTextColor, backgroundColor)); - int secondaryTextColor = builder.getSecondaryTextColor(); + int secondaryTextColor = builder.getSecondaryTextColor(builder.mParams); assertTrue(satisfiesTextContrast(secondaryTextColor, backgroundColor)); } diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 5e669e4f82b9..305d2af2122d 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -126,6 +126,7 @@ public class SettingsBackupTest { Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS, Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS, Settings.Global.AUTOMATIC_POWER_SAVER_MODE, + Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED, Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD, Settings.Global.BATTERY_DISCHARGE_THRESHOLD, Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS, @@ -662,7 +663,8 @@ public class SettingsBackupTest { Settings.Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE, Settings.Secure.FLASHLIGHT_AVAILABLE, - Settings.Secure.FLASHLIGHT_ENABLED); + Settings.Secure.FLASHLIGHT_ENABLED, + Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED); @Test public void systemSettingsBackedUpOrBlacklisted() { diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java new file mode 100644 index 000000000000..ed472d2a7f64 --- /dev/null +++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java @@ -0,0 +1,103 @@ +/* + * 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; + +import static android.view.InsetsState.TYPE_NAVIGATION_BAR; +import static junit.framework.Assert.assertEquals; + +import android.graphics.Insets; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.FlakyTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@Presubmit +@FlakyTest(detail = "Promote once confirmed non-flaky") +@RunWith(AndroidJUnit4.class) +public class InsetsSourceTest { + + private InsetsSource mSource = new InsetsSource(TYPE_NAVIGATION_BAR); + + @Before + public void setUp() { + mSource.setVisible(true); + } + + @Test + public void testCalculateInsetsTop() { + mSource.setFrame(new Rect(0, 0, 500, 100)); + Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500), + false /* ignoreVisibility */); + assertEquals(Insets.of(0, 100, 0, 0), insets); + } + + @Test + public void testCalculateInsetsBottom() { + mSource.setFrame(new Rect(0, 400, 500, 500)); + Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500), + false /* ignoreVisibility */); + assertEquals(Insets.of(0, 0, 0, 100), insets); + } + + @Test + public void testCalculateInsetsLeft() { + mSource.setFrame(new Rect(0, 0, 100, 500)); + Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500), + false /* ignoreVisibility */); + assertEquals(Insets.of(100, 0, 0, 0), insets); + } + + @Test + public void testCalculateInsetsRight() { + mSource.setFrame(new Rect(400, 0, 500, 500)); + Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500), + false /* ignoreVisibility */); + assertEquals(Insets.of(0, 0, 100, 0), insets); + } + + @Test + public void testCalculateInsets_overextend() { + mSource.setFrame(new Rect(0, 0, 500, 100)); + Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500), + false /* ignoreVisibility */); + assertEquals(Insets.of(0, 100, 0, 0), insets); + } + + @Test + public void testCalculateInsets_invisible() { + mSource.setFrame(new Rect(0, 0, 500, 100)); + mSource.setVisible(false); + Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500), + false /* ignoreVisibility */); + assertEquals(Insets.of(0, 0, 0, 0), insets); + } + + @Test + public void testCalculateInsets_ignoreVisibility() { + mSource.setFrame(new Rect(0, 0, 500, 100)); + mSource.setVisible(false); + Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500), + true /* ignoreVisibility */); + assertEquals(Insets.of(0, 100, 0, 0), insets); + } + + // Parcel and equals already tested via InsetsStateTest +} diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java new file mode 100644 index 000000000000..6bb9539e89bd --- /dev/null +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -0,0 +1,130 @@ +/* + * 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; + +import static android.view.InsetsState.TYPE_IME; +import static android.view.InsetsState.TYPE_NAVIGATION_BAR; +import static android.view.InsetsState.TYPE_TOP_BAR; +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import android.graphics.Rect; +import android.os.Parcel; +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.FlakyTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@Presubmit +@FlakyTest(detail = "Promote once confirmed non-flaky") +@RunWith(AndroidJUnit4.class) +public class InsetsStateTest { + + private InsetsState mState = new InsetsState(); + private InsetsState mState2 = new InsetsState(); + + @Test + public void testCalculateInsets() { + mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(TYPE_TOP_BAR).setVisible(true); + mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300)); + mState.getSource(TYPE_IME).setVisible(true); + WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, + DisplayCutout.NO_CUTOUT); + assertEquals(new Rect(0, 100, 0, 100), insets.getSystemWindowInsets()); + } + + @Test + public void testCalculateInsets_imeAndNav() { + mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(0, 200, 100, 300)); + mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true); + mState.getSource(TYPE_IME).setFrame(new Rect(0, 100, 100, 300)); + mState.getSource(TYPE_IME).setVisible(true); + WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, + DisplayCutout.NO_CUTOUT); + assertEquals(100, insets.getStableInsetBottom()); + assertEquals(new Rect(0, 0, 0, 200), insets.getSystemWindowInsets()); + } + + @Test + public void testCalculateInsets_navRightStatusTop() { + mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(TYPE_TOP_BAR).setVisible(true); + mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300)); + mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true); + WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, + DisplayCutout.NO_CUTOUT); + assertEquals(new Rect(0, 100, 20, 0), insets.getSystemWindowInsets()); + } + + @Test + public void testStripForDispatch() { + mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(TYPE_TOP_BAR).setVisible(true); + mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300)); + mState.getSource(TYPE_IME).setVisible(true); + mState.removeSource(TYPE_IME); + WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, + DisplayCutout.NO_CUTOUT); + assertEquals(0, insets.getSystemWindowInsetBottom()); + } + + @Test + public void testEquals_differentRect() { + mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState2.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 10, 10)); + assertNotEquals(mState, mState2); + } + + @Test + public void testEquals_differentSource() { + mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100)); + assertNotEquals(mState, mState2); + } + + @Test + public void testEquals_sameButDifferentInsertOrder() { + mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100)); + mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100)); + mState2.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); + assertEquals(mState, mState2); + } + + @Test + public void testEquals_visibility() { + mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(TYPE_IME).setVisible(true); + mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100)); + assertNotEquals(mState, mState2); + } + + @Test + public void testParcelUnparcel() { + mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(TYPE_IME).setVisible(true); + mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100)); + Parcel p = Parcel.obtain(); + mState.writeToParcel(p, 0 /* flags */); + mState2.readFromParcel(p); + p.recycle(); + assertEquals(mState, mState2); + } +} diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java index 44561227a497..36792bbf6fb6 100644 --- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java +++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java @@ -467,6 +467,21 @@ public class RemoteViewsTest { assertArrayEquals(container.mSharedViewNames, new String[] {"e0", "e1", "e2"}); } + @Test + public void setIntTag() { + RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test); + int index = 10; + views.setIntTag( + R.id.layout, com.android.internal.R.id.notification_action_index_tag, index); + + RemoteViews recovered = parcelAndRecreate(views); + RemoteViews cloned = new RemoteViews(recovered); + View inflated = cloned.apply(mContext, mContainer); + + assertEquals( + index, inflated.getTag(com.android.internal.R.id.notification_action_index_tag)); + } + private class WidgetContainer extends AppWidgetHostView { int[] mSharedViewIds; String[] mSharedViewNames; diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java index 9fcb06ee54f6..404c99c82a4c 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java @@ -103,6 +103,49 @@ public class ResolverActivityTest { } @Test + public void setMaxHeight() throws Exception { + Intent sendIntent = createSendImageIntent(); + List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); + + when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + waitForIdle(); + + final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); + final View resolverList = activity.findViewById(R.id.resolver_list); + final int initialResolverHeight = resolverList.getHeight(); + + activity.runOnUiThread(() -> { + ResolverDrawerLayout layout = (ResolverDrawerLayout) + activity.findViewById( + R.id.contentPanel); + ((ResolverDrawerLayout.LayoutParams) resolverList.getLayoutParams()).maxHeight + = initialResolverHeight - 1; + // Force a relayout + layout.invalidate(); + layout.requestLayout(); + }); + waitForIdle(); + assertThat("Drawer should be capped at maxHeight", + resolverList.getHeight() == (initialResolverHeight - 1)); + + activity.runOnUiThread(() -> { + ResolverDrawerLayout layout = (ResolverDrawerLayout) + activity.findViewById( + R.id.contentPanel); + ((ResolverDrawerLayout.LayoutParams) resolverList.getLayoutParams()).maxHeight + = initialResolverHeight + 1; + // Force a relayout + layout.invalidate(); + layout.requestLayout(); + }); + waitForIdle(); + assertThat("Drawer should not change height if its height is less than maxHeight", + resolverList.getHeight() == initialResolverHeight); + } + + @Test public void setShowAtTopToTrue() throws Exception { Intent sendIntent = createSendImageIntent(); List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index 3b0dc9d9f125..135c13703131 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -64,7 +64,7 @@ public class Canvas extends BaseCanvas { public boolean isRecordingFor(Object o) { return false; } // may be null - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521088) private Bitmap mBitmap; // optional field set by the caller diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java index de110c849338..d9da27c8b931 100644 --- a/graphics/java/android/graphics/Insets.java +++ b/graphics/java/android/graphics/Insets.java @@ -82,6 +82,17 @@ public final class Insets { } /** + * Add two Insets. + * + * @param a The first Insets to add. + * @param b The second Insets to add. + * @return a + b, i. e. all insets on every side are added together. + */ + public static @NonNull Insets add(@NonNull Insets a, @NonNull Insets b) { + return Insets.of(a.left + b.left, a.top + b.top, a.right + b.right, a.bottom + b.bottom); + } + + /** * Two Insets instances are equal iff they belong to the same class and their fields are * pairwise equal. * diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java index c4dc0adb3be0..40a32f3429dc 100644 --- a/graphics/java/android/graphics/Rect.java +++ b/graphics/java/android/graphics/Rect.java @@ -106,6 +106,20 @@ public final class Rect implements Parcelable { } /** + * @hide + */ + public Rect(@Nullable Insets r) { + if (r == null) { + left = top = right = bottom = 0; + } else { + left = r.left; + top = r.top; + right = r.right; + bottom = r.bottom; + } + } + + /** * Returns a copy of {@code r} if {@code r} is not {@code null}, or {@code null} otherwise. * * @hide @@ -418,6 +432,18 @@ public final class Rect implements Parcelable { } /** + * Insets the rectangle on all sides specified by the dimensions of {@code insets}. + * @hide + * @param insets The insets to inset the rect by. + */ + public void inset(Insets insets) { + left += insets.left; + top += insets.top; + right -= insets.right; + bottom -= insets.bottom; + } + + /** * Insets the rectangle on all sides specified by the insets. * @hide * @param left The amount to add from the rectangle's left diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java index 45d7a2178b84..d6f08b92a648 100644 --- a/graphics/java/android/graphics/RenderNode.java +++ b/graphics/java/android/graphics/RenderNode.java @@ -1173,6 +1173,22 @@ public final class RenderNode { return nGetAllowForceDark(mNativeRenderNode); } + /** + * Returns the unique ID that identifies this RenderNode. This ID is unique for the + * lifetime of the process. IDs are reset on process death, and are unique only within + * the process. + * + * This ID is intended to be used with debugging tools to associate a particular + * RenderNode across different debug dumping & inspection tools. For example + * a View layout inspector should include the unique ID for any RenderNodes that it owns + * to associate the drawing content with the layout content. + * + * @return the unique ID for this RenderNode + */ + public long getUniqueId() { + return nGetUniqueId(mNativeRenderNode); + } + /////////////////////////////////////////////////////////////////////////// // Animations /////////////////////////////////////////////////////////////////////////// @@ -1479,4 +1495,7 @@ public final class RenderNode { @CriticalNative private static native boolean nGetAllowForceDark(long renderNode); + + @CriticalNative + private static native long nGetUniqueId(long renderNode); } diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index acb46b39daf5..3933f508fe84 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -655,10 +655,11 @@ public class Typeface { * Returns the maximum capacity of custom fallback families. * * This includes the the first font family passed to the constructor. + * It is guaranteed that the value will be greater than or equal to 64. * * @return the maximum number of font families for the custom fallback */ - public static @IntRange(from = 1) int getMaxCustomFallbackCount() { + public static @IntRange(from = 64) int getMaxCustomFallbackCount() { return MAX_CUSTOM_FALLBACK; } diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index caf610b8c236..5bd59d479876 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -713,11 +713,12 @@ public abstract class Drawable { } /** - * Whether this drawable requests projection. + * Whether this drawable requests projection. Indicates that the + * {@link android.graphics.RenderNode} this Drawable will draw into should be drawn immediately + * after the closest ancestor RenderNode containing a projection receiver. * - * @hide magic! + * @see android.graphics.RenderNode#setProjectBackwards(boolean) */ - @UnsupportedAppUsage public boolean isProjected() { return false; } diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 17d2db71ab58..5f27faed08f3 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -45,9 +45,6 @@ cc_defaults { ], product_variables: { - device_uses_hwc2: { - cflags: ["-DUSE_HWC2"], - }, eng: { lto: { never: true, @@ -182,6 +179,7 @@ cc_defaults { "renderthread/CanvasContext.cpp", "renderthread/DrawFrameTask.cpp", "renderthread/EglManager.cpp", + "renderthread/ReliableSurface.cpp", "renderthread/VulkanManager.cpp", "renderthread/RenderProxy.cpp", "renderthread/RenderTask.cpp", diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index e7ae7675f0b8..ccbb6c10d3af 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -82,7 +82,6 @@ static FrameInfoIndex sFrameStart = FrameInfoIndex::IntendedVsync; JankTracker::JankTracker(ProfileDataContainer* globalData, const DisplayInfo& displayInfo) { mGlobalData = globalData; nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1_s / displayInfo.fps); -#if USE_HWC2 nsecs_t sfOffset = frameIntervalNanos - (displayInfo.presentationDeadline - 1_ms); nsecs_t offsetDelta = sfOffset - displayInfo.appVsyncOffset; // There are two different offset cases. If the offsetDelta is positive @@ -96,7 +95,6 @@ JankTracker::JankTracker(ProfileDataContainer* globalData, const DisplayInfo& di // return due to the staggering of VSYNC-app & VSYNC-sf. mDequeueTimeForgiveness = offsetDelta + 4_ms; } -#endif setFrameInterval(frameIntervalNanos); } diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index d2a8f02cc6a7..4a639102192f 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -29,6 +29,7 @@ #include <SkPathOps.h> #include <algorithm> +#include <atomic> #include <sstream> #include <string> @@ -47,8 +48,14 @@ private: TreeInfo* mTreeInfo; }; +static int64_t generateId() { + static std::atomic<int64_t> sNextId{1}; + return sNextId++; +} + RenderNode::RenderNode() - : mDirtyPropertyFields(0) + : mUniqueId(generateId()) + , mDirtyPropertyFields(0) , mNeedsDisplayListSync(false) , mDisplayList(nullptr) , mStagingDisplayList(nullptr) @@ -444,5 +451,38 @@ const SkPath* RenderNode::getClippedOutline(const SkRect& clipRect) const { return &mClippedOutlineCache.clippedOutline; } +using StringBuffer = FatVector<char, 128>; + +template <typename... T> +static void format(StringBuffer& buffer, const std::string_view& format, T... args) { + buffer.resize(buffer.capacity()); + while (1) { + int needed = snprintf(buffer.data(), buffer.size(), + format.data(), std::forward<T>(args)...); + if (needed < 0) { + buffer[0] = '\0'; + buffer.resize(1); + return; + } + if (needed < buffer.size()) { + buffer.resize(needed + 1); + return; + } + buffer.resize(buffer.size() * 2); + } +} + +void RenderNode::markDrawStart(SkCanvas& canvas) { + StringBuffer buffer; + format(buffer, "RenderNode(id=%d, name='%s')", uniqueId(), getName()); + canvas.drawAnnotation(SkRect::MakeWH(getWidth(), getHeight()), buffer.data(), nullptr); +} + +void RenderNode::markDrawEnd(SkCanvas& canvas) { + StringBuffer buffer; + format(buffer, "/RenderNode(id=%d, name='%s')", uniqueId(), getName()); + canvas.drawAnnotation(SkRect::MakeWH(getWidth(), getHeight()), buffer.data(), nullptr); +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index be0b46b1c45f..6060123ed759 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -213,6 +213,11 @@ public: UsageHint usageHint() const { return mUsageHint; } + int64_t uniqueId() const { return mUniqueId; } + + void markDrawStart(SkCanvas& canvas); + void markDrawEnd(SkCanvas& canvas); + private: void computeOrderingImpl(RenderNodeOp* opState, std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface, @@ -233,6 +238,7 @@ private: void incParentRefCount() { mParentCount++; } void decParentRefCount(TreeObserver& observer, TreeInfo* info = nullptr); + const int64_t mUniqueId; String8 mName; sp<VirtualLightRefBase> mUserContext; diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index ea14d11b7b3e..d80cb6d1ab70 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -115,12 +115,26 @@ void RenderNodeDrawable::onDraw(SkCanvas* canvas) { } } +class MarkDraw { +public: + explicit MarkDraw(SkCanvas& canvas, RenderNode& node) : mCanvas(canvas), mNode(node) { + if (CC_UNLIKELY(Properties::skpCaptureEnabled)) { + mNode.markDrawStart(mCanvas); + } + } + ~MarkDraw() { + if (CC_UNLIKELY(Properties::skpCaptureEnabled)) { + mNode.markDrawEnd(mCanvas); + } + } +private: + SkCanvas& mCanvas; + RenderNode& mNode; +}; + void RenderNodeDrawable::forceDraw(SkCanvas* canvas) { RenderNode* renderNode = mRenderNode.get(); - if (CC_UNLIKELY(Properties::skpCaptureEnabled)) { - SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight()); - canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr); - } + MarkDraw _marker{*canvas, *renderNode}; // We only respect the nothingToDraw check when we are composing a layer. This // ensures that we paint the layer even if it is not currently visible in the diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 142bca95e598..07979a22c988 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -155,7 +155,7 @@ void SkiaOpenGLPipeline::onStop() { } } -bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior, +bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior, ColorMode colorMode) { if (mEglSurface != EGL_NO_SURFACE) { mEglManager.destroySurface(mEglSurface); diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index 4ab3541d447b..479910697871 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -42,7 +42,7 @@ public: bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) override; DeferredLayerUpdater* createTextureLayer() override; - bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior, + bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior, renderthread::ColorMode colorMode) override; void onStop() override; bool isSurfaceReady() override; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index a494e490aea1..e50ad1cd8c44 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -115,7 +115,7 @@ DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { void SkiaVulkanPipeline::onStop() {} -bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior, +bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior, ColorMode colorMode) { if (mVkSurface) { mVkManager.destroySurface(mVkSurface); diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index 14c0d69dba33..02874c7d2c69 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -38,7 +38,7 @@ public: bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) override; DeferredLayerUpdater* createTextureLayer() override; - bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior, + bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior, renderthread::ColorMode colorMode) override; void onStop() override; bool isSurfaceReady() override; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index f1a522ecd588..182233fb3715 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -142,7 +142,12 @@ void CanvasContext::destroy() { void CanvasContext::setSurface(sp<Surface>&& surface) { ATRACE_CALL(); - mNativeSurface = std::move(surface); + if (surface) { + mNativeSurface = new ReliableSurface{std::move(surface)}; + mNativeSurface->setDequeueTimeout(500_ms); + } else { + mNativeSurface = nullptr; + } ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB; bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode); @@ -285,6 +290,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy info.damageAccumulator = &mDamageAccumulator; info.layerUpdateQueue = &mLayerUpdateQueue; + info.out.canDrawThisFrame = true; mAnimationContext->startFrame(info.mode); mRenderPipeline->onPrepareTree(); @@ -304,7 +310,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy mIsDirty = true; - if (CC_UNLIKELY(!mNativeSurface.get())) { + if (CC_UNLIKELY(!hasSurface())) { mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); info.out.canDrawThisFrame = false; return; @@ -323,27 +329,6 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy // the deadline for RT animations info.out.canDrawThisFrame = false; } - /* This logic exists to try and recover from a display latch miss, which essentially - * results in the bufferqueue being double-buffered instead of triple-buffered. - * SurfaceFlinger itself now tries to handle & recover from this situation, so this - * logic should no longer be necessary. As it's occasionally triggering when - * undesired disable it. - * TODO: Remove this entirely if the results are solid. - else if (vsyncDelta >= mRenderThread.timeLord().frameIntervalNanos() * 3 || - (latestVsync - mLastDropVsync) < 500_ms) { - // It's been several frame intervals, assume the buffer queue is fine - // or the last drop was too recent - info.out.canDrawThisFrame = true; - } else { - info.out.canDrawThisFrame = !isSwapChainStuffed(); - if (!info.out.canDrawThisFrame) { - // dropping frame - mLastDropVsync = mRenderThread.timeLord().latestVsync(); - } - } - */ - } else { - info.out.canDrawThisFrame = true; } // TODO: Do we need to abort out if the backdrop is added but not ready? Should that even @@ -354,6 +339,19 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy if (!info.out.canDrawThisFrame) { mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); + return; + } + + int err = mNativeSurface->reserveNext(); + if (err != OK) { + mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); + info.out.canDrawThisFrame = false; + ALOGW("reserveNext failed, error = %d", err); + if (err != TIMED_OUT) { + // A timed out surface can still recover, but assume others are permanently dead. + setSurface(nullptr); + } + return; } bool postedFrameCallback = false; diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 70be4a6d7730..9e7abf447cd6 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -25,6 +25,7 @@ #include "IRenderPipeline.h" #include "LayerUpdateQueue.h" #include "RenderNode.h" +#include "ReliableSurface.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" #include "thread/Task.h" @@ -219,7 +220,7 @@ private: EGLint mLastFrameHeight = 0; RenderThread& mRenderThread; - sp<Surface> mNativeSurface; + sp<ReliableSurface> mNativeSurface; // stopped indicates the CanvasContext will reject actual redraw operations, // and defer repaint until it is un-stopped bool mStopped = false; diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 65ced6ad9316..8230dfd44f9a 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -31,6 +31,8 @@ #include <string> #include <vector> +#include <system/window.h> +#include <gui/Surface.h> #define GLES_VERSION 2 @@ -106,7 +108,7 @@ void EglManager::initialize() { LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE, "Failed to initialize display %p! err=%s", mEglDisplay, eglErrorString()); - ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor); + ALOGV("Initialized EGL, version %d.%d", (int)major, (int)minor); initExtensions(); diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index 4972554c65cc..42e17b273bee 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -28,9 +28,9 @@ class GrContext; -namespace android { +struct ANativeWindow; -class Surface; +namespace android { namespace uirenderer { @@ -67,7 +67,7 @@ public: virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) = 0; virtual DeferredLayerUpdater* createTextureLayer() = 0; - virtual bool setSurface(Surface* window, SwapBehavior swapBehavior, ColorMode colorMode) = 0; + virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior, ColorMode colorMode) = 0; virtual void onStop() = 0; virtual bool isSurfaceReady() = 0; virtual bool isContextReady() = 0; diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp new file mode 100644 index 000000000000..0ab4cd29f1cd --- /dev/null +++ b/libs/hwui/renderthread/ReliableSurface.cpp @@ -0,0 +1,283 @@ +/* + * 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. + */ + +#include "ReliableSurface.h" + +#include <private/android/AHardwareBufferHelpers.h> + +namespace android::uirenderer::renderthread { + +// TODO: Make surface less protected +// This exists because perform is a varargs, and ANativeWindow has no va_list perform. +// So wrapping/chaining that is hard. Telling the compiler to ignore protected is easy, so we do +// that instead +struct SurfaceExposer : Surface { + // Make warnings happy + SurfaceExposer() = delete; + + using Surface::setBufferCount; + using Surface::setSwapInterval; + using Surface::dequeueBuffer; + using Surface::queueBuffer; + using Surface::cancelBuffer; + using Surface::lockBuffer_DEPRECATED; + using Surface::perform; +}; + +#define callProtected(surface, func, ...) ((*surface).*&SurfaceExposer::func)(__VA_ARGS__) + +ReliableSurface::ReliableSurface(sp<Surface>&& surface) : mSurface(std::move(surface)) { + LOG_ALWAYS_FATAL_IF(!mSurface, "Error, unable to wrap a nullptr"); + + ANativeWindow::setSwapInterval = hook_setSwapInterval; + ANativeWindow::dequeueBuffer = hook_dequeueBuffer; + ANativeWindow::cancelBuffer = hook_cancelBuffer; + ANativeWindow::queueBuffer = hook_queueBuffer; + ANativeWindow::query = hook_query; + ANativeWindow::perform = hook_perform; + + ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED; + ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED; + ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED; + ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED; +} + +void ReliableSurface::perform(int operation, va_list args) { + std::lock_guard _lock{mMutex}; + + switch (operation) { + case NATIVE_WINDOW_SET_USAGE: + mUsage = va_arg(args, uint32_t); + break; + case NATIVE_WINDOW_SET_USAGE64: + mUsage = va_arg(args, uint64_t); + break; + case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: + /* width */ va_arg(args, uint32_t); + /* height */ va_arg(args, uint32_t); + mFormat = va_arg(args, PixelFormat); + break; + case NATIVE_WINDOW_SET_BUFFERS_FORMAT: + mFormat = va_arg(args, PixelFormat); + break; + } +} + +int ReliableSurface::reserveNext() { + { + std::lock_guard _lock{mMutex}; + if (mReservedBuffer) { + ALOGW("reserveNext called but there was already a buffer reserved?"); + return OK; + } + if (mInErrorState) { + return UNKNOWN_ERROR; + } + } + + int fenceFd = -1; + ANativeWindowBuffer* buffer = nullptr; + int result = callProtected(mSurface, dequeueBuffer, &buffer, &fenceFd); + + { + std::lock_guard _lock{mMutex}; + LOG_ALWAYS_FATAL_IF(mReservedBuffer, "race condition in reserveNext"); + mReservedBuffer = buffer; + mReservedFenceFd.reset(fenceFd); + if (result != OK) { + ALOGW("reserveNext failed, error %d", result); + } + } + + return result; +} + +void ReliableSurface::clearReservedBuffer() { + std::lock_guard _lock{mMutex}; + if (mReservedBuffer) { + ALOGW("Reserved buffer %p was never used", mReservedBuffer); + } + mReservedBuffer = nullptr; + mReservedFenceFd.reset(); +} + +int ReliableSurface::cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd) { + clearReservedBuffer(); + if (isFallbackBuffer(buffer)) { + if (fenceFd > 0) { + close(fenceFd); + } + return OK; + } + int result = callProtected(mSurface, cancelBuffer, buffer, fenceFd); + return result; +} + +int ReliableSurface::dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd) { + { + std::lock_guard _lock{mMutex}; + if (mReservedBuffer) { + *buffer = mReservedBuffer; + *fenceFd = mReservedFenceFd.release(); + mReservedBuffer = nullptr; + return OK; + } + } + + int result = callProtected(mSurface, dequeueBuffer, buffer, fenceFd); + if (result != OK) { + ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result); + *buffer = acquireFallbackBuffer(); + *fenceFd = -1; + return *buffer ? OK : INVALID_OPERATION; + } + return OK; +} + +int ReliableSurface::queueBuffer(ANativeWindowBuffer* buffer, int fenceFd) { + clearReservedBuffer(); + + if (isFallbackBuffer(buffer)) { + if (fenceFd > 0) { + close(fenceFd); + } + return OK; + } + + int result = callProtected(mSurface, queueBuffer, buffer, fenceFd); + return result; +} + +bool ReliableSurface::isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const { + if (!mScratchBuffer || !windowBuffer) { + return false; + } + ANativeWindowBuffer* scratchBuffer = + AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get()); + return windowBuffer == scratchBuffer; +} + +ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer() { + std::lock_guard _lock{mMutex}; + mInErrorState = true; + + if (mScratchBuffer) { + return AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get()); + } + + AHardwareBuffer_Desc desc; + desc.usage = mUsage; + desc.format = mFormat; + desc.width = 1; + desc.height = 1; + desc.layers = 1; + desc.rfu0 = 0; + desc.rfu1 = 0; + AHardwareBuffer* newBuffer = nullptr; + int err = AHardwareBuffer_allocate(&desc, &newBuffer); + if (err) { + // Allocate failed, that sucks + ALOGW("Failed to allocate scratch buffer, error=%d", err); + return nullptr; + } + mScratchBuffer.reset(newBuffer); + return AHardwareBuffer_to_ANativeWindowBuffer(newBuffer); +} + +Surface* ReliableSurface::getWrapped(const ANativeWindow* window) { + return getSelf(window)->mSurface.get(); +} + +int ReliableSurface::hook_setSwapInterval(ANativeWindow* window, int interval) { + return callProtected(getWrapped(window), setSwapInterval, interval); +} + +int ReliableSurface::hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, + int* fenceFd) { + return getSelf(window)->dequeueBuffer(buffer, fenceFd); +} + +int ReliableSurface::hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, + int fenceFd) { + return getSelf(window)->cancelBuffer(buffer, fenceFd); +} + +int ReliableSurface::hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, + int fenceFd) { + return getSelf(window)->queueBuffer(buffer, fenceFd); +} + +int ReliableSurface::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer** buffer) { + ANativeWindowBuffer* buf; + int fenceFd = -1; + int result = window->dequeueBuffer(window, &buf, &fenceFd); + if (result != OK) { + return result; + } + sp<Fence> fence(new Fence(fenceFd)); + int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED"); + if (waitResult != OK) { + ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d", waitResult); + window->cancelBuffer(window, buf, -1); + return waitResult; + } + *buffer = buf; + return result; +} + +int ReliableSurface::hook_cancelBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer* buffer) { + return window->cancelBuffer(window, buffer, -1); +} + +int ReliableSurface::hook_lockBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer* buffer) { + // This method is a no-op in Surface as well + return OK; +} + +int ReliableSurface::hook_queueBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer* buffer) { + return window->queueBuffer(window, buffer, -1); +} + +int ReliableSurface::hook_query(const ANativeWindow* window, int what, int* value) { + return getWrapped(window)->query(what, value); +} + +int ReliableSurface::hook_perform(ANativeWindow* window, int operation, ...) { + va_list args; + va_start(args, operation); + int result = callProtected(getWrapped(window), perform, operation, args); + va_end(args); + + switch (operation) { + case NATIVE_WINDOW_SET_BUFFERS_FORMAT: + case NATIVE_WINDOW_SET_USAGE: + case NATIVE_WINDOW_SET_USAGE64: + va_start(args, operation); + getSelf(window)->perform(operation, args); + va_end(args); + break; + default: + break; + } + + return result; +} + +}; // namespace android::uirenderer::renderthread
\ No newline at end of file diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h new file mode 100644 index 000000000000..9ae53a9798d3 --- /dev/null +++ b/libs/hwui/renderthread/ReliableSurface.h @@ -0,0 +1,85 @@ +/* + * 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. + */ + +#pragma once + +#include <gui/Surface.h> +#include <utils/Macros.h> +#include <utils/StrongPointer.h> + +#include <memory> + +namespace android::uirenderer::renderthread { + +class ReliableSurface : public ANativeObjectBase<ANativeWindow, ReliableSurface, RefBase> { + PREVENT_COPY_AND_ASSIGN(ReliableSurface); + +public: + ReliableSurface(sp<Surface>&& surface); + + void setDequeueTimeout(nsecs_t timeout) { mSurface->setDequeueTimeout(timeout); } + + int reserveNext(); + + void allocateBuffers() { mSurface->allocateBuffers(); } + + int query(int what, int* value) const { return mSurface->query(what, value); } + + nsecs_t getLastDequeueStartTime() const { return mSurface->getLastDequeueStartTime(); } + + uint64_t getNextFrameNumber() const { return mSurface->getNextFrameNumber(); } + +private: + const sp<Surface> mSurface; + + mutable std::mutex mMutex; + + uint64_t mUsage = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER; + PixelFormat mFormat = PIXEL_FORMAT_RGBA_8888; + std::unique_ptr<AHardwareBuffer, void (*)(AHardwareBuffer*)> mScratchBuffer{ + nullptr, AHardwareBuffer_release}; + bool mInErrorState = false; + ANativeWindowBuffer* mReservedBuffer = nullptr; + base::unique_fd mReservedFenceFd; + + bool isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const; + ANativeWindowBuffer* acquireFallbackBuffer(); + void clearReservedBuffer(); + + void perform(int operation, va_list args); + int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd); + int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd); + int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd); + + static Surface* getWrapped(const ANativeWindow*); + + // ANativeWindow hooks + static int hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd); + static int hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, + int* fenceFd); + static int hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd); + + static int hook_perform(ANativeWindow* window, int operation, ...); + static int hook_query(const ANativeWindow* window, int what, int* value); + static int hook_setSwapInterval(ANativeWindow* window, int interval); + + static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); + static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer** buffer); + static int hook_lockBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); + static int hook_queueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); +}; + +}; // namespace android::uirenderer::renderthread
\ No newline at end of file diff --git a/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h index f60c338bf9c4..8de2ab49f42b 100644 --- a/libs/services/include/android/os/StatsLogEventWrapper.h +++ b/libs/services/include/android/os/StatsLogEventWrapper.h @@ -82,6 +82,11 @@ struct StatsLogValue { STATS_LOG_VALUE_TYPE type; }; +struct WorkChain { + std::vector<int32_t> uids; + std::vector<std::string> tags; +}; + // Represents a parcelable object. Only used to send data from Android OS to statsd. class StatsLogEventWrapper : public android::Parcelable { public: @@ -99,7 +104,9 @@ class StatsLogEventWrapper : public android::Parcelable { int64_t getWallClockTimeNs() const { return mWallClockTimeNs; } - std::vector<StatsLogValue> getElements() const { return mElements; } + const std::vector<StatsLogValue>& getElements() const { return mElements; } + + const std::vector<WorkChain>& getWorkChains() const { return mWorkChains; } private: int mTagId; @@ -109,6 +116,8 @@ class StatsLogEventWrapper : public android::Parcelable { int64_t mWallClockTimeNs; std::vector<StatsLogValue> mElements; + + std::vector<WorkChain> mWorkChains; }; } // Namespace os } // Namespace android diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp index 04c4629b5432..f6dfdef16e19 100644 --- a/libs/services/src/os/StatsLogEventWrapper.cpp +++ b/libs/services/src/os/StatsLogEventWrapper.cpp @@ -58,6 +58,31 @@ status_t StatsLogEventWrapper::readFromParcel(const Parcel* in) { ALOGE("statsd could not read wall clock time from parcel"); return res; } + int numWorkChain = 0; + if ((res = in->readInt32(&numWorkChain)) != OK) { + ALOGE("statsd could not read number of work chains from parcel"); + return res; + } + if (numWorkChain > 0) { + for (int i = 0; i < numWorkChain; i++) { + int numNodes = 0; + if ((res = in->readInt32(&numNodes)) != OK) { + ALOGE( + "statsd could not read number of nodes in work chain from parcel"); + return res; + } + if (numNodes == 0) { + ALOGE("empty work chain"); + return BAD_VALUE; + } + WorkChain wc; + for (int j = 0; j < numNodes; j++) { + wc.uids.push_back(in->readInt32()); + wc.tags.push_back(std::string(String8(in->readString16()).string())); + } + mWorkChains.push_back(wc); + } + } int dataSize = 0; if ((res = in->readInt32(&dataSize)) != OK) { ALOGE("statsd could not read data size from parcel"); diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index ff1bdd47f565..d10900e5d160 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -541,6 +541,7 @@ public class AudioManager { * Adjusting the volume due to a hardware key press. * @hide */ + @SystemApi public static final int FLAG_FROM_KEY = 1 << 12; private static final String[] FLAG_NAMES = { diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 1ebe059ad44b..082a375470ee 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -924,6 +924,15 @@ public class AudioSystem public static native int setSurroundFormatEnabled(int audioFormat, boolean enabled); + /** + * Communicate UID of active assistant to audio policy service. + */ + public static native int setAssistantUid(int uid); + /** + * Communicate UIDs of active accessibility services to audio policy service. + */ + public static native int setA11yServicesUids(int[] uids); + // Items shared with audio service /** diff --git a/media/java/android/media/CallbackDataSourceDesc.java b/media/java/android/media/CallbackDataSourceDesc.java index 82273da7097d..0e8e6ceeaa15 100644 --- a/media/java/android/media/CallbackDataSourceDesc.java +++ b/media/java/android/media/CallbackDataSourceDesc.java @@ -20,27 +20,29 @@ import android.annotation.NonNull; /** * @hide - * Structure for file data source descriptor. + * Structure of data source descriptor for sources using callback. * - * Used by {@link MediaPlayer2#setDataSource(CallbackDataSourceDesc)} + * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}, + * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and + * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)} * to set data source for playback. * * <p>Users should use {@link Builder} to create {@link CallbackDataSourceDesc}. * */ public class CallbackDataSourceDesc extends DataSourceDesc { - private Media2DataSource mMedia2DataSource; + private DataSourceCallback mDataSourceCallback; private CallbackDataSourceDesc() { } /** - * Return the Media2DataSource of this data source. + * Return the DataSourceCallback of this data source. * It's meaningful only when {@code getType} returns {@link #TYPE_CALLBACK}. - * @return the Media2DataSource of this data source + * @return the DataSourceCallback of this data source */ - public Media2DataSource getMedia2DataSource() { - return mMedia2DataSource; + public DataSourceCallback getDataSourceCallback() { + return mDataSourceCallback; } /** @@ -58,7 +60,7 @@ public class CallbackDataSourceDesc extends DataSourceDesc { * </pre> */ public static class Builder extends BuilderBase<Builder> { - private Media2DataSource mMedia2DataSource; + private DataSourceCallback mDataSourceCallback; /** * Constructs a new Builder with the defaults. @@ -77,7 +79,7 @@ public class CallbackDataSourceDesc extends DataSourceDesc { if (dsd == null) { return; // use default } - mMedia2DataSource = dsd.mMedia2DataSource; + mDataSourceCallback = dsd.mDataSourceCallback; } /** @@ -90,21 +92,21 @@ public class CallbackDataSourceDesc extends DataSourceDesc { public @NonNull CallbackDataSourceDesc build() { CallbackDataSourceDesc dsd = new CallbackDataSourceDesc(); super.build(dsd); - dsd.mMedia2DataSource = mMedia2DataSource; + dsd.mDataSourceCallback = mDataSourceCallback; return dsd; } /** - * Sets the data source (Media2DataSource) to use. + * Sets the data source (DataSourceCallback) to use. * - * @param m2ds the Media2DataSource for the media to play + * @param dscb the DataSourceCallback for the media to play * @return the same Builder instance. - * @throws NullPointerException if m2ds is null. + * @throws NullPointerException if dscb is null. */ - public @NonNull Builder setDataSource(@NonNull Media2DataSource m2ds) { - Media2Utils.checkArgument(m2ds != null, "data source cannot be null."); - mMedia2DataSource = m2ds; + public @NonNull Builder setDataSource(@NonNull DataSourceCallback dscb) { + Media2Utils.checkArgument(dscb != null, "data source cannot be null."); + mDataSourceCallback = dscb; return this; } } diff --git a/media/java/android/media/Media2DataSource.java b/media/java/android/media/DataSourceCallback.java index 08df632a99b3..9b27baf32204 100644 --- a/media/java/android/media/Media2DataSource.java +++ b/media/java/android/media/DataSourceCallback.java @@ -27,12 +27,12 @@ import java.io.IOException; * * <p class="note">Methods of this interface may be called on multiple different * threads. There will be a thread synchronization point between each call to ensure that - * modifications to the state of your Media2DataSource are visible to future calls. This means + * modifications to the state of your DataSourceCallback are visible to future calls. This means * you don't need to do your own synchronization unless you're modifying the - * Media2DataSource from another thread while it's being used by the framework.</p> + * DataSourceCallback from another thread while it's being used by the framework.</p> * */ -public abstract class Media2DataSource implements Closeable { +public abstract class DataSourceCallback implements Closeable { /** * Called to request data from the given position. * diff --git a/media/java/android/media/DataSourceDesc.java b/media/java/android/media/DataSourceDesc.java index 360af34bc722..702034e987ca 100644 --- a/media/java/android/media/DataSourceDesc.java +++ b/media/java/android/media/DataSourceDesc.java @@ -22,7 +22,9 @@ import android.annotation.NonNull; * @hide * Base class of data source descriptor. * - * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)} + * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}, + * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and + * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)} * to set data source for playback. * * <p>Users should use subclasses' builder to change {@link DataSourceDesc}. @@ -30,7 +32,7 @@ import android.annotation.NonNull; */ public class DataSourceDesc { // intentionally less than long.MAX_VALUE - public static final long LONG_MAX = 0x7ffffffffffffffL; + static final long LONG_MAX = 0x7ffffffffffffffL; // keep consistent with native code public static final long LONG_MAX_TIME_MS = LONG_MAX / 1000; @@ -46,6 +48,19 @@ public class DataSourceDesc { } /** + * Releases the resources held by this {@code DataSourceDesc} object. + */ + void close() { + } + + // Have to declare protected for finalize() since it is protected + // in the base class Object. + @Override + protected void finalize() throws Throwable { + close(); + } + + /** * Return the media Id of data source. * @return the media Id of data source */ diff --git a/media/java/android/media/FileDataSourceDesc.java b/media/java/android/media/FileDataSourceDesc.java index 9e80975e032c..763a81f53765 100644 --- a/media/java/android/media/FileDataSourceDesc.java +++ b/media/java/android/media/FileDataSourceDesc.java @@ -17,20 +17,26 @@ package android.media; import android.annotation.NonNull; +import android.os.ParcelFileDescriptor; +import android.util.Log; -import java.io.FileDescriptor; +import java.io.IOException; /** * @hide - * Structure for data source descriptor. + * Structure of data source descriptor for sources using file descriptor. * - * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)} + * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}, + * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and + * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)} * to set data source for playback. * * <p>Users should use {@link Builder} to create {@link FileDataSourceDesc}. * */ public class FileDataSourceDesc extends DataSourceDesc { + private static final String TAG = "FileDataSourceDesc"; + /** * Used when the length of file descriptor is unknown. * @@ -38,34 +44,61 @@ public class FileDataSourceDesc extends DataSourceDesc { */ public static final long FD_LENGTH_UNKNOWN = LONG_MAX; - private FileDescriptor mFD; + private ParcelFileDescriptor mPFD; private long mOffset = 0; private long mLength = FD_LENGTH_UNKNOWN; private FileDataSourceDesc() { + super(); + } + + /** + * Releases the resources held by this {@code FileDataSourceDesc} object. + */ + @Override + void close() { + super.close(); + closeFD(); + } + + /** + * Releases the file descriptor held by this {@code FileDataSourceDesc} object. + */ + void closeFD() { + synchronized (this) { + if (mPFD != null) { + try { + mPFD.close(); + } catch (IOException e) { + Log.e(TAG, "failed to close pfd: " + e); + } + + mPFD = null; + } + } } /** - * Return the FileDescriptor of this data source. - * @return the FileDescriptor of this data source + * Return the ParcelFileDescriptor of this data source. + * @return the ParcelFileDescriptor of this data source */ - public FileDescriptor getFileDescriptor() { - return mFD; + public ParcelFileDescriptor getParcelFileDescriptor() { + return mPFD; } /** - * Return the offset associated with the FileDescriptor of this data source. + * Return the offset associated with the ParcelFileDescriptor of this data source. * It's meaningful only when it has been set by the {@link Builder}. - * @return the offset associated with the FileDescriptor of this data source + * @return the offset associated with the ParcelFileDescriptor of this data source */ public long getOffset() { return mOffset; } /** - * Return the content length associated with the FileDescriptor of this data source. + * Return the content length associated with the ParcelFileDescriptor of this data source. * {@link #FD_LENGTH_UNKNOWN} means same as the length of source content. - * @return the content length associated with the FileDescriptor of this data source + * @return the content length associated with the ParcelFileDescriptor of this data source */ public long getLength() { return mLength; @@ -78,7 +111,7 @@ public class FileDataSourceDesc extends DataSourceDesc { * * <pre class="prettyprint"> * FileDataSourceDesc newDSD = new FileDataSourceDesc.Builder() - * .setDataSource(fd, 0, srcLength) + * .setDataSource(pfd, 0, srcLength) * .setStartPosition(1000) * .setEndPosition(15000) * .build(); @@ -86,7 +119,7 @@ public class FileDataSourceDesc extends DataSourceDesc { * </pre> */ public static class Builder extends BuilderBase<Builder> { - private FileDescriptor mFD; + private ParcelFileDescriptor mPFD; private long mOffset = 0; private long mLength = FD_LENGTH_UNKNOWN; @@ -107,7 +140,7 @@ public class FileDataSourceDesc extends DataSourceDesc { if (dsd == null) { return; // use default } - mFD = dsd.mFD; + mPFD = dsd.mPFD; mOffset = dsd.mOffset; mLength = dsd.mLength; } @@ -122,7 +155,7 @@ public class FileDataSourceDesc extends DataSourceDesc { public @NonNull FileDataSourceDesc build() { FileDataSourceDesc dsd = new FileDataSourceDesc(); super.build(dsd); - dsd.mFD = mFD; + dsd.mPFD = mPFD; dsd.mOffset = mOffset; dsd.mLength = mLength; @@ -130,38 +163,46 @@ public class FileDataSourceDesc extends DataSourceDesc { } /** - * Sets the data source (FileDescriptor) to use. The FileDescriptor must be - * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility - * to close the file descriptor after the source has been used. + * Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be + * seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc} + * created by this builder is passed to {@link MediaPlayer2} via + * {@link MediaPlayer2#setDataSource(DataSourceDesc)}, + * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} or + * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}, MediaPlayer2 will + * close the ParcelFileDescriptor. * - * @param fd the FileDescriptor for the file to play + * @param pfd the ParcelFileDescriptor for the file to play * @return the same Builder instance. - * @throws NullPointerException if fd is null. + * @throws NullPointerException if pfd is null. */ - public @NonNull Builder setDataSource(@NonNull FileDescriptor fd) { - Media2Utils.checkArgument(fd != null, "fd cannot be null."); + public @NonNull Builder setDataSource(@NonNull ParcelFileDescriptor pfd) { + Media2Utils.checkArgument(pfd != null, "pfd cannot be null."); resetDataSource(); - mFD = fd; + mPFD = pfd; return this; } /** - * Sets the data source (FileDescriptor) to use. The FileDescriptor must be - * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility - * to close the file descriptor after the source has been used. + * Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be + * seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc} + * created by this builder is passed to {@link MediaPlayer2} via + * {@link MediaPlayer2#setDataSource(DataSourceDesc)}, + * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} or + * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}, MediaPlayer2 will + * close the ParcelFileDescriptor. * * Any negative number for offset is treated as 0. * Any negative number for length is treated as maximum length of the data source. * - * @param fd the FileDescriptor for the file to play + * @param pfd the ParcelFileDescriptor for the file to play * @param offset the offset into the file where the data to be played starts, in bytes * @param length the length in bytes of the data to be played * @return the same Builder instance. - * @throws NullPointerException if fd is null. + * @throws NullPointerException if pfd is null. */ public @NonNull Builder setDataSource( - @NonNull FileDescriptor fd, long offset, long length) { - Media2Utils.checkArgument(fd != null, "fd cannot be null."); + @NonNull ParcelFileDescriptor pfd, long offset, long length) { + Media2Utils.checkArgument(pfd != null, "pfd cannot be null."); if (offset < 0) { offset = 0; } @@ -169,14 +210,14 @@ public class FileDataSourceDesc extends DataSourceDesc { length = FD_LENGTH_UNKNOWN; } resetDataSource(); - mFD = fd; + mPFD = pfd; mOffset = offset; mLength = length; return this; } private void resetDataSource() { - mFD = null; + mPFD = null; mOffset = 0; mLength = FD_LENGTH_UNKNOWN; } diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java index 267dab9c46a1..b047f8de515d 100644 --- a/media/java/android/media/MediaPlayer2.java +++ b/media/java/android/media/MediaPlayer2.java @@ -36,6 +36,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; +import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.PowerManager; import android.util.Log; @@ -412,6 +413,9 @@ public class MediaPlayer2 implements AutoCloseable mHandlerThread = null; } + setCurrentSourceInfo(null); + clearNextSourceInfos(); + // Modular DRM clean up mOnDrmConfigHelper = null; synchronized (mDrmEventCbLock) { @@ -457,10 +461,8 @@ public class MediaPlayer2 implements AutoCloseable synchronized (mDrmEventCbLock) { mDrmEventCallbackRecords.clear(); } - synchronized (mSrcLock) { - mCurrentSourceInfo = null; - mNextSourceInfos.clear(); - } + setCurrentSourceInfo(null); + clearNextSourceInfos(); synchronized (mTaskLock) { mPendingTasks.clear(); @@ -685,6 +687,8 @@ public class MediaPlayer2 implements AutoCloseable /** * Sets the data source as described by a DataSourceDesc. + * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor} + * in the {@link FileDataSourceDesc} will be closed by the player. * * @param dsd the descriptor of data source you want to play * @return a token which can be used to cancel the operation later with {@link #cancelCommand}. @@ -696,13 +700,17 @@ public class MediaPlayer2 implements AutoCloseable void process() throws IOException { Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null"); int state = getState(); - if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) { - throw new IllegalStateException("called in wrong state " + state); - } + try { + if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) { + throw new IllegalStateException("called in wrong state " + state); + } - synchronized (mSrcLock) { - mCurrentSourceInfo = new SourceInfo(dsd); - handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId); + synchronized (mSrcLock) { + setCurrentSourceInfo(new SourceInfo(dsd)); + handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId); + } + } finally { + dsd.close(); } } }); @@ -711,6 +719,8 @@ public class MediaPlayer2 implements AutoCloseable /** * Sets a single data source as described by a DataSourceDesc which will be played * after current data source is finished. + * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor} + * in the {@link FileDataSourceDesc} will be closed by the player. * * @param dsd the descriptor of data source you want to play after current one * @return a token which can be used to cancel the operation later with {@link #cancelCommand}. @@ -722,7 +732,7 @@ public class MediaPlayer2 implements AutoCloseable void process() { Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null"); synchronized (mSrcLock) { - mNextSourceInfos.clear(); + clearNextSourceInfos(); mNextSourceInfos.add(new SourceInfo(dsd)); } prepareNextDataSource(); @@ -732,6 +742,8 @@ public class MediaPlayer2 implements AutoCloseable /** * Sets a list of data sources to be played sequentially after current data source is done. + * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor} + * in the {@link FileDataSourceDesc} will be closed by the player. * * @param dsds the list of data sources you want to play after current one * @return a token which can be used to cancel the operation later with {@link #cancelCommand}. @@ -744,17 +756,15 @@ public class MediaPlayer2 implements AutoCloseable if (dsds == null || dsds.size() == 0) { throw new IllegalArgumentException("data source list cannot be null or empty."); } - for (DataSourceDesc dsd : dsds) { - if (dsd == null) { - throw new IllegalArgumentException( - "DataSourceDesc in the source list cannot be null."); - } - } synchronized (mSrcLock) { - mNextSourceInfos.clear(); + clearNextSourceInfos(); for (DataSourceDesc dsd : dsds) { - mNextSourceInfos.add(new SourceInfo(dsd)); + if (dsd != null) { + mNextSourceInfos.add(new SourceInfo(dsd)); + } else { + Log.w(TAG, "DataSourceDesc in the source list shall not be null."); + } } } prepareNextDataSource(); @@ -771,7 +781,7 @@ public class MediaPlayer2 implements AutoCloseable return addTask(new Task(CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES, false) { @Override void process() { - mNextSourceInfos.clear(); + clearNextSourceInfos(); } }); } @@ -795,14 +805,14 @@ public class MediaPlayer2 implements AutoCloseable CallbackDataSourceDesc cbDSD = (CallbackDataSourceDesc) dsd; handleDataSource(isCurrent, srcId, - cbDSD.getMedia2DataSource(), + cbDSD.getDataSourceCallback(), cbDSD.getStartPosition(), cbDSD.getEndPosition()); } else if (dsd instanceof FileDataSourceDesc) { FileDataSourceDesc fileDSD = (FileDataSourceDesc) dsd; handleDataSource(isCurrent, srcId, - fileDSD.getFileDescriptor(), + fileDSD.getParcelFileDescriptor(), fileDSD.getOffset(), fileDSD.getLength(), fileDSD.getStartPosition(), @@ -886,7 +896,7 @@ public class MediaPlayer2 implements AutoCloseable if (afd.getDeclaredLength() < 0) { handleDataSource(isCurrent, srcId, - afd.getFileDescriptor(), + ParcelFileDescriptor.dup(afd.getFileDescriptor()), 0, DataSourceDesc.LONG_MAX, startPos, @@ -894,7 +904,7 @@ public class MediaPlayer2 implements AutoCloseable } else { handleDataSource(isCurrent, srcId, - afd.getFileDescriptor(), + ParcelFileDescriptor.dup(afd.getFileDescriptor()), afd.getStartOffset(), afd.getDeclaredLength(), startPos, @@ -960,7 +970,8 @@ public class MediaPlayer2 implements AutoCloseable if (file.exists()) { FileInputStream is = new FileInputStream(file); FileDescriptor fd = is.getFD(); - handleDataSource(isCurrent, srcId, fd, 0, DataSourceDesc.LONG_MAX, startPos, endPos); + handleDataSource(isCurrent, srcId, ParcelFileDescriptor.dup(fd), + 0, DataSourceDesc.LONG_MAX, startPos, endPos); is.close(); } else { throw new IOException("handleDataSource failed."); @@ -984,9 +995,10 @@ public class MediaPlayer2 implements AutoCloseable */ private void handleDataSource( boolean isCurrent, long srcId, - FileDescriptor fd, long offset, long length, + ParcelFileDescriptor pfd, long offset, long length, long startPos, long endPos) throws IOException { - nativeHandleDataSourceFD(isCurrent, srcId, fd, offset, length, startPos, endPos); + nativeHandleDataSourceFD(isCurrent, srcId, pfd.getFileDescriptor(), offset, length, + startPos, endPos); } private native void nativeHandleDataSourceFD(boolean isCurrent, long srcId, @@ -995,15 +1007,15 @@ public class MediaPlayer2 implements AutoCloseable /** * @throws IllegalStateException if it is called in an invalid state - * @throws IllegalArgumentException if dataSource is not a valid Media2DataSource + * @throws IllegalArgumentException if dataSource is not a valid DataSourceCallback */ - private void handleDataSource(boolean isCurrent, long srcId, Media2DataSource dataSource, + private void handleDataSource(boolean isCurrent, long srcId, DataSourceCallback dataSource, long startPos, long endPos) { nativeHandleDataSourceCallback(isCurrent, srcId, dataSource, startPos, endPos); } private native void nativeHandleDataSourceCallback( - boolean isCurrent, long srcId, Media2DataSource dataSource, + boolean isCurrent, long srcId, DataSourceCallback dataSource, long startPos, long endPos); // return true if there is a next data source, false otherwise. @@ -1037,7 +1049,10 @@ public class MediaPlayer2 implements AutoCloseable MEDIA_ERROR, MEDIA_ERROR_IO, MEDIA_ERROR_UNKNOWN, null); mTaskHandler.handleMessage(msg, nextSource.mId); - mNextSourceInfos.poll(); + SourceInfo nextSourceInfo = mNextSourceInfos.poll(); + if (nextSource != null) { + nextSourceInfo.close(); + } return prepareNextDataSource(); } } @@ -1058,7 +1073,7 @@ public class MediaPlayer2 implements AutoCloseable SourceInfo nextSourceInfo = mNextSourceInfos.peek(); if (nextSourceInfo.mStateAsNextSource == NEXT_SOURCE_STATE_PREPARED) { // Switch to next source only when it has been prepared. - mCurrentSourceInfo = mNextSourceInfos.poll(); + setCurrentSourceInfo(mNextSourceInfos.poll()); long srcId = mCurrentSourceInfo.mId; try { @@ -4490,6 +4505,7 @@ public class MediaPlayer2 implements AutoCloseable final DataSourceDesc mDSD; final long mId = mSrcIdGenerator.getAndIncrement(); AtomicInteger mBufferedPercentage = new AtomicInteger(0); + boolean mClosed = false; // m*AsNextSource (below) only applies to pending data sources in the playlist; // the meanings of mCurrentSourceInfo.{mStateAsNextSource,mPlayPendingAsNextSource} @@ -4501,6 +4517,17 @@ public class MediaPlayer2 implements AutoCloseable this.mDSD = dsd; } + void close() { + synchronized (this) { + if (!mClosed) { + if (mDSD != null) { + mDSD.close(); + } + mClosed = true; + } + } + } + @Override public String toString() { return String.format("%s(%d)", SourceInfo.class.getName(), mId); @@ -4531,6 +4558,26 @@ public class MediaPlayer2 implements AutoCloseable return nextSourceInfo != null && nextSourceInfo.mId == srcId; } + private void setCurrentSourceInfo(SourceInfo newSourceInfo) { + synchronized (mSrcLock) { + if (mCurrentSourceInfo != null) { + mCurrentSourceInfo.close(); + } + mCurrentSourceInfo = newSourceInfo; + } + } + + private void clearNextSourceInfos() { + synchronized (mSrcLock) { + for (SourceInfo sourceInfo : mNextSourceInfos) { + if (sourceInfo != null) { + sourceInfo.close(); + } + } + mNextSourceInfos.clear(); + } + } + public static final class MetricsConstants { private MetricsConstants() {} diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java index b6e3276a4e43..5e9eed737256 100644 --- a/media/java/android/media/RemoteController.java +++ b/media/java/android/media/RemoteController.java @@ -20,7 +20,6 @@ import android.annotation.UnsupportedAppUsage; import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; -import android.content.Intent; import android.graphics.Bitmap; import android.media.session.MediaController; import android.media.session.MediaSession; @@ -36,7 +35,6 @@ import android.util.DisplayMetrics; import android.util.Log; import android.view.KeyEvent; -import java.lang.ref.WeakReference; import java.util.List; /** @@ -250,7 +248,7 @@ import java.util.List; * @throws IllegalArgumentException */ public boolean sendMediaKeyEvent(KeyEvent keyEvent) throws IllegalArgumentException { - if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) { + if (!KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) { throw new IllegalArgumentException("not a media key event"); } synchronized (mInfoLock) { diff --git a/media/java/android/media/UriDataSourceDesc.java b/media/java/android/media/UriDataSourceDesc.java index d4a43a1d1c41..6a83dab14aa4 100644 --- a/media/java/android/media/UriDataSourceDesc.java +++ b/media/java/android/media/UriDataSourceDesc.java @@ -31,9 +31,11 @@ import java.util.Map; /** * @hide - * Structure for data source descriptor. + * Structure of data source descriptor for sources using URI. * - * Used by {@link MediaPlayer2#setDataSource(UriDataSourceDesc)} + * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}, + * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and + * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)} * to set data source for playback. * * <p>Users should use {@link Builder} to change {@link UriDataSourceDesc}. diff --git a/media/java/android/media/midi/MidiOutputPort.java b/media/java/android/media/midi/MidiOutputPort.java index 511f6cd51917..5411e669f14d 100644 --- a/media/java/android/media/midi/MidiOutputPort.java +++ b/media/java/android/media/midi/MidiOutputPort.java @@ -59,6 +59,8 @@ public final class MidiOutputPort extends MidiSender implements Closeable { // read next event int count = mInputStream.read(buffer); if (count < 0) { + // This is the exit condition as read() returning <0 indicates + // that the pipe has been closed. break; // FIXME - inform receivers here? } @@ -81,10 +83,15 @@ public final class MidiOutputPort extends MidiSender implements Closeable { Log.e(TAG, "Unknown packet type " + packetType); break; } - } + } // while (true) } catch (IOException e) { // FIXME report I/O failure? - Log.e(TAG, "read failed", e); + // TODO: The comment above about the exit condition is not currently working + // as intended. The read from the closed pipe is throwing an error rather than + // returning <0, so this becomes (probably) not an error, but the exit case. + // This warrants further investigation; + // Silence the (probably) spurious error message. + // Log.e(TAG, "read failed", e); } finally { IoUtils.closeQuietly(mInputStream); } diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index c4b82c3141d3..b457357600fe 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -149,7 +149,7 @@ public final class MediaController { if (keyEvent == null) { throw new IllegalArgumentException("KeyEvent may not be null"); } - if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) { + if (!KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) { return false; } try { diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index d9017b4fb14c..ff6977914ac3 100644 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -59,9 +59,7 @@ import com.android.internal.os.SomeArgs; import com.android.internal.util.Preconditions; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; /** * The TvInputService class represents a TV input or source such as HDMI or built-in tuner which @@ -1400,7 +1398,7 @@ public abstract class TvInputService extends Service { // ViewRootImpl always consumes the keys. In this case, the application loses // a chance to handle media keys. Therefore, media keys are not dispatched to // ViewRootImpl. - skipDispatchToOverlayView = KeyEvent.isMediaKey(keyEvent.getKeyCode()) + skipDispatchToOverlayView = KeyEvent.isMediaSessionKey(keyEvent.getKeyCode()) || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK; } else if (event instanceof MotionEvent) { MotionEvent motionEvent = (MotionEvent) event; diff --git a/media/jni/Android.bp b/media/jni/Android.bp index faf4301a7245..e25e6a5e735f 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -88,7 +88,7 @@ cc_library_shared { name: "libmedia2_jni", srcs: [ - "android_media_Media2DataSource.cpp", + "android_media_DataSourceCallback.cpp", "android_media_MediaMetricsJNI.cpp", "android_media_MediaPlayer2.cpp", "android_media_SyncParams.cpp", diff --git a/media/jni/android_media_Media2DataSource.cpp b/media/jni/android_media_DataSourceCallback.cpp index b3434e9ab8ea..c91d4095a32f 100644 --- a/media/jni/android_media_Media2DataSource.cpp +++ b/media/jni/android_media_DataSourceCallback.cpp @@ -15,10 +15,10 @@ */ //#define LOG_NDEBUG 0 -#define LOG_TAG "JMedia2DataSource-JNI" +#define LOG_TAG "JDataSourceCallback-JNI" #include <utils/Log.h> -#include "android_media_Media2DataSource.h" +#include "android_media_DataSourceCallback.h" #include "log/log.h" #include "jni.h" @@ -33,14 +33,14 @@ namespace android { static const size_t kBufferSize = 64 * 1024; -JMedia2DataSource::JMedia2DataSource(JNIEnv* env, jobject source) +JDataSourceCallback::JDataSourceCallback(JNIEnv* env, jobject source) : mJavaObjStatus(OK), mSizeIsCached(false), mCachedSize(0) { - mMedia2DataSourceObj = env->NewGlobalRef(source); - CHECK(mMedia2DataSourceObj != NULL); + mDataSourceCallbackObj = env->NewGlobalRef(source); + CHECK(mDataSourceCallbackObj != NULL); - ScopedLocalRef<jclass> media2DataSourceClass(env, env->GetObjectClass(mMedia2DataSourceObj)); + ScopedLocalRef<jclass> media2DataSourceClass(env, env->GetObjectClass(mDataSourceCallbackObj)); CHECK(media2DataSourceClass.get() != NULL); mReadAtMethod = env->GetMethodID(media2DataSourceClass.get(), "readAt", "(J[BII)I"); @@ -55,17 +55,17 @@ JMedia2DataSource::JMedia2DataSource(JNIEnv* env, jobject source) CHECK(mByteArrayObj != NULL); } -JMedia2DataSource::~JMedia2DataSource() { +JDataSourceCallback::~JDataSourceCallback() { JNIEnv* env = JavaVMHelper::getJNIEnv(); - env->DeleteGlobalRef(mMedia2DataSourceObj); + env->DeleteGlobalRef(mDataSourceCallbackObj); env->DeleteGlobalRef(mByteArrayObj); } -status_t JMedia2DataSource::initCheck() const { +status_t JDataSourceCallback::initCheck() const { return OK; } -ssize_t JMedia2DataSource::readAt(off64_t offset, void *data, size_t size) { +ssize_t JDataSourceCallback::readAt(off64_t offset, void *data, size_t size) { Mutex::Autolock lock(mLock); if (mJavaObjStatus != OK) { @@ -76,7 +76,7 @@ ssize_t JMedia2DataSource::readAt(off64_t offset, void *data, size_t size) { } JNIEnv* env = JavaVMHelper::getJNIEnv(); - jint numread = env->CallIntMethod(mMedia2DataSourceObj, mReadAtMethod, + jint numread = env->CallIntMethod(mDataSourceCallbackObj, mReadAtMethod, (jlong)offset, mByteArrayObj, (jint)0, (jint)size); if (env->ExceptionCheck()) { ALOGW("An exception occurred in readAt()"); @@ -106,7 +106,7 @@ ssize_t JMedia2DataSource::readAt(off64_t offset, void *data, size_t size) { return numread; } -status_t JMedia2DataSource::getSize(off64_t* size) { +status_t JDataSourceCallback::getSize(off64_t* size) { Mutex::Autolock lock(mLock); if (mJavaObjStatus != OK) { @@ -118,7 +118,7 @@ status_t JMedia2DataSource::getSize(off64_t* size) { } JNIEnv* env = JavaVMHelper::getJNIEnv(); - *size = env->CallLongMethod(mMedia2DataSourceObj, mGetSizeMethod); + *size = env->CallLongMethod(mDataSourceCallbackObj, mGetSizeMethod); if (env->ExceptionCheck()) { ALOGW("An exception occurred in getSize()"); jniLogException(env, ANDROID_LOG_WARN, LOG_TAG); @@ -139,20 +139,20 @@ status_t JMedia2DataSource::getSize(off64_t* size) { return OK; } -void JMedia2DataSource::close() { +void JDataSourceCallback::close() { Mutex::Autolock lock(mLock); JNIEnv* env = JavaVMHelper::getJNIEnv(); - env->CallVoidMethod(mMedia2DataSourceObj, mCloseMethod); + env->CallVoidMethod(mDataSourceCallbackObj, mCloseMethod); // The closed state is effectively the same as an error state. mJavaObjStatus = UNKNOWN_ERROR; } -String8 JMedia2DataSource::toString() { - return String8::format("JMedia2DataSource(pid %d, uid %d)", getpid(), getuid()); +String8 JDataSourceCallback::toString() { + return String8::format("JDataSourceCallback(pid %d, uid %d)", getpid(), getuid()); } -String8 JMedia2DataSource::getMIMEType() const { +String8 JDataSourceCallback::getMIMEType() const { return String8("application/octet-stream"); } diff --git a/media/jni/android_media_Media2DataSource.h b/media/jni/android_media_DataSourceCallback.h index dc085f3f90d1..5bde682754f3 100644 --- a/media/jni/android_media_Media2DataSource.h +++ b/media/jni/android_media_DataSourceCallback.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef _ANDROID_MEDIA_MEDIA2DATASOURCE_H_ -#define _ANDROID_MEDIA_MEDIA2DATASOURCE_H_ +#ifndef _ANDROID_MEDIA_DATASOURCECALLBACK_H_ +#define _ANDROID_MEDIA_DATASOURCECALLBACK_H_ #include "jni.h" @@ -26,16 +26,16 @@ namespace android { -// The native counterpart to a Java android.media.Media2DataSource. It inherits from +// The native counterpart to a Java android.media.DataSourceCallback. It inherits from // DataSource. // // If the java DataSource returns an error or throws an exception it // will be considered to be in a broken state, and the only further call this // will make is to close(). -class JMedia2DataSource : public DataSource { +class JDataSourceCallback : public DataSource { public: - JMedia2DataSource(JNIEnv *env, jobject source); - virtual ~JMedia2DataSource(); + JDataSourceCallback(JNIEnv *env, jobject source); + virtual ~JDataSourceCallback(); virtual status_t initCheck() const override; virtual ssize_t readAt(off64_t offset, void *data, size_t size) override; @@ -56,15 +56,15 @@ private: bool mSizeIsCached; off64_t mCachedSize; - jobject mMedia2DataSourceObj; + jobject mDataSourceCallbackObj; jmethodID mReadAtMethod; jmethodID mGetSizeMethod; jmethodID mCloseMethod; jbyteArray mByteArrayObj; - DISALLOW_EVIL_CONSTRUCTORS(JMedia2DataSource); + DISALLOW_EVIL_CONSTRUCTORS(JDataSourceCallback); }; } // namespace android -#endif // _ANDROID_MEDIA_MEDIA2DATASOURCE_H_ +#endif // _ANDROID_MEDIA_DATASOURCECALLBACK_H_ diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp index f7de2e78e822..456749279696 100644 --- a/media/jni/android_media_MediaPlayer2.cpp +++ b/media/jni/android_media_MediaPlayer2.cpp @@ -46,7 +46,7 @@ #include "utils/KeyedVector.h" #include "utils/String8.h" #include "android_media_BufferingParams.h" -#include "android_media_Media2DataSource.h" +#include "android_media_DataSourceCallback.h" #include "android_media_MediaMetricsJNI.h" #include "android_media_PlaybackParams.h" #include "android_media_SyncParams.h" @@ -423,7 +423,7 @@ android_media_MediaPlayer2_handleDataSourceCallback( jniThrowException(env, "java/lang/IllegalArgumentException", NULL); return; } - sp<DataSource> callbackDataSource = new JMedia2DataSource(env, dataSource); + sp<DataSource> callbackDataSource = new JDataSourceCallback(env, dataSource); sp<DataSourceDesc> dsd = new DataSourceDesc(); dsd->mId = srcId; dsd->mType = DataSourceDesc::TYPE_CALLBACK; @@ -1390,7 +1390,7 @@ static const JNINativeMethod gMethods[] = { }, { "nativeHandleDataSourceCallback", - "(ZJLandroid/media/Media2DataSource;JJ)V", + "(ZJLandroid/media/DataSourceCallback;JJ)V", (void *)android_media_MediaPlayer2_handleDataSourceCallback }, {"nativePlayNextDataSource", "(J)V", (void *)android_media_MediaPlayer2_playNextDataSource}, diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index e7e8384c5567..207508e18a8b 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -144,6 +144,7 @@ LIBANDROID { AHardwareBuffer_describe; # introduced=26 AHardwareBuffer_fromHardwareBuffer; # introduced=26 AHardwareBuffer_getNativeHandle; # introduced=26 + AHardwareBuffer_isSupported; # introduced=29 AHardwareBuffer_lock; # introduced=26 AHardwareBuffer_recvHandleFromUnixSocket; # introduced=26 AHardwareBuffer_release; # introduced=26 diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java new file mode 100644 index 000000000000..5bf30ca10694 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java @@ -0,0 +1,72 @@ +/* + * 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.systemui.statusbar.car; + +import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE; + +import android.content.Context; +import android.content.res.TypedArray; +import android.os.Bundle; +import android.util.AttributeSet; +import android.util.Log; + +import com.android.internal.app.AssistUtils; +import com.android.internal.app.IVoiceInteractionSessionShowCallback; + +/** + * AssitantButton is a ui component that will trigger the Voice Interaction Service. + */ +public class AssitantButton extends CarFacetButton { + + private static final String TAG = "CarFacetButton"; + private IVoiceInteractionSessionShowCallback mShowCallback = + new IVoiceInteractionSessionShowCallback.Stub() { + @Override + public void onFailed() { + Log.w(TAG, "Failed to show VoiceInteractionSession"); + } + + @Override + public void onShown() { + Log.d(TAG, "IVoiceInteractionSessionShowCallback onShown()"); + } + }; + + private static final String EXTRA_CAR_PUSH_TO_TALK = + "com.android.car.input.EXTRA_CAR_PUSH_TO_TALK"; + private final AssistUtils mAssistUtils; + + public AssitantButton(Context context, AttributeSet attrs) { + super(context, attrs); + mAssistUtils = new AssistUtils(context); + setOnClickListener(v -> { + showAssistant(); + }); + } + + private void showAssistant() { + final Bundle args = new Bundle(); + args.putBoolean(EXTRA_CAR_PUSH_TO_TALK, true); + mAssistUtils.showSessionForActiveService(args, + SHOW_SOURCE_ASSIST_GESTURE, mShowCallback, /*activityToken=*/ null); + } + + @Override + protected void setupIntents(TypedArray typedArray){ + // left blank because for the assistant button Intent will not be passed from the layout. + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java new file mode 100644 index 000000000000..cea4ab0e4992 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java @@ -0,0 +1,204 @@ +/* + * 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.systemui.statusbar.car; + +import android.content.Context; +import android.content.Intent; +import android.content.res.TypedArray; +import android.os.UserHandle; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import com.android.keyguard.AlphaOptimizedImageButton; +import com.android.systemui.Dependency; +import com.android.systemui.R; + +/** + * CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined + * category. It can also render a indicator impling that there are more options of apps to launch + * using this component. This is done with a "More icon" currently an arrow as defined in the layout + * file. The class is to serve as an example. + * Usage example: A button that allows a user to select a music app and indicate that there are + * other music apps installed. + */ +public class CarFacetButton extends LinearLayout { + private static final String FACET_FILTER_DELIMITER = ";"; + /** + * Extra information to be sent to a helper to make the decision of what app to launch when + * clicked. + */ + private static final String EXTRA_FACET_CATEGORIES = "categories"; + private static final String EXTRA_FACET_PACKAGES = "packages"; + private static final String EXTRA_FACET_ID = "filter_id"; + private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker"; + private static final String TAG = "CarFacetButton"; + + private Context mContext; + private AlphaOptimizedImageButton mIcon; + private AlphaOptimizedImageButton mMoreIcon; + private boolean mSelected = false; + private String[] mComponentNames; + /** App categories that are to be used with this widget */ + private String[] mFacetCategories; + /** App packages that are allowed to be used with this widget */ + private String[] mFacetPackages; + private int mIconResourceId; + /** + * If defined in the xml this will be the icon that's rendered when the button is marked as + * selected + */ + private int mSelectedIconResourceId; + private boolean mUseMoreIcon = true; + private float mSelectedAlpha = 1f; + private float mUnselectedAlpha = 1f; + + public CarFacetButton(Context context, AttributeSet attrs) { + super(context, attrs); + mContext = context; + View.inflate(context, R.layout.car_facet_button, this); + // extract custom attributes + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton); + setupIntents(typedArray); + setupIcons(typedArray); + CarFacetButtonController carFacetButtonController = Dependency.get( + CarFacetButtonController.class); + carFacetButtonController.addFacetButton(this); + } + + /** + * Reads the custom attributes to setup click handlers for this component. + */ + protected void setupIntents(TypedArray typedArray) { + String intentString = typedArray.getString(R.styleable.CarFacetButton_intent); + String longPressIntentString = typedArray.getString(R.styleable.CarFacetButton_longIntent); + String categoryString = typedArray.getString(R.styleable.CarFacetButton_categories); + String packageString = typedArray.getString(R.styleable.CarFacetButton_packages); + String componentNameString = + typedArray.getString(R.styleable.CarFacetButton_componentNames); + try { + final Intent intent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME); + intent.putExtra(EXTRA_FACET_ID, Integer.toString(getId())); + + if (packageString != null) { + mFacetPackages = packageString.split(FACET_FILTER_DELIMITER); + intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages); + } + if (categoryString != null) { + mFacetCategories = categoryString.split(FACET_FILTER_DELIMITER); + intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories); + } + if (componentNameString != null) { + mComponentNames = componentNameString.split(FACET_FILTER_DELIMITER); + } + + setOnClickListener(v -> { + intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected); + mContext.startActivityAsUser(intent, UserHandle.CURRENT); + }); + + if (longPressIntentString != null) { + final Intent longPressIntent = Intent.parseUri(longPressIntentString, + Intent.URI_INTENT_SCHEME); + setOnLongClickListener(v -> { + mContext.startActivityAsUser(longPressIntent, UserHandle.CURRENT); + return true; + }); + } + } catch (Exception e) { + throw new RuntimeException("Failed to attach intent", e); + } + } + + private void setupIcons(TypedArray styledAttributes) { + mSelectedAlpha = styledAttributes.getFloat( + R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha); + mUnselectedAlpha = styledAttributes.getFloat( + R.styleable.CarFacetButton_unselectedAlpha, mUnselectedAlpha); + mIcon = findViewById(R.id.car_nav_button_icon); + mIcon.setScaleType(ImageView.ScaleType.CENTER); + mIcon.setClickable(false); + mIcon.setAlpha(mUnselectedAlpha); + mIconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0); + mIcon.setImageResource(mIconResourceId); + mSelectedIconResourceId = styledAttributes.getResourceId( + R.styleable.CarFacetButton_selectedIcon, mIconResourceId); + + mMoreIcon = findViewById(R.id.car_nav_button_more_icon); + mMoreIcon.setClickable(false); + mMoreIcon.setAlpha(mSelectedAlpha); + mMoreIcon.setVisibility(GONE); + mUseMoreIcon = styledAttributes.getBoolean(R.styleable.CarFacetButton_useMoreIcon, true); + } + + /** + * @return The app categories the component represents + */ + public String[] getCategories() { + if (mFacetCategories == null) { + return new String[0]; + } + return mFacetCategories; + } + + /** + * @return The valid packages that should be considered. + */ + public String[] getFacetPackages() { + if (mFacetPackages == null) { + return new String[0]; + } + return mFacetPackages; + } + + /** + * @return The list of component names. + */ + public String[] getComponentName() { + if (mComponentNames == null) { + return new String[0]; + } + return mComponentNames; + } + + /** + * Updates the alpha of the icons to "selected" and shows the "More icon" + * + * @param selected true if the view must be selected, false otherwise + */ + public void setSelected(boolean selected) { + super.setSelected(selected); + setSelected(selected, selected); + } + + /** + * Updates the visual state to let the user know if it's been selected. + * + * @param selected true if should update the alpha of the icon to selected, false otherwise + * @param showMoreIcon true if the "more icon" should be shown, false otherwise. Note this + * is ignored if the attribute useMoreIcon is set to false + */ + public void setSelected(boolean selected, boolean showMoreIcon) { + mSelected = selected; + mIcon.setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha); + mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId); + if (mUseMoreIcon) { + mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE); + } + } +} diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java index 22d0e3b7bdc9..4b212c25c89e 100644 --- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java +++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java @@ -378,6 +378,14 @@ public class Assistant extends NotificationAssistantService { } @Override + public void onActionClicked(String key, Notification.Action action, int source) { + if (DEBUG) { + Log.d(TAG, "onActionClicked() called with: key = [" + key + "], action = [" + action.title + + "], source = [" + source + "]"); + } + } + + @Override public void onListenerConnected() { if (DEBUG) Log.i(TAG, "CONNECTED"); try { diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml index 06782633a4de..789d185b8b42 100644 --- a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml +++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml @@ -72,7 +72,6 @@ </LinearLayout> <LinearLayout - android:id="@+id/entity_header_links" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_centerVertical="true" @@ -85,6 +84,7 @@ android:layout_width="wrap_content" android:layout_weight="1" android:layout_height="0dp" + android:visibility="gone" android:minWidth="24dp" android:src="@null" android:tint="?android:attr/colorAccent"/> @@ -95,6 +95,7 @@ android:layout_width="wrap_content" android:layout_weight="1" android:layout_height="0dp" + android:visibility="gone" android:minWidth="24dp" android:src="@null" android:tint="?android:attr/colorAccent"/> diff --git a/packages/SettingsLib/tests/robotests/Android.mk b/packages/SettingsLib/tests/robotests/Android.mk index d15a3ef2946d..cfa067f13680 100644 --- a/packages/SettingsLib/tests/robotests/Android.mk +++ b/packages/SettingsLib/tests/robotests/Android.mk @@ -32,12 +32,13 @@ include frameworks/base/packages/SettingsLib/common.mk include $(BUILD_PACKAGE) -############################################# -# SettingsLib Robolectric test target. # -############################################# +############################################################ +# SettingsLib Robolectric test target. # +############################################################ include $(CLEAR_VARS) LOCAL_MODULE := SettingsLibRoboTests +LOCAL_MODULE_CLASS := JAVA_LIBRARIES LOCAL_SRC_FILES := $(call all-java-files-under, src) @@ -53,6 +54,9 @@ LOCAL_INSTRUMENTATION_FOR := SettingsLibShell LOCAL_MODULE_TAGS := optional +# Generate test_config.properties +include external/robolectric-shadows/gen_test_config.mk + include $(BUILD_STATIC_JAVA_LIBRARY) ############################################################# diff --git a/packages/SettingsLib/tests/robotests/config/robolectric.properties b/packages/SettingsLib/tests/robotests/config/robolectric.properties index 6b5b8e59472b..fab7251d020b 100644 --- a/packages/SettingsLib/tests/robotests/config/robolectric.properties +++ b/packages/SettingsLib/tests/robotests/config/robolectric.properties @@ -1,5 +1 @@ -manifest=frameworks/base/packages/SettingsLib/tests/robotests/AndroidManifest.xml sdk=NEWEST_SDK - -shadows=\ - com.android.settingslib.testutils.shadow.ShadowXmlUtils
\ No newline at end of file diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java index 9ba996752f49..3a4e2e403ee0 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java @@ -30,10 +30,11 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.util.ReflectionHelpers; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class CustomEditTextPreferenceComaptTest { @Mock @@ -70,7 +71,7 @@ public class CustomEditTextPreferenceComaptTest { } private static class TestPreference extends CustomEditTextPreferenceCompat { - public TestPreference(Context context) { + private TestPreference(Context context) { super(context); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java index 9d7f59a78fa5..e94a06ce7f6d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java @@ -30,10 +30,11 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.util.ReflectionHelpers; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class CustomEditTextPreferenceTest { @Mock @@ -70,7 +71,7 @@ public class CustomEditTextPreferenceTest { } private static class TestPreference extends CustomEditTextPreference { - public TestPreference(Context context) { + private TestPreference(Context context) { super(context); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java index 19a916cf85da..4e8af7350f8a 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java @@ -24,9 +24,10 @@ import android.system.StructUtsname; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class DeviceInfoUtilsTest { private Context mContext; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java index 36b70dfe2297..4d76331d8da7 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java @@ -18,12 +18,13 @@ package com.android.settingslib; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.R; import android.app.Activity; import android.content.Context; import android.content.Intent; @@ -36,20 +37,19 @@ import android.content.res.TypedArray; import android.provider.Settings; import android.view.MenuItem; -import android.R; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; /** * Tests for {@link HelpUtils}. */ -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class HelpUtilsTest { private static final String TEST_HELP_URL = "intent:#Intent;action=com.android.test;end"; private static final String PACKAGE_NAME_KEY = "package-name-key"; @@ -83,8 +83,6 @@ public class HelpUtilsTest { when(mContext.getResources().getString(R.string.config_feedbackIntentNameKey)) .thenReturn(FEEDBACK_INTENT_NAME_KEY); when(mActivity.getPackageManager()).thenReturn(mPackageManager); - - } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java index 88ac8ce5fae5..2b5a4e069001 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java @@ -25,8 +25,8 @@ import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; @@ -44,11 +44,12 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import java.util.Arrays; import java.util.Collections; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class RestrictedLockUtilsTest { @Mock @@ -178,8 +179,7 @@ public class RestrictedLockUtilsTest { public void checkIfKeyguardFeaturesAreDisabled_doesMatchAllowedFeature_unifiedManagedProfile() { UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1}); UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2}); - when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] { - userInfo, profileInfo})); + when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo)); when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId)) .thenReturn(KEYGUARD_DISABLE_FEATURES_NONE); @@ -207,8 +207,7 @@ public class RestrictedLockUtilsTest { public void checkIfKeyguardFeaturesAreDisabled_notMatchOtherFeatures_unifiedManagedProfile() { UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1}); UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2}); - when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] { - userInfo, profileInfo})); + when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo)); when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId)) .thenReturn(KEYGUARD_DISABLE_FEATURES_NONE); @@ -231,8 +230,7 @@ public class RestrictedLockUtilsTest { public void checkIfKeyguardFeaturesAreDisabled_onlyMatchesProfile_separateManagedProfile() { UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1}); UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2}); - when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] { - userInfo, profileInfo})); + when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo)); when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId)) .thenReturn(KEYGUARD_DISABLE_FEATURES_NONE); @@ -268,8 +266,7 @@ public class RestrictedLockUtilsTest { public void checkIfKeyguardFeaturesAreDisabled_onlyMatchesParent_profileParentPolicy() { UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1}); UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2}); - when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] { - userInfo, profileInfo})); + when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo)); when(mProxy.getParentProfileInstance(any(DevicePolicyManager.class), any()) .getKeyguardDisabledFeatures(mAdmin2, mProfileId)) diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java index 79d682d67a4a..1b10c736f266 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java @@ -16,7 +16,6 @@ package com.android.settingslib; - import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -35,8 +34,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class RestrictedPreferenceHelperTest { @Mock diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java deleted file mode 100644 index 8757eed8b746..000000000000 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.settingslib; - -import android.annotation.NonNull; - -import org.junit.runners.model.InitializationError; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; -import org.robolectric.manifest.AndroidManifest; -import org.robolectric.res.Fs; -import org.robolectric.res.ResourcePath; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.List; - -public class SettingsLibRobolectricTestRunner extends RobolectricTestRunner { - - public SettingsLibRobolectricTestRunner(Class<?> testClass) throws InitializationError { - super(testClass); - } - - /** - * We are going to create our own custom manifest so we can add multiple resource paths to it. - */ - @Override - protected AndroidManifest getAppManifest(Config config) { - try { - // Using the manifest file's relative path, we can figure out the application directory. - final URL appRoot = - new URL("file:frameworks/base/packages/SettingsLib/tests/robotests"); - final URL manifestPath = new URL(appRoot, "AndroidManifest.xml"); - final URL resDir = new URL(appRoot, "res"); - final URL assetsDir = new URL(appRoot, "assets"); - - return new AndroidManifest(Fs.fromURL(manifestPath), Fs.fromURL(resDir), - Fs.fromURL(assetsDir), "com.android.settingslib") { - @Override - public List<ResourcePath> getIncludedResourcePaths() { - final List<ResourcePath> paths = super.getIncludedResourcePaths(); - 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/")); - return paths; - } - }; - } catch (MalformedURLException e) { - throw new RuntimeException("SettingsLibRobolectricTestRunner failure", e); - } - } - - private static ResourcePath resourcePath(@NonNull String spec) { - try { - return new ResourcePath(null, Fs.fromURL(new URL(spec)), null); - } catch (MalformedURLException e) { - throw new RuntimeException("SettingsLibRobolectricTestRunner failure", e); - } - } -} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java index e70baa197123..0ca779162ef2 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java @@ -32,12 +32,13 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import java.util.ArrayList; import java.util.List; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class TetherUtilTest { private Context mContext; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java index c0b69f2260eb..3f0ba13ce50a 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java @@ -36,9 +36,10 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class TwoTargetPreferenceTest { private PreferenceViewHolder mViewHolder; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java index 08a75ab3cfd5..594d767675c8 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java @@ -49,6 +49,7 @@ import org.mockito.ArgumentMatcher; import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; @@ -58,10 +59,8 @@ import org.robolectric.shadows.ShadowSettings; import java.util.HashMap; import java.util.Map; -@RunWith(SettingsLibRobolectricTestRunner.class) -@Config(shadows = { - UtilsTest.ShadowSecure.class, - UtilsTest.ShadowLocationManager.class}) +@RunWith(RobolectricTestRunner.class) +@Config(shadows = {UtilsTest.ShadowSecure.class, UtilsTest.ShadowLocationManager.class}) public class UtilsTest { private static final double[] TEST_PERCENTAGES = {0, 0.4, 0.5, 0.6, 49, 49.3, 49.8, 50, 100}; private static final String PERCENTAGE_0 = "0%"; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java index 152d024d0155..44fdaec49f73 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java @@ -23,14 +23,13 @@ import android.content.Context; import android.os.UserHandle; import android.provider.Settings; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class AccessibilityUtilsTest { private Context mContext; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java index b307b4730fa1..ccec175aefad 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java @@ -41,7 +41,6 @@ import android.os.Handler; import android.os.UserHandle; import android.util.IconDrawableFactory; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.applications.ApplicationsState.Callbacks; import com.android.settingslib.applications.ApplicationsState.Session; @@ -55,6 +54,7 @@ import org.mockito.ArgumentMatchers; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; @@ -67,7 +67,7 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowUserManager.class, ApplicationsStateRoboTest.ShadowIconDrawableFactory.class, ApplicationsStateRoboTest.ShadowPackageManager.class}) diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java index a92a2dd8c11a..50fad70f0a0e 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java @@ -18,8 +18,8 @@ package com.android.settingslib.applications; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -32,16 +32,15 @@ import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class DefaultAppInfoTest { @Mock diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java index d8c459c07b75..f7fd25b9fb7d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java @@ -26,14 +26,13 @@ import static org.mockito.Mockito.verify; import android.content.ComponentName; import android.provider.Settings; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class ServiceListingTest { private static final String TEST_SETTING = "testSetting"; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java index 29831a89027a..c555cbec4bab 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java @@ -17,8 +17,8 @@ package com.android.settingslib.bluetooth; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -32,7 +32,6 @@ import android.content.Context; import android.content.res.Resources; import com.android.settingslib.R; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; import org.junit.Before; @@ -40,26 +39,27 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowBluetoothAdapter.class}) public class A2dpProfileTest { @Mock - Context mContext; + private Context mContext; @Mock - CachedBluetoothDeviceManager mDeviceManager; + private CachedBluetoothDeviceManager mDeviceManager; @Mock - LocalBluetoothProfileManager mProfileManager; + private LocalBluetoothProfileManager mProfileManager; @Mock - BluetoothDevice mDevice; + private BluetoothDevice mDevice; @Mock - BluetoothA2dp mBluetoothA2dp; - BluetoothProfile.ServiceListener mServiceListener; + private BluetoothA2dp mBluetoothA2dp; + private BluetoothProfile.ServiceListener mServiceListener; - A2dpProfile mProfile; + private A2dpProfile mProfile; private ShadowBluetoothAdapter mShadowBluetoothAdapter; @Before diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java index 274fff83ea8a..976445eb8c04 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java @@ -18,18 +18,14 @@ package com.android.settingslib.bluetooth; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothA2dpSink; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothA2dpSink; import android.bluetooth.BluetoothProfile; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; import org.junit.Before; @@ -37,11 +33,12 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.robolectric.annotation.Config; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowBluetoothAdapter.class}) public class A2dpSinkProfileTest { @@ -52,8 +49,6 @@ public class A2dpSinkProfileTest { @Mock private BluetoothA2dpSink mService; @Mock - private CachedBluetoothDevice mCachedBluetoothDevice; - @Mock private BluetoothDevice mBluetoothDevice; private BluetoothProfile.ServiceListener mServiceListener; private A2dpSinkProfile mProfile; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java index c147d5e306c2..27b8dfc28448 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java @@ -29,20 +29,18 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.os.Handler; import android.os.UserHandle; import android.telephony.TelephonyManager; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class BluetoothEventManagerTest { @Mock diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java index 07310bd5746c..0eb6de9584eb 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java @@ -21,14 +21,14 @@ import android.bluetooth.BluetoothDevice; import android.graphics.drawable.Drawable; import com.android.settingslib.R; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.graph.BluetoothDeviceLayerDrawable; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class BluetoothUtilsTest { @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java index 9c7549147217..47b12103e772 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java @@ -28,18 +28,17 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; import org.robolectric.RuntimeEnvironment; import java.util.Collection; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class CachedBluetoothDeviceManagerTest { private final static String DEVICE_NAME_1 = "TestName_1"; private final static String DEVICE_NAME_2 = "TestName_2"; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java index 5ceede1ccf72..4e5d38ab5799 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java @@ -31,16 +31,15 @@ import android.bluetooth.BluetoothProfile; import android.content.Context; import android.media.AudioManager; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class CachedBluetoothDeviceTest { private final static String DEVICE_NAME = "TestName"; private final static String DEVICE_ALIAS = "TestAlias"; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java index c0a1f0cda3ee..9adef8287355 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java @@ -11,7 +11,6 @@ import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfile; import android.content.Context; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; import org.junit.Before; @@ -19,11 +18,12 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowBluetoothAdapter.class}) public class HeadsetProfileTest { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java index cb1b12d04f83..2b5466c4161f 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java @@ -29,16 +29,15 @@ import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; import android.content.Context; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class HearingAidDeviceManagerTest { private final static long HISYNCID1 = 10; private final static long HISYNCID2 = 11; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java index 187be0bf647b..69c020dd5c08 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java @@ -18,18 +18,14 @@ package com.android.settingslib.bluetooth; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadsetClient; import android.bluetooth.BluetoothProfile; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; import org.junit.Before; @@ -37,11 +33,12 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.robolectric.annotation.Config; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowBluetoothAdapter.class}) public class HfpClientProfileTest { @@ -52,8 +49,6 @@ public class HfpClientProfileTest { @Mock private BluetoothHeadsetClient mService; @Mock - private CachedBluetoothDevice mCachedBluetoothDevice; - @Mock private BluetoothDevice mBluetoothDevice; private BluetoothProfile.ServiceListener mServiceListener; private HfpClientProfile mProfile; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java index c91ee22d8587..f38af70c7498 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java @@ -18,18 +18,14 @@ package com.android.settingslib.bluetooth; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHidDevice; import android.bluetooth.BluetoothProfile; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; import org.junit.Before; @@ -37,11 +33,12 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.robolectric.annotation.Config; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowBluetoothAdapter.class}) public class HidDeviceProfileTest { @@ -52,8 +49,6 @@ public class HidDeviceProfileTest { @Mock private BluetoothHidDevice mService; @Mock - private CachedBluetoothDevice mCachedBluetoothDevice; - @Mock private BluetoothDevice mBluetoothDevice; private BluetoothProfile.ServiceListener mServiceListener; private HidDeviceProfile mProfile; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java index a3c3a54c38f0..5d5872ea2354 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java @@ -37,7 +37,6 @@ import android.content.Context; import android.content.Intent; import android.os.ParcelUuid; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; import org.junit.Before; @@ -45,6 +44,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; @@ -52,7 +52,7 @@ import org.robolectric.shadow.api.Shadow; import java.util.ArrayList; import java.util.List; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowBluetoothAdapter.class}) public class LocalBluetoothProfileManagerTest { private final static long HISYNCID = 10; @@ -270,13 +270,13 @@ public class LocalBluetoothProfileManagerTest { verify(mCachedBluetoothDevice).refresh(); } - private List<Integer> generateList(int[] profile) { - if (profile == null) { + private List<Integer> generateList(int[] profiles) { + if (profiles == null) { return null; } - final List<Integer> profileList = new ArrayList<>(profile.length); - for(int i = 0; i < profile.length; i++) { - profileList.add(profile[i]); + final List<Integer> profileList = new ArrayList<>(profiles.length); + for (int profile : profiles) { + profileList.add(profile); } return profileList; } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java index c4c48a8bce8c..6f667094a5aa 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java @@ -18,18 +18,14 @@ package com.android.settingslib.bluetooth; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothMapClient; import android.bluetooth.BluetoothProfile; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; import org.junit.Before; @@ -37,11 +33,12 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.robolectric.annotation.Config; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowBluetoothAdapter.class}) public class MapClientProfileTest { @@ -52,8 +49,6 @@ public class MapClientProfileTest { @Mock private BluetoothMapClient mService; @Mock - private CachedBluetoothDevice mCachedBluetoothDevice; - @Mock private BluetoothDevice mBluetoothDevice; private BluetoothProfile.ServiceListener mServiceListener; private MapClientProfile mProfile; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java index e4a444c836ab..b21ec9c3e52a 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java @@ -18,18 +18,14 @@ package com.android.settingslib.bluetooth; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothPbapClient; import android.bluetooth.BluetoothProfile; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; import org.junit.Before; @@ -37,12 +33,13 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.robolectric.annotation.Config; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; -@RunWith(SettingsLibRobolectricTestRunner.class) -@Config(shadows = {ShadowBluetoothAdapter.class}) +@RunWith(RobolectricTestRunner.class) +@Config(shadows = ShadowBluetoothAdapter.class) public class PbapClientProfileTest { @Mock @@ -52,8 +49,6 @@ public class PbapClientProfileTest { @Mock private BluetoothPbapClient mService; @Mock - private CachedBluetoothDevice mCachedBluetoothDevice; - @Mock private BluetoothDevice mBluetoothDevice; private BluetoothProfile.ServiceListener mServiceListener; private PbapClientProfile mProfile; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java index 9bb53ee6a343..ec880345f6f0 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java @@ -18,18 +18,14 @@ package com.android.settingslib.bluetooth; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothSap; import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothSap; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; import org.junit.Before; @@ -37,11 +33,12 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.robolectric.annotation.Config; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowBluetoothAdapter.class}) public class SapProfileTest { @@ -52,8 +49,6 @@ public class SapProfileTest { @Mock private BluetoothSap mService; @Mock - private CachedBluetoothDevice mCachedBluetoothDevice; - @Mock private BluetoothDevice mBluetoothDevice; private BluetoothProfile.ServiceListener mServiceListener; private SapProfile mProfile; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java index 4d7553cd85da..28de1914838f 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java @@ -24,16 +24,15 @@ import android.content.Context; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class AbstractPreferenceControllerTest { @Mock diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java index 4ec6fb2efab1..8a0ae9190a8c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java @@ -27,7 +27,6 @@ import android.content.Context; import android.content.Intent; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import org.junit.Before; import org.junit.Test; @@ -35,13 +34,14 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.util.ReflectionHelpers; import java.util.ArrayList; import java.util.List; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class MetricsFeatureProviderTest { @Mock private LogWriter mLogWriter; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java index 6285fcdb10b3..8f51dece64e5 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java @@ -17,8 +17,8 @@ package com.android.settingslib.core.instrumentation; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -26,16 +26,15 @@ import android.app.settings.SettingsEnums; import android.content.Context; import android.content.SharedPreferences; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class SharedPreferenceLoggerTest { private static final String TEST_TAG = "tag"; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java index b251c09ff33e..097db176a99a 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java @@ -17,10 +17,10 @@ package com.android.settingslib.core.instrumentation; import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -35,7 +35,6 @@ import android.os.Bundle; import androidx.fragment.app.FragmentActivity; import com.android.internal.logging.nano.MetricsProto; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import org.junit.Before; import org.junit.Test; @@ -43,10 +42,10 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; import org.robolectric.android.controller.ActivityController; - -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class VisibilityLoggerMixinTest { @Mock @@ -139,7 +138,7 @@ public class VisibilityLoggerMixinTest { private final class TestInstrumentable implements Instrumentable { - public static final int TEST_METRIC = 12345; + private static final int TEST_METRIC = 12345; @Override public int getMetricsCategory() { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java index 887c1d57c870..29e37e4938a8 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java @@ -28,7 +28,6 @@ import android.widget.LinearLayout; import androidx.lifecycle.LifecycleOwner; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.core.lifecycle.events.OnAttach; import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu; import com.android.settingslib.core.lifecycle.events.OnDestroy; @@ -43,10 +42,11 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; import org.robolectric.android.controller.ActivityController; import org.robolectric.shadows.androidx.fragment.FragmentController; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class LifecycleTest { private LifecycleOwner mLifecycleOwner; @@ -56,7 +56,7 @@ public class LifecycleTest { final TestObserver mFragObserver; - public TestDialogFragment() { + private TestDialogFragment() { mFragObserver = new TestObserver(); mLifecycle.addObserver(mFragObserver); } @@ -236,11 +236,11 @@ public class LifecycleTest { } private static class OptionItemAccepter implements LifecycleObserver, OnOptionsItemSelected { - public boolean wasCalled = false; + private boolean mWasCalled = false; @Override public boolean onOptionsItemSelected(MenuItem menuItem) { - wasCalled = true; + mWasCalled = true; return false; } } @@ -258,14 +258,14 @@ public class LifecycleTest { fragment.onPrepareOptionsMenu(null); fragment.onOptionsItemSelected(null); - assertThat(accepter.wasCalled).isFalse(); + assertThat(accepter.mWasCalled).isFalse(); } private class OnStartObserver implements LifecycleObserver, OnStart { private final Lifecycle mLifecycle; - public OnStartObserver(Lifecycle lifecycle) { + private OnStartObserver(Lifecycle lifecycle) { mLifecycle = lifecycle; } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java index 9dd93b3af390..6191a00b377c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java @@ -22,16 +22,15 @@ import static org.mockito.Mockito.verify; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class DeveloperOptionsPreferenceControllerTest { private static final String TEST_KEY = "Test_pref_key"; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java index a0fa6b599b45..3475ff7d96f8 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java @@ -18,23 +18,19 @@ package com.android.settingslib.development; import static com.google.common.truth.Truth.assertThat; -import android.os.UserManager; import android.content.Context; +import android.os.UserManager; import android.provider.Settings; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - -import org.robolectric.shadows.ShadowUserManager; -import org.robolectric.shadow.api.Shadow; - -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowUserManager; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class DevelopmentSettingsEnablerTest { private Context mContext; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java index d7b23b0ef636..e84a25c0ba4e 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java @@ -32,17 +32,16 @@ import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreference; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; import org.robolectric.RuntimeEnvironment; import org.robolectric.shadows.ShadowApplication; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class EnableAdbPreferenceControllerTest { @Mock(answer = RETURNS_DEEP_STUBS) private PreferenceScreen mScreen; @@ -150,7 +149,7 @@ public class EnableAdbPreferenceControllerTest { } class ConcreteEnableAdbPreferenceController extends AbstractEnableAdbPreferenceController { - public ConcreteEnableAdbPreferenceController(Context context) { + private ConcreteEnableAdbPreferenceController(Context context) { super(context); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java index 2f78899ff92d..146be23f1683 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java @@ -45,16 +45,16 @@ import androidx.preference.ListPreference; import androidx.preference.PreferenceScreen; import com.android.settingslib.R; -import com.android.settingslib.SettingsLibRobolectricTestRunner; 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; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class LogdSizePreferenceControllerTest { @Mock diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java index ed128e098c6f..d5afb4b08a93 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java @@ -29,7 +29,6 @@ import androidx.preference.ListPreference; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; @@ -37,9 +36,10 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class LogpersistPreferenceControllerTest { private LifecycleOwner mLifecycleOwner; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java index 40db478f2dc7..d1212fcad864 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java @@ -27,16 +27,15 @@ import static org.mockito.Mockito.verify; import android.os.IBinder; import android.os.Parcel; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; +import org.robolectric.RobolectricTestRunner; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class SystemPropPokerTest { @Spy diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java index 234b4d5ac604..16de5f804b68 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java @@ -26,7 +26,6 @@ import android.content.Context; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; @@ -34,11 +33,12 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class BluetoothAddressPreferenceControllerTest { @Mock private Context mContext; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java index aee956cf5518..4444e6369b67 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java @@ -30,7 +30,6 @@ import android.content.Context; import android.content.IntentFilter; import android.os.Handler; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; @@ -39,8 +38,9 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class ConnectivityPreferenceControllerTest { @Mock private Context mContext; @@ -91,8 +91,7 @@ public class ConnectivityPreferenceControllerTest { private static class ConcreteConnectivityPreferenceController extends AbstractConnectivityPreferenceController { - - public ConcreteConnectivityPreferenceController(Context context, + private ConcreteConnectivityPreferenceController(Context context, Lifecycle lifecycle) { super(context, lifecycle); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java index 2b490ee63856..bd223bd778bb 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java @@ -25,12 +25,10 @@ import static org.mockito.Mockito.mock; import android.content.Context; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; -import android.telephony.SubscriptionManager; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; @@ -38,11 +36,10 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.robolectric.annotation.Config; -import org.robolectric.annotation.Implementation; -import org.robolectric.annotation.Implements; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.shadows.ShadowSubscriptionManager; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class ImsStatusPreferenceControllerTest { @Mock private Context mContext; @@ -61,8 +58,9 @@ public class ImsStatusPreferenceControllerTest { } @Test - @Config(shadows = ShadowSubscriptionManager.class) public void testIsAvailable() { + ShadowSubscriptionManager.setDefaultDataSubscriptionId(1234); + CarrierConfigManager carrierConfigManager = mock(CarrierConfigManager.class); doReturn(carrierConfigManager).when(mContext).getSystemService(CarrierConfigManager.class); @@ -92,18 +90,10 @@ public class ImsStatusPreferenceControllerTest { .that(imsStatusPreferenceController.isAvailable()).isFalse(); } - @Implements(SubscriptionManager.class) - public static class ShadowSubscriptionManager { - @Implementation - public static int getDefaultDataSubscriptionId() { - return 1234; - } - } - private static class ConcreteImsStatusPreferenceController extends AbstractImsStatusPreferenceController { - public ConcreteImsStatusPreferenceController(Context context, + private ConcreteImsStatusPreferenceController(Context context, Lifecycle lifecycle) { super(context, lifecycle); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java index 1d957c3b5e5b..76a26d917969 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java @@ -27,7 +27,6 @@ import android.net.wifi.WifiManager; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; @@ -35,11 +34,12 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import java.util.Arrays; import java.util.List; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class IpAddressPreferenceControllerTest { @Mock private Context mContext; @@ -75,8 +75,7 @@ public class IpAddressPreferenceControllerTest { private static class ConcreteIpAddressPreferenceController extends AbstractIpAddressPreferenceController { - public ConcreteIpAddressPreferenceController(Context context, - Lifecycle lifecycle) { + private ConcreteIpAddressPreferenceController(Context context, Lifecycle lifecycle) { super(context, lifecycle); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java index dc77400e2547..5b71bdd3d760 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java @@ -25,16 +25,15 @@ import android.content.Context; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class SerialNumberPreferenceControllerTest { @Mock diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java index eb77cb6271e9..5252c6c82754 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java @@ -24,17 +24,16 @@ import android.net.ConnectivityManager; import android.os.UserManager; import android.util.SparseBooleanArray; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(shadows = {SimStatusImeiInfoPreferenceControllerTest.ShadowUserManager.class, SimStatusImeiInfoPreferenceControllerTest.ShadowConnectivityManager.class}) public class SimStatusImeiInfoPreferenceControllerTest { @@ -106,7 +105,7 @@ public class SimStatusImeiInfoPreferenceControllerTest { private final SparseBooleanArray mSupportedNetworkTypes = new SparseBooleanArray(); - public void setNetworkSupported(int networkType, boolean supported) { + private void setNetworkSupported(int networkType, boolean supported) { mSupportedNetworkTypes.put(networkType, supported); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java index 2e0348daaa51..f09879b95221 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java @@ -28,7 +28,6 @@ import android.text.format.DateUtils; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; @@ -36,9 +35,10 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.shadows.ShadowLooper; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class UptimePreferenceControllerTest { @Mock private Context mContext; @@ -92,7 +92,7 @@ public class UptimePreferenceControllerTest { private static class ConcreteUptimePreferenceController extends AbstractUptimePreferenceController { - public ConcreteUptimePreferenceController(Context context, + private ConcreteUptimePreferenceController(Context context, Lifecycle lifecycle) { super(context, lifecycle); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java index 359ea7791922..74e5bf5a8034 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java @@ -33,7 +33,6 @@ import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import com.android.settingslib.R; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; @@ -41,13 +40,14 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import java.util.Arrays; import java.util.List; @SuppressLint("HardwareIds") -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class WifiMacAddressPreferenceControllerTest { @Mock private Lifecycle mLifecycle; @@ -197,7 +197,7 @@ public class WifiMacAddressPreferenceControllerTest { private static class ConcreteWifiMacAddressPreferenceController extends AbstractWifiMacAddressPreferenceController { - public ConcreteWifiMacAddressPreferenceController(Context context, + private ConcreteWifiMacAddressPreferenceController(Context context, Lifecycle lifecycle) { super(context, lifecycle); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java index ca621ca66829..c0924d9a8b35 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java @@ -20,12 +20,11 @@ import static com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX; import static com.google.common.truth.Truth.assertThat; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class BrightnessUtilsTest { private static final int MIN = 1; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java index 59a3dd61475c..605c861fa07f 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java @@ -20,14 +20,13 @@ import static com.google.common.truth.Truth.assertThat; import android.util.ArraySet; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import java.util.Set; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class CategoryKeyTest { @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java index 40e7386cf5af..b77670bd01e5 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java @@ -13,15 +13,14 @@ import android.content.pm.ActivityInfo; import android.os.Bundle; import com.android.settingslib.R; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; - -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class TileTest { private ActivityInfo mActivityInfo; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java index 362ae4c84cbf..bbb4249317f7 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java @@ -54,7 +54,6 @@ import android.util.ArrayMap; import android.util.Pair; import com.android.settingslib.R; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import org.junit.Before; import org.junit.Test; @@ -62,12 +61,13 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import java.util.ArrayList; import java.util.List; import java.util.Map; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class TileUtilsTest { private Context mContext; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java index d0b6dab43281..2988905b44a6 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java @@ -33,28 +33,26 @@ import android.os.PowerManager; import android.provider.Settings.Global; import android.provider.Settings.Secure; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; - -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class BatterySaverUtilsTest { - final int BATTERY_SAVER_THRESHOLD_1 = 15; - final int BATTERY_SAVER_THRESHOLD_2 = 20; + private static final int BATTERY_SAVER_THRESHOLD_1 = 15; + private static final int BATTERY_SAVER_THRESHOLD_2 = 20; @Mock - Context mMockContext; + private Context mMockContext; @Mock - ContentResolver mMockResolver; + private ContentResolver mMockResolver; @Mock - PowerManager mMockPowerManager; + private PowerManager mMockPowerManager; @Before public void setUp() throws Exception { @@ -66,11 +64,11 @@ public class BatterySaverUtilsTest { } @Test - public void testSetPowerSaveMode_enable_firstCall_needWarning() throws Exception { + public void testSetPowerSaveMode_enable_firstCall_needWarning() { Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null"); Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null"); - assertEquals(false, BatterySaverUtils.setPowerSaveMode(mMockContext, true, true)); + assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true)).isFalse(); verify(mMockContext, times(1)).sendBroadcast(any(Intent.class)); verify(mMockPowerManager, times(0)).setPowerSaveMode(anyBoolean()); @@ -83,11 +81,11 @@ public class BatterySaverUtilsTest { } @Test - public void testSetPowerSaveMode_enable_secondCall_needWarning() throws Exception { + public void testSetPowerSaveMode_enable_secondCall_needWarning() { Secure.putInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1); // Already acked. Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null"); - assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, true, true)); + assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true)).isTrue(); verify(mMockContext, times(0)).sendBroadcast(any(Intent.class)); verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(true)); @@ -97,11 +95,11 @@ public class BatterySaverUtilsTest { } @Test - public void testSetPowerSaveMode_enable_thridCall_needWarning() throws Exception { + public void testSetPowerSaveMode_enable_thridCall_needWarning() { Secure.putInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1); // Already acked. Secure.putInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, 1); - assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, true, true)); + assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true)).isTrue(); verify(mMockContext, times(0)).sendBroadcast(any(Intent.class)); verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(true)); @@ -111,11 +109,11 @@ public class BatterySaverUtilsTest { } @Test - public void testSetPowerSaveMode_enable_firstCall_noWarning() throws Exception { + public void testSetPowerSaveMode_enable_firstCall_noWarning() { Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null"); Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null"); - assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, true, false)); + assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, false)).isTrue(); verify(mMockContext, times(0)).sendBroadcast(any(Intent.class)); verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(true)); @@ -125,12 +123,12 @@ public class BatterySaverUtilsTest { } @Test - public void testSetPowerSaveMode_disable_firstCall_noWarning() throws Exception { + public void testSetPowerSaveMode_disable_firstCall_noWarning() { Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null"); Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null"); // When disabling, needFirstTimeWarning doesn't matter. - assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, false, false)); + assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, false, false)).isTrue(); verify(mMockContext, times(0)).sendBroadcast(any(Intent.class)); verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(false)); @@ -141,12 +139,12 @@ public class BatterySaverUtilsTest { } @Test - public void testSetPowerSaveMode_disable_firstCall_needWarning() throws Exception { + public void testSetPowerSaveMode_disable_firstCall_needWarning() { Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null"); Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null"); // When disabling, needFirstTimeWarning doesn't matter. - assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, false, true)); + assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, false, true)).isTrue(); verify(mMockContext, times(0)).sendBroadcast(any(Intent.class)); verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(false)); @@ -157,7 +155,7 @@ public class BatterySaverUtilsTest { } @Test - public void testEnsureAutoBatterysaver_setNewPositiveValue_doNotOverwrite() throws Exception { + public void testEnsureAutoBatterysaver_setNewPositiveValue_doNotOverwrite() { Global.putInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); BatterySaverUtils.ensureAutoBatterySaver(mMockContext, BATTERY_SAVER_THRESHOLD_1); @@ -172,7 +170,7 @@ public class BatterySaverUtilsTest { } @Test - public void testSetAutoBatterySaverTriggerLevel_setSuppressSuggestion() throws Exception { + public void testSetAutoBatterySaverTriggerLevel_setSuppressSuggestion() { Global.putString(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, "null"); Secure.putString(mMockResolver, Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, "null"); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java index 9b1fe5f6029d..bbf807d29402 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java @@ -31,7 +31,6 @@ import android.content.Context; import android.content.pm.PackageManager; import android.os.IDeviceIdleController; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.testutils.shadow.ShadowDefaultDialerManager; import com.android.settingslib.testutils.shadow.ShadowSmsApplication; @@ -40,12 +39,13 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; import org.robolectric.shadows.ShadowPackageManager; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowDefaultDialerManager.class, ShadowSmsApplication.class}) public class PowerWhitelistBackendTest { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java index 49dde0e6fcfa..35743c219129 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java @@ -16,8 +16,8 @@ package com.android.settingslib.graph; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -25,17 +25,16 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; import org.robolectric.RuntimeEnvironment; import org.robolectric.util.ReflectionHelpers; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class BatteryMeterDrawableBaseTest { private static final int CRITICAL_LEVEL = 5; private static final int PADDING = 5; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java index 5dbb5caf60eb..1b350cc83285 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java @@ -22,14 +22,14 @@ import android.content.Context; import android.graphics.drawable.VectorDrawable; import com.android.settingslib.R; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class BluetoothDeviceLayerDrawableTest { private static final int RES_ID = R.drawable.ic_bt_cellphone; private static final int BATTERY_LEVEL = 15; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java index fa64afec0461..b930aa6ee1bd 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java @@ -25,26 +25,21 @@ import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class InputMethodAndSubtypeUtilCompatTest { private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>(); private static HashSet<String> asHashSet(String... strings) { - HashSet<String> hashSet = new HashSet<>(); - for (String s : strings) { - hashSet.add(s); - } - return hashSet; + return new HashSet<>(Arrays.asList(strings)); } @Test @@ -105,7 +100,6 @@ public class InputMethodAndSubtypeUtilCompatTest { "ime0;subtype0;subtype1:ime1;subtype1;subtype2")) .containsExactly("ime0", asHashSet("subtype0", "subtype1"), "ime1", asHashSet("subtype1", "subtype2")); - } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java index 03ab261aa75a..84606b4e4502 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java @@ -25,26 +25,21 @@ import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class InputMethodAndSubtypeUtilTest { private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>(); private static HashSet<String> asHashSet(String... strings) { - HashSet<String> hashSet = new HashSet<>(); - for (String s : strings) { - hashSet.add(s); - } - return hashSet; + return new HashSet<>(Arrays.asList(strings)); } @Test @@ -103,7 +98,6 @@ public class InputMethodAndSubtypeUtilTest { "ime0;subtype0;subtype1:ime1;subtype1;subtype2")) .containsExactly("ime0", asHashSet("subtype0", "subtype1"), "ime1", asHashSet("subtype1", "subtype2")); - } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java index b00476b24921..4b5e9097b3fe 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java @@ -18,10 +18,9 @@ package com.android.settingslib.license; import static com.google.common.truth.Truth.assertThat; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.xmlpull.v1.XmlPullParserException; import java.io.ByteArrayInputStream; @@ -32,7 +31,7 @@ import java.io.StringWriter; import java.util.HashMap; import java.util.Map; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class LicenseHtmlGeneratorFromXmlTest { private static final String VALILD_XML_STRING = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" @@ -92,8 +91,8 @@ public class LicenseHtmlGeneratorFromXmlTest { @Test public void testParseValidXmlStream() throws XmlPullParserException, IOException { - Map<String, String> fileNameToContentIdMap = new HashMap<String, String>(); - Map<String, String> contentIdToFileContentMap = new HashMap<String, String>(); + Map<String, String> fileNameToContentIdMap = new HashMap<>(); + Map<String, String> contentIdToFileContentMap = new HashMap<>(); LicenseHtmlGeneratorFromXml.parse( new InputStreamReader(new ByteArrayInputStream(VALILD_XML_STRING.getBytes())), @@ -107,8 +106,8 @@ public class LicenseHtmlGeneratorFromXmlTest { @Test(expected = XmlPullParserException.class) public void testParseInvalidXmlStream() throws XmlPullParserException, IOException { - Map<String, String> fileNameToContentIdMap = new HashMap<String, String>(); - Map<String, String> contentIdToFileContentMap = new HashMap<String, String>(); + Map<String, String> fileNameToContentIdMap = new HashMap<>(); + Map<String, String> contentIdToFileContentMap = new HashMap<>(); LicenseHtmlGeneratorFromXml.parse( new InputStreamReader(new ByteArrayInputStream(INVALILD_XML_STRING.getBytes())), @@ -117,8 +116,8 @@ public class LicenseHtmlGeneratorFromXmlTest { @Test public void testGenerateHtml() { - Map<String, String> fileNameToContentIdMap = new HashMap<String, String>(); - Map<String, String> contentIdToFileContentMap = new HashMap<String, String>(); + Map<String, String> fileNameToContentIdMap = new HashMap<>(); + Map<String, String> contentIdToFileContentMap = new HashMap<>(); fileNameToContentIdMap.put("/file0", "0"); fileNameToContentIdMap.put("/file1", "0"); @@ -132,8 +131,8 @@ public class LicenseHtmlGeneratorFromXmlTest { @Test public void testGenerateHtmlWithCustomHeading() { - Map<String, String> fileNameToContentIdMap = new HashMap<String, String>(); - Map<String, String> contentIdToFileContentMap = new HashMap<String, String>(); + Map<String, String> fileNameToContentIdMap = new HashMap<>(); + Map<String, String> contentIdToFileContentMap = new HashMap<>(); fileNameToContentIdMap.put("/file0", "0"); fileNameToContentIdMap.put("/file1", "0"); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java index c90de5fe621e..e82bc0678108 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java @@ -20,14 +20,13 @@ import static com.google.common.truth.Truth.assertThat; import android.content.Context; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; +import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; @@ -37,7 +36,7 @@ import java.io.File; import java.util.ArrayList; import java.util.List; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(shadows = LicenseHtmlLoaderCompatTest.ShadowLicenseHtmlLoaderCompat.class) public class LicenseHtmlLoaderCompatTest { @@ -58,7 +57,7 @@ public class LicenseHtmlLoaderCompatTest { @Test public void testLoadInBackground() { - ArrayList<File> xmlFiles = new ArrayList(); + ArrayList<File> xmlFiles = new ArrayList<>(); xmlFiles.add(new File("test.xml")); File cachedHtmlFile = new File("test.html"); @@ -69,7 +68,7 @@ public class LicenseHtmlLoaderCompatTest { @Test public void testLoadInBackgroundWithNoVaildXmlFiles() { - ArrayList<File> xmlFiles = new ArrayList(); + ArrayList<File> xmlFiles = new ArrayList<>(); File cachedHtmlFile = new File("test.html"); setupFakeData(xmlFiles, cachedHtmlFile, true, true); @@ -79,7 +78,7 @@ public class LicenseHtmlLoaderCompatTest { @Test public void testLoadInBackgroundWithNonOutdatedCachedHtmlFile() { - ArrayList<File> xmlFiles = new ArrayList(); + ArrayList<File> xmlFiles = new ArrayList<>(); xmlFiles.add(new File("test.xml")); File cachedHtmlFile = new File("test.html"); @@ -90,7 +89,7 @@ public class LicenseHtmlLoaderCompatTest { @Test public void testLoadInBackgroundWithGenerateHtmlFileFailed() { - ArrayList<File> xmlFiles = new ArrayList(); + ArrayList<File> xmlFiles = new ArrayList<>(); xmlFiles.add(new File("test.xml")); File cachedHtmlFile = new File("test.html"); @@ -112,10 +111,10 @@ public class LicenseHtmlLoaderCompatTest { @Implements(LicenseHtmlLoaderCompat.class) public static class ShadowLicenseHtmlLoaderCompat { - public static List<File> sValidXmlFiles; - public static File sCachedHtmlFile; - public static boolean sIsCachedHtmlFileOutdated; - public static boolean sGenerateHtmlFileSucceeded; + private static List<File> sValidXmlFiles; + private static File sCachedHtmlFile; + private static boolean sIsCachedHtmlFileOutdated; + private static boolean sGenerateHtmlFileSucceeded; @Resetter public static void reset() { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java index c29481f633a4..8c2e8992fd6a 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java @@ -18,12 +18,11 @@ package com.android.settingslib.location; import static com.google.common.truth.Truth.assertThat; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public final class InjectedSettingTest { private static final String TEST_STRING = "test"; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java index 9c168f7b1a45..08d536720029 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java @@ -2,7 +2,7 @@ package com.android.settingslib.location; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Matchers.isA; +import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.when; import android.app.AppOpsManager; @@ -17,20 +17,19 @@ import android.os.Process; import android.os.UserHandle; import android.os.UserManager; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class RecentLocationAppsTest { private static final int TEST_UID = 1234; @@ -56,8 +55,6 @@ public class RecentLocationAppsTest { private int mTestUserId; private RecentLocationApps mRecentLocationApps; - - @Before public void setUp() throws NameNotFoundException { MockitoAnnotations.initMocks(this); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java index 50044f2cc0ea..72ed5e123add 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java @@ -42,16 +42,15 @@ import android.telephony.TelephonyManager; import android.text.format.DateUtils; import android.util.FeatureFlagUtils; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class DataUsageControllerTest { private static final String SUB_ID = "Test Subscriber"; @@ -85,7 +84,6 @@ public class DataUsageControllerTest { doReturn(null).when(mController).getSession(); assertThat(mController.getHistoricalUsageLevel(null /* template */)).isEqualTo(-1L); - } @Test @@ -95,7 +93,6 @@ public class DataUsageControllerTest { assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard())) .isEqualTo(0L); - } @Test @@ -113,7 +110,6 @@ public class DataUsageControllerTest { assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard())) .isEqualTo(receivedBytes + transmittedBytes); - } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java index 0a036317910e..011f234ab4f1 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java @@ -27,15 +27,14 @@ import android.net.NetworkPolicyManager; import android.os.RemoteException; import android.text.format.DateUtils; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class NetworkCycleChartDataLoaderTest { @Mock diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java index 2314f272c8ea..d9159631e8a9 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java @@ -19,7 +19,7 @@ package com.android.settingslib.net; import static android.app.usage.NetworkStats.Bucket.STATE_FOREGROUND; import static android.net.NetworkStats.TAG_NONE; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -33,15 +33,14 @@ import android.net.NetworkPolicy; import android.net.NetworkPolicyManager; import android.text.format.DateUtils; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class NetworkCycleDataForUidLoaderTest { @Mock diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java index 9d60a97f8584..2d8ea125a97e 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java @@ -16,8 +16,8 @@ package com.android.settingslib.net; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.nullable; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -37,20 +37,19 @@ import android.os.RemoteException; import android.text.format.DateUtils; import android.util.Range; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; import org.robolectric.util.ReflectionHelpers; import java.time.ZonedDateTime; import java.util.Iterator; import java.util.List; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class NetworkCycleDataLoaderTest { @Mock diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java index 89c319a7e483..59d56747ec5d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java @@ -16,11 +16,10 @@ package com.android.settingslib.notification; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertNull; -import static junit.framework.Assert.assertTrue; - +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -38,16 +37,15 @@ import android.net.Uri; import android.service.notification.Condition; import android.view.LayoutInflater; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class EnableZenModeDialogTest { private EnableZenModeDialog mController; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java index 81476564f9b9..437c0d4f4469 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java @@ -25,27 +25,23 @@ import static org.mockito.Mockito.spy; import android.content.ContentResolver; import android.content.Context; import android.provider.Settings; -import android.service.notification.Condition; import android.view.LayoutInflater; import android.view.View; import androidx.appcompat.app.AlertDialog; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class ZenDurationDialogTest { private ZenDurationDialog mController; private Context mContext; private LayoutInflater mLayoutInflater; - private Condition mCountdownCondition; - private Condition mAlarmCondition; private ContentResolver mContentResolver; private AlertDialog.Builder mBuilder; @@ -102,7 +98,6 @@ public class ZenDurationDialogTest { ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX).rb.isChecked()); } - @Test public void testChooseAlwaysPromptSetting() { Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_DURATION, diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java index 449451a63e2d..ffaa7443eb46 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java @@ -31,7 +31,6 @@ import android.content.Context; import androidx.lifecycle.LifecycleOwner; import androidx.loader.app.LoaderManager; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.After; @@ -40,10 +39,11 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(shadows = ShadowSuggestionController.class) public class SuggestionControllerMixinCompatTest { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java index aac582f9b3ac..4dc80f442649 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java @@ -31,7 +31,6 @@ import android.content.Context; import androidx.lifecycle.LifecycleOwner; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.After; @@ -40,10 +39,11 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(shadows = ShadowSuggestionController.class) public class SuggestionControllerMixinTest { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java index f4afdb11ff95..3e91641a69ae 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java @@ -26,19 +26,19 @@ import org.robolectric.annotation.Resetter; @Implements(DefaultDialerManager.class) public class ShadowDefaultDialerManager { - private static String sDefaultDailer; + private static String sDefaultDialer; @Resetter public void reset() { - sDefaultDailer = null; + sDefaultDialer = null; } @Implementation public static String getDefaultDialerApplication(Context context) { - return sDefaultDailer; + return sDefaultDialer; } public static void setDefaultDialerApplication(String dialer) { - sDefaultDailer = dialer; + sDefaultDialer = dialer; } }
\ No newline at end of file diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java index 4705cd2b183b..9a169d2663de 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java @@ -27,7 +27,6 @@ import android.content.pm.UserInfo; import android.os.UserHandle; import android.os.UserManager; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.testutils.shadow.ShadowActivityManager; import org.junit.After; @@ -36,6 +35,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; @@ -45,7 +45,7 @@ import org.robolectric.annotation.Resetter; import java.util.ArrayList; import java.util.List; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(shadows = { ShadowActivityManager.class, UserManagerHelperRoboTest.ShadowUserHandle.class}) public class UserManagerHelperRoboTest { @Mock diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java index 645dfa127626..026ad47f99a2 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java @@ -30,14 +30,13 @@ import android.content.Context; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class IconCacheTest { private Icon mIcon; private Context mContext; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java index 6a9579b770ce..7ef31df6ab26 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java @@ -22,31 +22,30 @@ import static org.mockito.Mockito.spy; import android.content.Context; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import java.time.Duration; import java.util.regex.Pattern; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class PowerUtilTest { - public static final String TEST_BATTERY_LEVEL_10 = "10%"; - public static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis(); - public static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis(); - public static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis(); - public static final long THREE_DAYS_MILLIS = Duration.ofDays(3).toMillis(); - public static final long THIRTY_HOURS_MILLIS = Duration.ofHours(30).toMillis(); - public static final String NORMAL_CASE_EXPECTED_PREFIX = "Should last until about"; - public static final String ENHANCED_SUFFIX = " based on your usage"; + private static final String TEST_BATTERY_LEVEL_10 = "10%"; + private static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis(); + private static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis(); + private static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis(); + private static final long THREE_DAYS_MILLIS = Duration.ofDays(3).toMillis(); + private static final long THIRTY_HOURS_MILLIS = Duration.ofHours(30).toMillis(); + private static final String NORMAL_CASE_EXPECTED_PREFIX = "Should last until about"; + private static final String ENHANCED_SUFFIX = " based on your usage"; // matches a time (ex: '1:15 PM', '2 AM', '23:00') - public static final String TIME_OF_DAY_REGEX = " (\\d)+:?(\\d)* ((AM)*)|((PM)*)"; + private static final String TIME_OF_DAY_REGEX = " (\\d)+:?(\\d)* ((AM)*)|((PM)*)"; // matches a percentage with parenthesis (ex: '(10%)') - public static final String PERCENTAGE_REGEX = " \\(\\d?\\d%\\)"; + private static final String PERCENTAGE_REGEX = " \\(\\d?\\d%\\)"; private Context mContext; @@ -108,7 +107,6 @@ public class PowerUtilTest { + "(" + PERCENTAGE_REGEX + "){0}")); // no percentage } - @Test public void testGetBatteryRemainingStringFormatted_lessThanSevenMinutes_usesCorrectString() { String info = PowerUtil.getBatteryRemainingStringFormatted(mContext, diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java index e4bbbcb0b207..8fbbfbbd5047 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java @@ -25,14 +25,13 @@ import android.text.SpannableStringBuilder; import android.text.format.DateUtils; import android.text.style.TtsSpan; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class StringUtilTest { private Context mContext; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java index 1e066b1b0f74..26db124c0041 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java @@ -15,28 +15,22 @@ */ package com.android.settingslib.utils; - import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.shadows.ShadowLooper; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class ThreadUtilsTest { @Test public void testMainThread() throws InterruptedException { assertThat(ThreadUtils.isMainThread()).isTrue(); - Thread background = new Thread(new Runnable() { - public void run() { - assertThat(ThreadUtils.isMainThread()).isFalse(); - } - }); + Thread background = new Thread(() -> assertThat(ThreadUtils.isMainThread()).isFalse()); background.start(); background.join(); } @@ -44,13 +38,11 @@ public class ThreadUtilsTest { @Test public void testEnsureMainThread() throws InterruptedException { ThreadUtils.ensureMainThread(); - Thread background = new Thread(new Runnable() { - public void run() { - try { - ThreadUtils.ensureMainThread(); - fail("Should not pass ensureMainThread in a background thread"); - } catch (RuntimeException e) { - } + Thread background = new Thread(() -> { + try { + ThreadUtils.ensureMainThread(); + fail("Should not pass ensureMainThread in a background thread"); + } catch (RuntimeException expected) { } }); background.start(); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java index a00f12d9a6d9..d41d5112e6b2 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java @@ -22,14 +22,13 @@ import android.app.Activity; import android.graphics.drawable.AnimatedRotateDrawable; import android.view.View; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class AnimatedImageViewTest { private AnimatedImageView mAnimatedImageView; @@ -47,5 +46,4 @@ public class AnimatedImageViewTest { AnimatedRotateDrawable drawable = (AnimatedRotateDrawable) mAnimatedImageView.getDrawable(); assertThat(drawable.isRunning()).isTrue(); } - } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java index e030005e2d09..601da0512c7c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java @@ -18,7 +18,7 @@ package com.android.settingslib.widget; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -29,7 +29,6 @@ import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; @@ -37,9 +36,10 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class FooterPreferenceMixinCompatTest { @Mock @@ -97,5 +97,4 @@ public class FooterPreferenceMixinCompatTest { verify(mScreen).removePreference(any(FooterPreference.class)); verify(mScreen, times(2)).addPreference(any(FooterPreference.class)); } - } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java index 8817ff7f65b3..7ae5d2d97cb8 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java @@ -18,7 +18,7 @@ package com.android.settingslib.widget; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -29,7 +29,6 @@ import androidx.preference.PreferenceFragment; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; @@ -37,10 +36,10 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import org.robolectric.shadows.ShadowApplication; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class FooterPreferenceMixinTest { @Mock diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java index e0eceb418f27..0d2399e3dcab 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java @@ -26,14 +26,14 @@ import android.widget.TextView; import androidx.preference.PreferenceViewHolder; import com.android.settingslib.R; -import com.android.settingslib.SettingsLibRobolectricTestRunner; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class FooterPreferenceTest { private Context mContext; 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 index 427a611d61da..99261a38f73b 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java @@ -27,14 +27,13 @@ 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.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class LayoutPreferenceTest { private LayoutPreference mPreference; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java index 10c9dfbe6067..da97cc8b74dd 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java @@ -23,14 +23,13 @@ import android.view.View; 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.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class AppPreferenceTest { private Context mContext; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java index 86443bde4667..c5cbea78120f 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java @@ -25,16 +25,15 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.graphics.drawable.ColorDrawable; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - 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; import org.robolectric.RuntimeEnvironment; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class AccessPointPreferenceTest { private Context mContext = RuntimeEnvironment.application; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java index f0e8c66e8544..b059df1fd8cb 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java @@ -22,15 +22,14 @@ import android.net.ScoredNetwork; import android.net.WifiKey; import android.os.Parcel; -import com.android.settingslib.SettingsLibRobolectricTestRunner; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import java.util.Date; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class TimestampedScoredNetworkTest { private TimestampedScoredNetwork impl; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java index 07c50fde00fa..89960cba2bf5 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java @@ -37,19 +37,19 @@ import android.text.format.DateUtils; import android.util.ArraySet; import com.android.settingslib.R; -import com.android.settingslib.SettingsLibRobolectricTestRunner; 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; import org.robolectric.RuntimeEnvironment; import java.util.ArrayList; import java.util.Set; -@RunWith(SettingsLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class WifiUtilsTest { private static final String TEST_SSID = "\"test_ssid\""; private static final String TEST_BSSID = "00:00:00:00:00:00"; @@ -79,7 +79,7 @@ public class WifiUtilsTest { Bundle bundle = new Bundle(); ArrayList<ScanResult> scanResults = buildScanResultCache(); bundle.putParcelableArray(AccessPoint.KEY_SCANRESULTS, - scanResults.toArray(new Parcelable[scanResults.size()])); + scanResults.toArray(new Parcelable[0])); AccessPoint ap = new AccessPoint(mContext, bundle); when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class))) diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java index f4922088bb05..4891e5006279 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java @@ -30,6 +30,12 @@ public interface ActivityStarter { int VERSION = 1; void startPendingIntentDismissingKeyguard(PendingIntent intent); + + /** + * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but + * allow you to specify the callback that is executed after the intent is sent. + */ + void startPendingIntentDismissingKeyguard(PendingIntent intent, Runnable intentSentCallback); void startActivity(Intent intent, boolean dismissShade); void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade); void startActivity(Intent intent, boolean dismissShade, Callback callback); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java index 70258c20538d..2aba3fa607b7 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java @@ -20,7 +20,6 @@ import android.graphics.Matrix; import android.graphics.Rect; import android.os.IBinder; import android.view.Surface; -import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; public class TransactionCompat { @@ -53,7 +52,7 @@ public class TransactionCompat { } public TransactionCompat setSize(SurfaceControlCompat surfaceControl, int w, int h) { - mTransaction.setSize(surfaceControl.mSurfaceControl, w, h); + mTransaction.setBufferSize(surfaceControl.mSurfaceControl, w, h); return this; } diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java index e1b8dc839bde..9e7c5ba1d66c 100644 --- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java @@ -36,6 +36,15 @@ public class ActivityStarterDelegate implements ActivityStarter { } @Override + public void startPendingIntentDismissingKeyguard(PendingIntent intent, + Runnable intentSentCallback) { + if (mActualStarter == null) { + return; + } + mActualStarter.startPendingIntentDismissingKeyguard(intent, intentSentCallback); + } + + @Override public void startActivity(Intent intent, boolean dismissShade) { if (mActualStarter == null) { return; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index c6dcfc7356be..416cc594051b 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -23,10 +23,10 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.android.systemui.bubbles.BubbleMovementHelper.EDGE_OVERLAP; import android.app.Notification; -import android.app.NotificationManager; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; +import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.view.ViewGroup; import android.view.WindowManager; @@ -57,6 +57,11 @@ public class BubbleController { // When a bubble is dismissed, recreate it as a notification public static final boolean DEBUG_DEMOTE_TO_NOTIF = false; + // Secure settings + private static final String ENABLE_AUTO_BUBBLE_MESSAGES = "experiment_autobubble_messaging"; + private static final String ENABLE_AUTO_BUBBLE_ONGOING = "experiment_autobubble_ongoing"; + private static final String ENABLE_AUTO_BUBBLE_ALL = "experiment_autobubble_all"; + private Context mContext; private BubbleDismissListener mDismissListener; private BubbleStateChangeListener mStateChangeListener; @@ -318,11 +323,15 @@ public class BubbleController { /** * Whether the notification should bubble or not. */ - public static boolean shouldAutoBubble(NotificationData.Entry entry, int priority, - boolean canAppOverlay) { - if (!DEBUG_ENABLE_AUTO_BUBBLE || entry.isBubbleDismissed()) { + public static boolean shouldAutoBubble(Context context, NotificationData.Entry entry) { + if (entry.isBubbleDismissed()) { return false; } + + boolean autoBubbleMessages = shouldAutoBubbleMessages(context) || DEBUG_ENABLE_AUTO_BUBBLE; + boolean autoBubbleOngoing = shouldAutoBubbleOngoing(context) || DEBUG_ENABLE_AUTO_BUBBLE; + boolean autoBubbleAll = shouldAutoBubbleAll(context) || DEBUG_ENABLE_AUTO_BUBBLE; + StatusBarNotification n = entry.notification; boolean hasRemoteInput = false; if (n.getNotification().actions != null) { @@ -333,12 +342,28 @@ public class BubbleController { } } } + Class<? extends Notification.Style> style = n.getNotification().getNotificationStyle(); - boolean shouldBubble = priority >= NotificationManager.IMPORTANCE_HIGH - || Notification.MessagingStyle.class.equals(style) + boolean isMessageType = Notification.MessagingStyle.class.equals(style) || Notification.CATEGORY_MESSAGE.equals(n.getNotification().category) - || hasRemoteInput - || canAppOverlay; - return shouldBubble && !entry.isBubbleDismissed(); + || hasRemoteInput; + return (isMessageType && autoBubbleMessages) + || (n.isOngoing() && autoBubbleOngoing) + || autoBubbleAll; + } + + private static boolean shouldAutoBubbleMessages(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + ENABLE_AUTO_BUBBLE_MESSAGES, 0) != 0; + } + + private static boolean shouldAutoBubbleOngoing(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + ENABLE_AUTO_BUBBLE_ONGOING, 0) != 0; + } + + private static boolean shouldAutoBubbleAll(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + ENABLE_AUTO_BUBBLE_ALL, 0) != 0; } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java index 01a234544c5b..1dd3101075b0 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java @@ -24,20 +24,21 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; -import android.os.Build; import android.os.Handler; +import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.Dependency; /** * Controls the screen brightness when dozing. */ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachine.Part, SensorEventListener { + private static final boolean DEBUG_AOD_BRIGHTNESS = SystemProperties + .getBoolean("debug.aod_brightness", false); protected static final String ACTION_AOD_BRIGHTNESS = "com.android.systemui.doze.AOD_BRIGHTNESS"; protected static final String BRIGHTNESS_BUCKET = "brightness_bucket"; @@ -83,11 +84,9 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi mSensorToScrimOpacity = sensorToScrimOpacity; if (mDebuggable) { - Dependency.get(Dependency.BG_HANDLER).post(()-> { - IntentFilter filter = new IntentFilter(); - filter.addAction(ACTION_AOD_BRIGHTNESS); - mContext.registerReceiverAsUser(this, UserHandle.ALL, filter, null, handler); - }); + IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_AOD_BRIGHTNESS); + mContext.registerReceiverAsUser(this, UserHandle.ALL, filter, null, handler); } } @@ -97,7 +96,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi this(context, service, sensorManager, lightSensor, host, handler, context.getResources().getInteger( com.android.internal.R.integer.config_screenBrightnessDoze), - policy.screenBrightnessArray, policy.dimmingScrimArray, Build.IS_DEBUGGABLE); + policy.screenBrightnessArray, policy.dimmingScrimArray, DEBUG_AOD_BRIGHTNESS); } @Override @@ -126,9 +125,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi private void onDestroy() { setLightSensorEnabled(false); if (mDebuggable) { - Dependency.get(Dependency.BG_HANDLER).post(()-> { - mContext.unregisterReceiver(this); - }); + mContext.unregisterReceiver(this); } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java index 9a5a5b855999..be504ef5eb9c 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java @@ -23,6 +23,7 @@ import android.os.ServiceManager; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.DozeParameters; import java.io.PrintWriter; @@ -80,11 +81,12 @@ public class DozeWallpaperState implements DozeMachine.Part { if (isAmbientMode != mIsAmbientMode) { mIsAmbientMode = isAmbientMode; try { + long duration = animated ? StackStateAnimator.ANIMATION_DURATION_WAKEUP : 0L; if (DEBUG) { Log.i(TAG, "AOD wallpaper state changed to: " + mIsAmbientMode - + ", animated: " + animated); + + ", animationDuration: " + duration); } - mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, animated); + mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, duration); } catch (RemoteException e) { // Cannot notify wallpaper manager service, but it's fine, let's just skip it. Log.w(TAG, "Cannot notify state to WallpaperManagerService: " + mIsAmbientMode); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java index e447defadd08..8495fd38b5d4 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -19,6 +19,8 @@ package com.android.systemui.pip.phone; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS; +import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS; +import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS; import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ACTIONS; import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ALLOW_TIMEOUT; @@ -65,6 +67,7 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.WindowManager.LayoutParams; +import android.view.accessibility.AccessibilityManager; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; @@ -90,8 +93,8 @@ public class PipMenuActivity extends Activity { public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5; public static final int MESSAGE_ANIMATION_ENDED = 6; - private static final long INITIAL_DISMISS_DELAY = 3500; - private static final long POST_INTERACTION_DISMISS_DELAY = 2000; + private static final int INITIAL_DISMISS_DELAY = 3500; + private static final int POST_INTERACTION_DISMISS_DELAY = 2000; private static final long MENU_FADE_DURATION = 125; private static final float MENU_BACKGROUND_ALPHA = 0.3f; @@ -105,6 +108,7 @@ public class PipMenuActivity extends Activity { private final List<RemoteAction> mActions = new ArrayList<>(); + private AccessibilityManager mAccessibilityManager; private View mViewRoot; private Drawable mBackgroundDrawable; private View mMenuContainer; @@ -194,6 +198,7 @@ public class PipMenuActivity extends Activity { super.onCreate(savedInstanceState); setContentView(R.layout.pip_menu_activity); + mAccessibilityManager = getSystemService(AccessibilityManager.class); mBackgroundDrawable = new ColorDrawable(Color.BLACK); mBackgroundDrawable.setAlpha(0); mViewRoot = findViewById(R.id.background); @@ -639,8 +644,10 @@ public class PipMenuActivity extends Activity { mHandler.removeCallbacks(mFinishRunnable); } - private void repostDelayedFinish(long delay) { + private void repostDelayedFinish(int delay) { + int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay, + FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS); mHandler.removeCallbacks(mFinishRunnable); - mHandler.postDelayed(mFinishRunnable, delay); + mHandler.postDelayed(mFinishRunnable, recommendedTimeout); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 03a573ea48d8..6a9f24c619fe 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -368,7 +368,7 @@ public class PipTouchHandler { private void onAccessibilityShowMenu() { mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(), - mMovementBounds, false /* allowMenuTimeout */, willResizeMenu()); + mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()); } private boolean handleTouchEvent(MotionEvent ev) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java index 8b434a546504..496aa0e572ae 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java @@ -323,7 +323,9 @@ public class QSDetail extends LinearLayout { post(new Runnable() { @Override public void run() { - handleShowingDetail(detail, x, y, false /* toggleQs */); + if (isAttachedToWindow()) { + handleShowingDetail(detail, x, y, false /* toggleQs */); + } } }); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java index 0638998d8e67..3a96595dee06 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java @@ -198,7 +198,8 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { mIcon.setIcon(state, allowAnimations); setContentDescription(state.contentDescription); - mAccessibilityClass = state.expandedAccessibilityClassName; + mAccessibilityClass = + state.state == Tile.STATE_UNAVAILABLE ? null : state.expandedAccessibilityClassName; if (state instanceof QSTile.BooleanState) { boolean newState = ((BooleanState) state).value; if (mTileState != newState) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index 2ee5443ab3aa..7be5461f0afa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -150,34 +150,42 @@ public class NotificationRemoteInputManager implements Dumpable { } private void logActionClick(View view) { + Integer actionIndex = (Integer) + view.getTag(com.android.internal.R.id.notification_action_index_tag); + if (actionIndex == null) { + Log.e(TAG, "Couldn't retrieve the actionIndex from the clicked button"); + return; + } ViewParent parent = view.getParent(); - String key = getNotificationKeyForParent(parent); - if (key == null) { + StatusBarNotification statusBarNotification = getNotificationForParent(parent); + if (statusBarNotification == null) { Log.w(TAG, "Couldn't determine notification for click."); return; } - int index = -1; + String key = statusBarNotification.getKey(); + int buttonIndex = -1; // If this is a default template, determine the index of the button. if (view.getId() == com.android.internal.R.id.action0 && parent != null && parent instanceof ViewGroup) { ViewGroup actionGroup = (ViewGroup) parent; - index = actionGroup.indexOfChild(view); + buttonIndex = actionGroup.indexOfChild(view); } final int count = mEntryManager.getNotificationData().getActiveNotifications().size(); final int rank = mEntryManager.getNotificationData().getRank(key); + final Notification.Action action = + statusBarNotification.getNotification().actions[actionIndex]; final NotificationVisibility nv = NotificationVisibility.obtain(key, rank, count, true); try { - mBarService.onNotificationActionClick(key, index, nv); + mBarService.onNotificationActionClick(key, buttonIndex, action, nv, false); } catch (RemoteException e) { // Ignore } } - private String getNotificationKeyForParent(ViewParent parent) { + private StatusBarNotification getNotificationForParent(ViewParent parent) { while (parent != null) { if (parent instanceof ExpandableNotificationRow) { - return ((ExpandableNotificationRow) parent) - .getStatusBarNotification().getKey(); + return ((ExpandableNotificationRow) parent).getStatusBarNotification(); } parent = parent.getParent(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java index 37bdc1ce7cb9..f5d6904a1543 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java @@ -15,12 +15,15 @@ */ package com.android.systemui.statusbar; +import android.app.Notification; import android.os.RemoteException; import android.util.ArraySet; import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dependency; import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import java.util.Set; @@ -32,6 +35,9 @@ public class SmartReplyController { private IStatusBarService mBarService; private Set<String> mSendingKeys = new ArraySet<>(); private Callback mCallback; + private final NotificationEntryManager mEntryManager = + Dependency.get(NotificationEntryManager.class); + public SmartReplyController() { mBarService = Dependency.get(IStatusBarService.class); @@ -57,6 +63,24 @@ public class SmartReplyController { } /** + * Notifies StatusBarService a smart action is clicked. + */ + public void smartActionClicked( + NotificationData.Entry entry, int actionIndex, Notification.Action action, + boolean generatedByAssistant) { + final int count = mEntryManager.getNotificationData().getActiveNotifications().size(); + final int rank = mEntryManager.getNotificationData().getRank(entry.key); + final NotificationVisibility nv = + NotificationVisibility.obtain(entry.key, rank, count, true); + try { + mBarService.onNotificationActionClick( + entry.key, actionIndex, action, nv, generatedByAssistant); + } catch (RemoteException e) { + // Nothing to do, system going down + } + } + + /** * Have we posted an intent to an app about sending a smart reply from the * notification with this key. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java index 314a31d336fd..0a2e04fd9430 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java @@ -250,23 +250,24 @@ public class MessagingLayoutTransformState extends TransformState { otherChild = null; } } - if (otherChild == null) { + if (otherChild == null && previousTranslation < 0) { + // Let's fade out as we approach the top of the screen. We can only do this if + // we're actually moving up float distanceToTop = child.getTop() + child.getHeight() + previousTranslation; transformationAmount = distanceToTop / child.getHeight(); transformationAmount = Math.max(0.0f, Math.min(1.0f, transformationAmount)); - if (to) { - transformationAmount = 1.0f - transformationAmount; - } } transformView(transformationAmount, to, child, otherChild, false, /* sameAsAny */ useLinearTransformation); - if (transformationAmount == 0.0f - && otherGroup.getIsolatedMessage() == otherChild) { + boolean otherIsIsolated = otherGroup.getIsolatedMessage() == otherChild; + if (transformationAmount == 0.0f && otherIsIsolated) { ownGroup.setTransformingImages(true); } if (otherChild == null) { child.setTranslationY(previousTranslation); setClippingDeactivated(child, true); + } else if (ownGroup.getIsolatedMessage() == child || otherIsIsolated) { + // We don't want to add any translation for the image that is transforming } else if (to) { float totalTranslation = child.getTop() + ownGroup.getTop() - otherChild.getTop() - otherChild.getTop(); 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 e33372957011..1616b6dc53de 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -24,7 +24,6 @@ import static com.android.systemui.statusbar.notification.row.NotificationInflat import android.annotation.Nullable; import android.app.Notification; -import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; @@ -177,9 +176,12 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. } // Check if the notification is displaying the menu, if so slide notification back - if (row.getProvider() != null && row.getProvider().isMenuVisible()) { + if (isMenuVisible(row)) { row.animateTranslateNotification(0); return; + } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) { + row.getNotificationParent().animateTranslateNotification(0); + return; } // Mark notification for one frame. @@ -194,6 +196,10 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. mCallback.onNotificationClicked(sbn, row); } + private boolean isMenuVisible(ExpandableNotificationRow row) { + return row.getProvider() != null && row.getProvider().isMenuVisible(); + } + public void register(ExpandableNotificationRow row, StatusBarNotification sbn) { Notification notification = sbn.getNotification(); if (notification.contentIntent != null || notification.fullScreenIntent != null) { @@ -766,7 +772,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. } NotificationData.Entry entry = new NotificationData.Entry(sbn, ranking); - if (shouldAutoBubble(entry)) { + if (BubbleController.shouldAutoBubble(getContext(), entry)) { entry.setIsBubble(true); } @@ -1207,17 +1213,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. } } - - /** - * Whether a bubble is appropriate to auto-bubble or not. - */ - private boolean shouldAutoBubble(NotificationData.Entry entry) { - int priority = mNotificationData.getImportance(entry.key); - NotificationChannel channel = mNotificationData.getChannel(entry.key); - boolean canAppOverlay = channel != null && channel.canOverlayApps(); - return BubbleController.shouldAutoBubble(entry, priority, canAppOverlay); - } - /** * Callback for NotificationEntryManager. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java index 247c1ababc18..a194eef39b6d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java @@ -16,15 +16,13 @@ package com.android.systemui.statusbar.notification; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; - /** * An object that can determine the visibility of a Notification. */ public interface VisibilityLocationProvider { /** - * Returns whether an ExpandableNotificationRow is in a visible location or not. + * Returns whether an Entry is in a visible location or not. * * @param entry * @return true if row is in a visible location 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 0cd431f9d25b..d4d45ea52a85 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 @@ -2067,6 +2067,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private void setChildIsExpanding(boolean isExpanding) { mChildIsExpanding = isExpanding; + updateClipping(); + invalidate(); } @Override @@ -2968,7 +2970,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return true; } } else if (child == mChildrenContainer) { - if (!mChildIsExpanding && (isClippingNeeded() || !hasNoRounding())) { + if (isClippingNeeded() || !hasNoRounding()) { return true; } } else if (child instanceof NotificationGuts) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java index a7aed5fce2e1..0efb1308e83e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java @@ -115,12 +115,14 @@ public abstract class ExpandableOutlineView extends ExpandableView { if (!mCustomOutline) { int translation = mShouldTranslateContents && !ignoreTranslation ? (int) getTranslation() : 0; - left = Math.max(translation, 0); + int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f); + left = Math.max(translation, 0) - halfExtraWidth; top = mClipTopAmount + mBackgroundTop; - right = getWidth() + Math.min(translation, 0); + right = getWidth() + halfExtraWidth + Math.min(translation, 0); // If the top is rounded we want the bottom to be at most at the top roundness, in order // to avoid the shadow changing when scrolling up. - bottom = Math.max(getActualHeight() - mClipBottomAmount, (int) (top + topRoundness)); + bottom = Math.max(mMinimumHeightForClipping, + Math.max(getActualHeight() - mClipBottomAmount, (int) (top + topRoundness))); } else { left = mOutlineRect.left; top = mOutlineRect.top; @@ -219,10 +221,12 @@ public abstract class ExpandableOutlineView extends ExpandableView { public void setExtraWidthForClipping(float extraWidthForClipping) { mExtraWidthForClipping = extraWidthForClipping; + invalidate(); } public void setMinimumHeightForClipping(int minimumHeightForClipping) { mMinimumHeightForClipping = minimumHeightForClipping; + invalidate(); } @Override 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 a4fdc08d5579..92d1b452bf44 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 @@ -57,7 +57,6 @@ import com.android.systemui.statusbar.policy.SmartReplyView; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.Collections; import java.util.List; /** @@ -1520,7 +1519,8 @@ public class NotificationContentView extends FrameLayout { smartRepliesAndActions.smartReplies, mSmartReplyController, entry); } if (smartRepliesAndActions.smartActions != null) { - smartReplyView.addSmartActions(smartRepliesAndActions.smartActions); + smartReplyView.addSmartActions( + smartRepliesAndActions.smartActions, mSmartReplyController, entry); } smartReplyContainer.setVisibility(View.VISIBLE); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 7895a8e7831f..3b407b5f8a65 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -18,10 +18,7 @@ package com.android.systemui.statusbar.notification.row; import static android.app.AppOpsManager.OP_CAMERA; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; -import static android.service.notification.NotificationListenerService.Ranking - .USER_SENTIMENT_NEGATIVE; - -import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE; +import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import android.app.INotificationManager; import android.app.NotificationChannel; @@ -43,7 +40,6 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; -import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.plugins.ActivityStarter; @@ -188,13 +184,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx } else if (gutsView instanceof AppOpsInfo) { initializeAppOpsInfo(row, (AppOpsInfo) gutsView); } else if (gutsView instanceof NotificationInfo) { - int action; - if (item instanceof NotificationMenuRow.NotificationInfoMenuItem) { - action = ((NotificationMenuRow.NotificationInfoMenuItem) item).mAction; - } else { - action = ACTION_NONE; - } - initializeNotificationInfo(row, (NotificationInfo) gutsView, action); + initializeNotificationInfo(row, (NotificationInfo) gutsView); } return true; } catch (Exception e) { @@ -253,13 +243,11 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx * Sets up the {@link NotificationInfo} inside the notification row's guts. * @param row view to set up the guts for * @param notificationInfoView view to set up/bind within {@code row} - * @param action The action to take immediately upon binding, if any. */ @VisibleForTesting void initializeNotificationInfo( final ExpandableNotificationRow row, - NotificationInfo notificationInfoView, - @NotificationInfo.NotificationInfoAction int action) throws Exception { + NotificationInfo notificationInfoView) throws Exception { NotificationGuts guts = row.getGuts(); StatusBarNotification sbn = row.getStatusBarNotification(); String packageName = sbn.getPackageName(); @@ -303,8 +291,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx isForBlockingHelper, row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE, row.getEntry().noisy, - row.getEntry().importance, - action); + row.getEntry().importance); } 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 0d36d2c2f77c..213ac704b06a 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 @@ -187,14 +187,13 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G boolean isDeviceProvisioned, boolean isNonblockable, boolean isNoisy, - int importance, - @NotificationInfoAction int action) + int importance) throws RemoteException { bindNotification(pm, iNotificationManager, pkg, notificationChannel, numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick, onAppSettingsClick, isDeviceProvisioned, isNonblockable, false /* isBlockingHelper */, false /* isUserSentimentNegative */, isNoisy, - importance, action); + importance); } public void bindNotification( @@ -212,8 +211,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G boolean isForBlockingHelper, boolean isUserSentimentNegative, boolean isNoisy, - int importance, - @NotificationInfoAction int action) + int importance) throws RemoteException { mINotificationManager = iNotificationManager; mMetricsLogger = Dependency.get(MetricsLogger.class); @@ -255,10 +253,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G bindHeader(); bindPrompt(); bindButtons(); - - if (action != ACTION_NONE) { - swapContent(action, false /* don't animate */); - } } private void bindHeader() throws RemoteException { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java index b6ff6fc7f0e5..948d2a5e2a18 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java @@ -17,9 +17,6 @@ package com.android.systemui.statusbar.notification.row; import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION; -import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_BLOCK; -import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE; -import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_TOGGLE_SILENT; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -46,7 +43,6 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.AlphaOptimizedImageView; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent; -import com.android.systemui.statusbar.notification.row.NotificationInfo.NotificationInfoAction; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import java.util.ArrayList; @@ -73,7 +69,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl private Context mContext; private FrameLayout mMenuContainer; - private NotificationInfoMenuItem mInfoItem; + private NotificationMenuItem mInfoItem; private MenuItem mAppOpsItem; private MenuItem mSnoozeItem; private ArrayList<MenuItem> mLeftMenuItems; @@ -248,36 +244,30 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl if (!isForeground) { // Only show snooze for non-foreground notifications mSnoozeItem = createSnoozeItem(mContext); - mLeftMenuItems.add(mSnoozeItem); } - mInfoItem = createInfoItem(mContext); - if (!NotificationUtils.useNewInterruptionModel(mContext)) { - mLeftMenuItems.add(mInfoItem); - } - mAppOpsItem = createAppOpsItem(mContext); - mLeftMenuItems.add(mAppOpsItem); - if (NotificationUtils.useNewInterruptionModel(mContext)) { - if (!mParent.getIsNonblockable()) { - mRightMenuItems.add(createBlockItem(mContext, mInfoItem.getGutsView())); - } - // TODO(kprevas): this is duplicated logic - // but it's currently spread across NotificationGutsManager and NotificationInfo. - // Try to consolidate and reuse here. - boolean canToggleSilent = !mParent.getIsNonblockable() - && !isForeground - && mParent.getEntry().noisy; - if (canToggleSilent) { - int channelImportance = mParent.getEntry().channel.getImportance(); - int effectiveImportance = - channelImportance == NotificationManager.IMPORTANCE_UNSPECIFIED - ? mParent.getEntry().importance : channelImportance; - mRightMenuItems.add(createToggleSilentItem(mContext, mInfoItem.getGutsView(), - effectiveImportance < NotificationManager.IMPORTANCE_DEFAULT)); + int channelImportance = mParent.getEntry().channel.getImportance(); + int effectiveImportance = + channelImportance == NotificationManager.IMPORTANCE_UNSPECIFIED + ? mParent.getEntry().importance : channelImportance; + mInfoItem = createInfoItem(mContext, + effectiveImportance < NotificationManager.IMPORTANCE_DEFAULT); + } else { + mInfoItem = createInfoItem(mContext); + } + + if (!NotificationUtils.useNewInterruptionModel(mContext)) { + if (!isForeground) { + mRightMenuItems.add(mSnoozeItem); } + mRightMenuItems.add(mInfoItem); + mRightMenuItems.add(mAppOpsItem); + mLeftMenuItems.addAll(mRightMenuItems); } else { - mRightMenuItems.addAll(mLeftMenuItems); + mRightMenuItems.add(mInfoItem); + mRightMenuItems.add(mAppOpsItem); + mRightMenuItems.add(mSnoozeItem); } populateMenuViews(); @@ -634,13 +624,24 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl return snooze; } - static NotificationInfoMenuItem createInfoItem(Context context) { + static NotificationMenuItem createInfoItem(Context context) { Resources res = context.getResources(); String infoDescription = res.getString(R.string.notification_menu_gear_description); NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate( R.layout.notification_info, null, false); - return new NotificationInfoMenuItem(context, infoDescription, infoContent, - R.drawable.ic_settings, ACTION_NONE); + return new NotificationMenuItem(context, infoDescription, infoContent, + R.drawable.ic_settings); + } + + static NotificationMenuItem createInfoItem(Context context, boolean isCurrentlySilent) { + Resources res = context.getResources(); + String infoDescription = res.getString(R.string.notification_menu_gear_description); + NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate( + R.layout.notification_info, null, false); + int iconResId = isCurrentlySilent + ? R.drawable.ic_notifications_alert + : R.drawable.ic_notifications_silence; + return new NotificationMenuItem(context, infoDescription, infoContent, iconResId); } static MenuItem createAppOpsItem(Context context) { @@ -651,29 +652,6 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl return info; } - private static MenuItem createBlockItem(Context context, NotificationInfo gutsView) { - return new NotificationInfoMenuItem( - context, - context.getResources().getString(R.string.inline_stop_button), - gutsView, - R.drawable.ic_notification_block, - ACTION_BLOCK); - } - - private static MenuItem createToggleSilentItem(Context context, NotificationInfo gutsView, - boolean isCurrentlySilent) { - return new NotificationInfoMenuItem( - context, - isCurrentlySilent - ? context.getResources().getString(R.string.inline_silent_button_alert) - : context.getResources().getString(R.string.inline_silent_button_silent), - gutsView, - isCurrentlySilent - ? R.drawable.ic_notifications_alert - : R.drawable.ic_notifications_silence, - ACTION_TOGGLE_SILENT); - } - private void addMenuView(MenuItem item, ViewGroup parent) { View menuView = item.getMenuView(); if (menuView != null) { @@ -789,23 +767,4 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl return mContentDescription; } } - - /** A {@link NotificationMenuItem} with an associated {@link NotificationInfoAction}. */ - public static class NotificationInfoMenuItem extends NotificationMenuItem { - - @NotificationInfoAction - int mAction; - - public NotificationInfoMenuItem(Context context, String contentDescription, - NotificationInfo content, int iconResId, - @NotificationInfoAction int action) { - super(context, contentDescription, content, iconResId); - this.mAction = action; - } - - @Override - public NotificationInfo getGutsView() { - return (NotificationInfo) super.getGutsView(); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java index 1002f9e45b3c..b83ebc7f8ea1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.phone; -import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME; - import android.annotation.NonNull; import android.hardware.input.InputManager; import android.os.Handler; @@ -35,10 +33,8 @@ import com.android.systemui.recents.OverviewProxyService; */ public class NavigationBackAction extends NavigationGestureAction { - private static final String PULL_HOME_GO_BACK_PROP = "quickstepcontroller_homegoesback"; private static final String BACK_AFTER_END_PROP = "quickstepcontroller_homegoesbackwhenend"; - private static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled"; private static final long BACK_BUTTON_FADE_OUT_ALPHA = 60; private static final long BACK_GESTURE_POLL_TIMEOUT = 1000; @@ -60,23 +56,13 @@ public class NavigationBackAction extends NavigationGestureAction { } @Override - public int requiresTouchDownHitTarget() { - return HIT_TARGET_HOME; - } - - @Override - public boolean requiresDragWithHitTarget() { - return true; - } - - @Override public boolean canPerformAction() { return mProxySender.getBackButtonAlpha() > 0; } @Override public boolean isEnabled() { - return swipeHomeGoBackGestureEnabled(); + return !getGlobalBoolean(NavigationPrototypeController.NAVBAR_EXPERIMENTS_DISABLED); } @Override @@ -110,13 +96,8 @@ public class NavigationBackAction extends NavigationGestureAction { mNavigationBarView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); } - private boolean swipeHomeGoBackGestureEnabled() { - return !getGlobalBoolean(NAVBAR_EXPERIMENTS_DISABLED) - && getGlobalBoolean(PULL_HOME_GO_BACK_PROP); - } - private boolean shouldExecuteBackOnUp() { - return !getGlobalBoolean(NAVBAR_EXPERIMENTS_DISABLED) + return !getGlobalBoolean(NavigationPrototypeController.NAVBAR_EXPERIMENTS_DISABLED) && getGlobalBoolean(BACK_AFTER_END_PROP); } 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 5db43eae8443..33d022c83c16 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -77,6 +77,8 @@ import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.NavigationBarCompat; import com.android.systemui.shared.system.WindowManagerWrapper; +import com.android.systemui.statusbar.phone.NavigationPrototypeController.GestureAction; +import com.android.systemui.statusbar.phone.NavigationPrototypeController.OnPrototypeChangedListener; import com.android.systemui.statusbar.policy.DeadZone; import com.android.systemui.statusbar.policy.KeyButtonDrawable; @@ -146,6 +148,8 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav private RecentsOnboarding mRecentsOnboarding; private NotificationPanelView mPanelView; + private NavigationPrototypeController mPrototypeController; + private NavigationGestureAction[] mDefaultGestureMap; private QuickScrubAction mQuickScrubAction; private QuickStepAction mQuickStepAction; private NavigationBackAction mBackAction; @@ -261,6 +265,18 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } }; + private OnPrototypeChangedListener mPrototypeListener = new OnPrototypeChangedListener() { + @Override + public void onGestureRemap(int[] actions) { + updateNavigationGestures(); + } + + @Override + public void onBackButtonVisibilityChanged(boolean visible) { + getBackButton().setVisibility(visible ? VISIBLE : GONE); + } + }; + public NavigationBarView(Context context, AttributeSet attrs) { super(context, attrs); @@ -309,6 +325,14 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mQuickScrubAction = new QuickScrubAction(this, mOverviewProxyService); mQuickStepAction = new QuickStepAction(this, mOverviewProxyService); mBackAction = new NavigationBackAction(this, mOverviewProxyService); + mDefaultGestureMap = new NavigationGestureAction[] { + mQuickStepAction, null /* swipeDownAction*/, null /* swipeLeftAction */, + mQuickScrubAction + }; + + mPrototypeController = new NavigationPrototypeController(mHandler, mContext); + mPrototypeController.register(); + mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener); } public BarTransitions getBarTransitions() { @@ -323,8 +347,32 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mPanelView = panel; if (mGestureHelper instanceof QuickStepController) { ((QuickStepController) mGestureHelper).setComponents(this); - ((QuickStepController) mGestureHelper).setGestureActions(mQuickStepAction, - null /* swipeDownAction*/, mBackAction, mQuickScrubAction); + updateNavigationGestures(); + } + } + + private void updateNavigationGestures() { + if (mGestureHelper instanceof QuickStepController) { + final int[] assignedMap = mPrototypeController.getGestureActionMap(); + ((QuickStepController) mGestureHelper).setGestureActions( + getNavigationActionFromType(assignedMap[0], mDefaultGestureMap[0]), + getNavigationActionFromType(assignedMap[1], mDefaultGestureMap[1]), + getNavigationActionFromType(assignedMap[2], mDefaultGestureMap[2]), + getNavigationActionFromType(assignedMap[3], mDefaultGestureMap[3])); + } + } + + private NavigationGestureAction getNavigationActionFromType(@GestureAction int actionType, + NavigationGestureAction defaultAction) { + switch(actionType) { + case NavigationPrototypeController.ACTION_QUICKSTEP: + return mQuickStepAction; + case NavigationPrototypeController.ACTION_QUICKSCRUB: + return mQuickScrubAction; + case NavigationPrototypeController.ACTION_BACK: + return mBackAction; + default: + return defaultAction; } } @@ -1043,6 +1091,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav if (mGestureHelper != null) { mGestureHelper.destroy(); } + mPrototypeController.unregister(); setUpSwipeUpOnboarding(false); for (int i = 0; i < mButtonDispatchers.size(); ++i) { mButtonDispatchers.valueAt(i).onDestroy(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java new file mode 100644 index 000000000000..e8c0bf13644f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import android.annotation.IntDef; +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; + +import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Coordinates with the prototype settings plugin app that uses Settings.Global to allow different + * prototypes to run in the system. The class will handle communication changes from the settings + * app and call back to listeners. + */ +public class NavigationPrototypeController extends ContentObserver { + private static final String HIDE_BACK_BUTTON_SETTING = "quickstepcontroller_hideback"; + + static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled"; + private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map"; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK}) + @interface GestureAction {} + static final int ACTION_DEFAULT = 0; + static final int ACTION_QUICKSTEP = 1; + static final int ACTION_QUICKSCRUB = 2; + static final int ACTION_BACK = 3; + + private OnPrototypeChangedListener mListener; + + /** + * Each index corresponds to a different action set in QuickStepController + * {@see updateSwipeLTRBackSetting} + */ + private int[] mActionMap = new int[4]; + + private final Context mContext; + + public NavigationPrototypeController(Handler handler, Context context) { + super(handler); + mContext = context; + updateSwipeLTRBackSetting(); + } + + public void setOnPrototypeChangedListener(OnPrototypeChangedListener listener) { + mListener = listener; + } + + /** + * Observe all the settings to react to from prototype settings + */ + public void register() { + registerObserver(HIDE_BACK_BUTTON_SETTING); + registerObserver(GESTURE_MATCH_SETTING); + } + + /** + * Disable observing settings to react to from prototype settings + */ + public void unregister() { + mContext.getContentResolver().unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + super.onChange(selfChange, uri); + if (!selfChange && mListener != null) { + try { + final String path = uri.getPath(); + if (path.endsWith(GESTURE_MATCH_SETTING)) { + // Get the settings gesture map corresponding to each action + // {@see updateSwipeLTRBackSetting} + updateSwipeLTRBackSetting(); + mListener.onGestureRemap(mActionMap); + } else if (path.endsWith(HIDE_BACK_BUTTON_SETTING)) { + mListener.onBackButtonVisibilityChanged( + !getGlobalBool(HIDE_BACK_BUTTON_SETTING)); + } + } catch (SettingNotFoundException e) { + e.printStackTrace(); + } + } + } + + /** + * Retrieve the action map to apply to the quick step controller + * @return an action map + */ + int[] getGestureActionMap() { + return mActionMap; + } + + /** + * Since Settings.Global cannot pass arrays, use a string to represent each character as a + * gesture map to actions corresponding to {@see GestureAction}. The number is represented as: + * Number: [up] [down] [left] [right] + */ + private void updateSwipeLTRBackSetting() { + String value = Settings.Global.getString(mContext.getContentResolver(), + GESTURE_MATCH_SETTING); + if (value != null) { + for (int i = 0; i < mActionMap.length; ++i) { + mActionMap[i] = Character.getNumericValue(value.charAt(i)); + } + } + } + + private boolean getGlobalBool(String name) throws SettingNotFoundException { + return Settings.Global.getInt(mContext.getContentResolver(), name) == 1; + } + + private void registerObserver(String name) { + mContext.getContentResolver() + .registerContentObserver(Settings.Global.getUriFor(name), false, this); + } + + public interface OnPrototypeChangedListener { + void onGestureRemap(@GestureAction int[] actions); + void onBackButtonVisibilityChanged(boolean visible); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java index 0eff4d4adff5..497fdfb2deb1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java @@ -30,9 +30,9 @@ import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ import android.annotation.Nullable; import android.content.Context; -import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Matrix; +import android.graphics.Rect; import android.os.RemoteException; import android.provider.Settings; import android.util.Log; @@ -100,6 +100,7 @@ public class QuickStepController implements GestureHelper { private NavigationGestureAction mCurrentAction; private NavigationGestureAction[] mGestureActions = new NavigationGestureAction[MAX_GESTURES]; + private final Rect mLastLayoutRect = new Rect(); private final OverviewProxyService mOverviewEventSender; private final Context mContext; private final StatusBar mStatusBar; @@ -107,7 +108,6 @@ public class QuickStepController implements GestureHelper { private final Matrix mTransformLocalMatrix = new Matrix(); public QuickStepController(Context context) { - final Resources res = context.getResources(); mContext = context; mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class); mOverviewEventSender = Dependency.get(OverviewProxyService.class); @@ -142,6 +142,8 @@ public class QuickStepController implements GestureHelper { if (action != null) { action.setBarState(true, mNavBarPosition, mDragHPositive, mDragVPositive); action.onDarkIntensityChange(mDarkIntensity); + action.onLayout(true /* changed */, mLastLayoutRect.left, mLastLayoutRect.top, + mLastLayoutRect.right, mLastLayoutRect.bottom); } } } @@ -382,6 +384,7 @@ public class QuickStepController implements GestureHelper { action.onLayout(changed, left, top, right, bottom); } } + mLastLayoutRect.set(left, top, right, bottom); } @Override 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 82f9e0340490..408ab42a6f06 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -196,7 +196,6 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; @@ -722,7 +721,7 @@ public class StatusBar extends SystemUI implements DemoMode, IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface( ServiceManager.getService(Context.WALLPAPER_SERVICE)); try { - wallpaperManager.setInAmbientMode(false /* ambientMode */, false /* animated */); + wallpaperManager.setInAmbientMode(false /* ambientMode */, 0L /* duration */); } catch (RemoteException e) { // Just pass, nothing critical. } @@ -4324,7 +4323,14 @@ public class StatusBar extends SystemUI implements DemoMode, }, afterKeyguardGone); } + @Override public void startPendingIntentDismissingKeyguard(final PendingIntent intent) { + startPendingIntentDismissingKeyguard(intent, null); + } + + @Override + public void startPendingIntentDismissingKeyguard( + final PendingIntent intent, @Nullable final Runnable intentSentCallback) { final boolean afterKeyguardGone = intent.isActivity() && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(), mLockscreenUserManager.getCurrentUserId()); @@ -4343,6 +4349,9 @@ public class StatusBar extends SystemUI implements DemoMode, if (intent.isActivity()) { mAssistManager.hideAssist(); } + if (intentSentCallback != null) { + intentSentCallback.run(); + } }, afterKeyguardGone); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java index 88ff0780c974..f36066ce3794 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java @@ -208,12 +208,14 @@ public class SmartReplyView extends ViewGroup { * Add smart actions to be shown next to smart replies. Only the actions that fit into the * notification are shown. */ - public void addSmartActions(SmartActions smartActions) { + public void addSmartActions(SmartActions smartActions, + SmartReplyController smartReplyController, NotificationData.Entry entry) { int numSmartActions = smartActions.actions.size(); for (int n = 0; n < numSmartActions; n++) { Notification.Action action = smartActions.actions.get(n); if (action.actionIntent != null) { - Button actionButton = inflateActionButton(getContext(), this, action); + Button actionButton = inflateActionButton( + getContext(), this, n, smartActions, smartReplyController, entry); addView(actionButton); } } @@ -270,7 +272,10 @@ public class SmartReplyView extends ViewGroup { } @VisibleForTesting - Button inflateActionButton(Context context, ViewGroup root, Notification.Action action) { + Button inflateActionButton(Context context, ViewGroup root, int actionIndex, + SmartActions smartActions, SmartReplyController smartReplyController, + NotificationData.Entry entry) { + Notification.Action action = smartActions.actions.get(actionIndex); Button button = (Button) LayoutInflater.from(context).inflate( R.layout.smart_action_button, root, false); button.setText(action.title); @@ -283,7 +288,10 @@ public class SmartReplyView extends ViewGroup { button.setCompoundDrawables(iconDrawable, null, null, null); button.setOnClickListener(view -> - getActivityStarter().startPendingIntentDismissingKeyguard(action.actionIntent)); + getActivityStarter().startPendingIntentDismissingKeyguard( + action.actionIntent, + () -> smartReplyController.smartActionClicked( + entry, actionIndex, action, smartActions.fromAssistant))); // TODO(b/119010281): handle accessibility diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java index 6ac44628b52f..ec2319d80194 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java @@ -16,9 +16,8 @@ package com.android.systemui.doze; -import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -27,8 +26,8 @@ import android.app.IWallpaperManager; import android.os.RemoteException; import android.support.test.filters.SmallTest; -import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.DozeParameters; import org.junit.Before; @@ -59,14 +58,14 @@ public class DozeWallpaperStateTest extends SysuiTestCase { mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.DOZE_AOD); - verify(mIWallpaperManager).setInAmbientMode(eq(true), anyBoolean()); + verify(mIWallpaperManager).setInAmbientMode(eq(true), anyLong()); mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH); - verify(mIWallpaperManager).setInAmbientMode(eq(false), anyBoolean()); + verify(mIWallpaperManager).setInAmbientMode(eq(false), anyLong()); // Make sure we're sending false when AoD is off reset(mDozeParameters); mDozeWallpaperState.transitionTo(DozeMachine.State.FINISH, DozeMachine.State.DOZE_AOD); - verify(mIWallpaperManager).setInAmbientMode(eq(false), anyBoolean()); + verify(mIWallpaperManager).setInAmbientMode(eq(false), anyLong()); } @Test @@ -78,10 +77,12 @@ public class DozeWallpaperStateTest extends SysuiTestCase { mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.DOZE_AOD); - verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(true)); + verify(mIWallpaperManager).setInAmbientMode(eq(true), + eq((long) StackStateAnimator.ANIMATION_DURATION_WAKEUP)); mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH); - verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(true)); + verify(mIWallpaperManager).setInAmbientMode(eq(false), + eq((long) StackStateAnimator.ANIMATION_DURATION_WAKEUP)); } @Test @@ -93,24 +94,24 @@ public class DozeWallpaperStateTest extends SysuiTestCase { mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.DOZE_AOD); - verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(false)); + verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(0L)); mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH); - verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(false)); + verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(0L)); } @Test public void testTransitionTo_requestPulseIsAmbientMode() throws RemoteException { mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE, DozeMachine.State.DOZE_REQUEST_PULSE); - verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(false)); + verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(0L)); } @Test public void testTransitionTo_pulseIsAmbientMode() throws RemoteException { mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_REQUEST_PULSE, DozeMachine.State.DOZE_PULSING); - verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(false)); + verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(0L)); } @Test @@ -120,6 +121,7 @@ public class DozeWallpaperStateTest extends SysuiTestCase { reset(mIWallpaperManager); mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_PULSING, DozeMachine.State.FINISH); - verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(true)); + verify(mIWallpaperManager).setInAmbientMode(eq(false), + eq((long) StackStateAnimator.ANIMATION_DURATION_WAKEUP)); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java index b3b45ebc94b4..f94ba95999bf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java @@ -121,9 +121,9 @@ public class NotificationDataTest extends SysuiTestCase { when(mEnvironment.isDeviceProvisioned()).thenReturn(true); when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true); mNotificationData = new TestableNotificationData(); - Dependency.get(InitController.class).executePostInitTasks(); mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class)); mRow = new NotificationTestHelper(getContext()).createRow(); + Dependency.get(InitController.class).executePostInitTasks(); } @Test 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 7fee0ee8c664..f0fa7887a0f9 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 @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.row; import static com.google.common.truth.Truth.assertThat; -import static org.hamcrest.core.IsEqual.equalTo; import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index 279796919879..84bfae650ce3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -20,8 +20,7 @@ import static android.app.AppOpsManager.OP_CAMERA; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; -import static android.service.notification.NotificationListenerService.Ranking - .USER_SENTIMENT_NEGATIVE; +import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; @@ -54,7 +53,6 @@ import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.ArraySet; -import android.util.Log; import android.view.View; import com.android.systemui.SysuiTestCase; @@ -63,8 +61,7 @@ import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationTestHelper; import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.row.NotificationGutsManager - .OnSettingsClickListener; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -298,8 +295,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { when(row.getIsNonblockable()).thenReturn(false); StatusBarNotification statusBarNotification = row.getStatusBarNotification(); - mGutsManager.initializeNotificationInfo(row, notificationInfoView, - NotificationInfo.ACTION_NONE); + mGutsManager.initializeNotificationInfo(row, notificationInfoView); verify(notificationInfoView).bindNotification( any(PackageManager.class), @@ -316,8 +312,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(true) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */, eq(false) /*isNoisy */, - eq(0), - eq(NotificationInfo.ACTION_NONE)); + eq(0)); } @Test @@ -329,8 +324,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { when(row.getIsNonblockable()).thenReturn(false); StatusBarNotification statusBarNotification = row.getStatusBarNotification(); - mGutsManager.initializeNotificationInfo(row, notificationInfoView, - NotificationInfo.ACTION_NONE); + mGutsManager.initializeNotificationInfo(row, notificationInfoView); verify(notificationInfoView).bindNotification( any(PackageManager.class), @@ -347,8 +341,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(false) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */, eq(false) /*isNoisy */, - eq(0), - eq(NotificationInfo.ACTION_NONE)); + eq(0)); } @Test @@ -361,8 +354,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { when(row.getIsNonblockable()).thenReturn(false); StatusBarNotification statusBarNotification = row.getStatusBarNotification(); - mGutsManager.initializeNotificationInfo(row, notificationInfoView, - NotificationInfo.ACTION_NONE); + mGutsManager.initializeNotificationInfo(row, notificationInfoView); verify(notificationInfoView).bindNotification( any(PackageManager.class), @@ -379,8 +371,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(true) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */, eq(true) /*isNoisy */, - eq(0), - eq(NotificationInfo.ACTION_NONE)); + eq(0)); } @Test @@ -393,8 +384,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { when(row.getIsNonblockable()).thenReturn(false); StatusBarNotification statusBarNotification = row.getStatusBarNotification(); - mGutsManager.initializeNotificationInfo(row, notificationInfoView, - NotificationInfo.ACTION_NONE); + mGutsManager.initializeNotificationInfo(row, notificationInfoView); verify(notificationInfoView).bindNotification( any(PackageManager.class), @@ -411,8 +401,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(true) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */, eq(false) /*isNoisy */, - eq(IMPORTANCE_DEFAULT), - eq(NotificationInfo.ACTION_NONE)); + eq(IMPORTANCE_DEFAULT)); } @Test @@ -425,8 +414,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { StatusBarNotification statusBarNotification = row.getStatusBarNotification(); when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true); - mGutsManager.initializeNotificationInfo(row, notificationInfoView, - NotificationInfo.ACTION_NONE); + mGutsManager.initializeNotificationInfo(row, notificationInfoView); verify(notificationInfoView).bindNotification( any(PackageManager.class), @@ -443,8 +431,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(false) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */, eq(false) /*isNoisy */, - eq(0), - eq(NotificationInfo.ACTION_NONE)); + eq(0)); } @Test @@ -456,8 +443,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { when(row.getIsNonblockable()).thenReturn(false); StatusBarNotification statusBarNotification = row.getStatusBarNotification(); - mGutsManager.initializeNotificationInfo(row, notificationInfoView, - NotificationInfo.ACTION_BLOCK); + mGutsManager.initializeNotificationInfo(row, notificationInfoView); verify(notificationInfoView).bindNotification( any(PackageManager.class), @@ -474,8 +460,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(true) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */, eq(false) /*isNoisy */, - eq(0), - eq(NotificationInfo.ACTION_BLOCK)); + eq(0)); } @Test 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 985827a9cd54..3dd493f0cd44 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 @@ -187,7 +187,7 @@ public class NotificationInfoTest extends SysuiTestCase { when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.pkgname); assertTrue(textView.getText().toString().contains("App Name")); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); @@ -200,7 +200,7 @@ public class NotificationInfoTest extends SysuiTestCase { .thenReturn(iconDrawable); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon); assertEquals(iconDrawable, iconView.getDrawable()); } @@ -209,7 +209,7 @@ public class NotificationInfoTest extends SysuiTestCase { 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); + false, IMPORTANCE_DEFAULT); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); assertEquals(GONE, nameView.getVisibility()); final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider); @@ -228,7 +228,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); assertEquals(VISIBLE, nameView.getVisibility()); assertTrue(nameView.getText().toString().contains("Other")); @@ -240,7 +240,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(GONE, groupNameView.getVisibility()); final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider); @@ -257,7 +257,7 @@ public class NotificationInfoTest extends SysuiTestCase { .thenReturn(notificationChannelGroup); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(View.VISIBLE, groupNameView.getVisibility()); assertEquals("Test Group Name", groupNameView.getText()); @@ -269,7 +269,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_SetsTextChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(TEST_CHANNEL_NAME, textView.getText()); } @@ -278,7 +278,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true, - false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(GONE, textView.getVisibility()); } @@ -291,7 +291,7 @@ public class NotificationInfoTest extends SysuiTestCase { eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true, - false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(VISIBLE, textView.getVisibility()); } @@ -300,7 +300,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(VISIBLE, textView.getVisibility()); } @@ -309,7 +309,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_BlockButton() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final View block = mNotificationInfo.findViewById(R.id.block); final View toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); final View minimize = mNotificationInfo.findViewById(R.id.minimize); @@ -323,7 +323,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_DEFAULT); final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); assertEquals(VISIBLE, toggleSilent.getVisibility()); assertEquals( @@ -335,7 +335,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_LOW); final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); assertEquals(VISIBLE, toggleSilent.getVisibility()); assertEquals( @@ -347,7 +347,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_DEFAULT); final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); assertEquals(VISIBLE, toggleSilent.getVisibility()); assertEquals( @@ -360,7 +360,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_LOW); final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); assertEquals(VISIBLE, toggleSilent.getVisibility()); assertEquals( @@ -372,7 +372,7 @@ public class NotificationInfoTest extends SysuiTestCase { mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final View block = mNotificationInfo.findViewById(R.id.block); final View minimize = mNotificationInfo.findViewById(R.id.minimize); assertEquals(GONE, block.getVisibility()); @@ -387,7 +387,7 @@ public class NotificationInfoTest extends SysuiTestCase { (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); latch.countDown(); - }, null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + }, null, true, false, false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); settingsButton.performClick(); @@ -399,7 +399,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -411,7 +411,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); - }, null, false, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + }, null, false, false, false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -420,11 +420,11 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, (View v, NotificationChannel c, int appUid) -> { - }, null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + }, null, true, false, false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertEquals(View.VISIBLE, settingsButton.getVisibility()); } @@ -433,7 +433,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testLogBlockingHelperCounter_doesntLogForNormalGutsView() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent"); verify(mMetricsLogger, times(0)).count(anyString(), anyInt()); } @@ -442,7 +442,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true, - true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, true, false, IMPORTANCE_DEFAULT); mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent"); verify(mMetricsLogger, times(1)).count(anyString(), anyInt()); } @@ -455,7 +455,7 @@ public class NotificationInfoTest extends SysuiTestCase { (View v, NotificationChannel c, int appUid) -> { assertEquals(null, c); latch.countDown(); - }, null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + }, null, true, true, false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.info).performClick(); // Verify that listener was triggered. @@ -468,7 +468,7 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null, - null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + null, true, true, false, IMPORTANCE_DEFAULT); final TextView channelNameView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(GONE, channelNameView.getVisibility()); @@ -479,7 +479,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null, - null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + null, true, true, false, IMPORTANCE_DEFAULT); final TextView blockView = mNotificationInfo.findViewById(R.id.block); assertEquals(GONE, blockView.getVisibility()); } @@ -488,7 +488,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testbindNotification_BlockingHelper() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, false, - true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, true, false, IMPORTANCE_DEFAULT); final TextView view = mNotificationInfo.findViewById(R.id.block_prompt); assertEquals(View.VISIBLE, view.getVisibility()); assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText()); @@ -498,7 +498,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final TextView view = mNotificationInfo.findViewById(R.id.block_prompt); assertEquals(View.VISIBLE, view.getVisibility()); assertEquals(mContext.getString(R.string.notification_unblockable_desc), @@ -509,7 +509,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); @@ -520,7 +520,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); mTestableLooper.processAllMessages(); @@ -534,7 +534,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); mTestableLooper.processAllMessages(); @@ -548,7 +548,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); mTestableLooper.processAllMessages(); @@ -562,7 +562,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); mTestableLooper.processAllMessages(); @@ -576,7 +576,7 @@ public class NotificationInfoTest extends SysuiTestCase { int originalImportance = mNotificationChannel.getImportance(); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true, false); mTestableLooper.processAllMessages(); @@ -591,7 +591,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true, false); @@ -609,8 +609,8 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , - true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT, - NotificationInfo.ACTION_NONE); + true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT + ); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -631,8 +631,8 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */, - true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT, - NotificationInfo.ACTION_NONE); + true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT + ); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -653,8 +653,7 @@ public class NotificationInfoTest extends SysuiTestCase { null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */, false /* isNonblockable */, true /* isForBlockingHelper */, - true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT, - NotificationInfo.ACTION_NONE); + true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT); NotificationGuts guts = spy(new NotificationGuts(mContext, null)); when(guts.getWindowToken()).thenReturn(mock(IBinder.class)); @@ -682,8 +681,7 @@ public class NotificationInfoTest extends SysuiTestCase { 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */, false /* isNonblockable */, true /* isForBlockingHelper */, - true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT, - NotificationInfo.ACTION_NONE); + true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT); NotificationGuts guts = spy(new NotificationGuts(mContext, null)); when(guts.getWindowToken()).thenReturn(mock(IBinder.class)); @@ -712,7 +710,7 @@ public class NotificationInfoTest extends SysuiTestCase { null /* onSettingsClick */, null /* onAppSettingsClick */ , false /* isNonblockable */, true /* isForBlockingHelper */, true, true /* isUserSentimentNegative */, false /* isNoisy */, - IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true /* save */, false /* force */); @@ -731,8 +729,7 @@ public class NotificationInfoTest extends SysuiTestCase { null /* onSettingsClick */, null /* onAppSettingsClick */, true /* provisioned */, false /* isNonblockable */, true /* isForBlockingHelper */, - true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT, - NotificationInfo.ACTION_NONE); + true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); mTestableLooper.processAllMessages(); @@ -755,7 +752,7 @@ public class NotificationInfoTest extends SysuiTestCase { true /* isForBlockingHelper */, true, false /* isUserSentimentNegative */, - false /* isNoisy */, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false /* isNoisy */, IMPORTANCE_DEFAULT); NotificationGuts guts = mock(NotificationGuts.class); doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean()); mNotificationInfo.setGutsParent(guts); @@ -770,7 +767,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -784,7 +781,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -817,7 +814,7 @@ public class NotificationInfoTest extends SysuiTestCase { false /* isNonblockable */, true /* isForBlockingHelper */, true /* isUserSentimentNegative */, - false/* isNoisy */, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false/* isNoisy */, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -839,7 +836,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -854,7 +851,7 @@ public class NotificationInfoTest extends SysuiTestCase { mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -875,7 +872,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true, false); @@ -893,7 +890,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -915,7 +912,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -937,7 +934,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); waitForUndoButton(); @@ -958,7 +955,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); waitForUndoButton(); @@ -980,7 +977,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); waitForUndoButton(); @@ -1002,7 +999,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_LOW); mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); waitForUndoButton(); @@ -1023,7 +1020,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -1039,7 +1036,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -1056,7 +1053,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { - }, null, null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + }, null, null, true, true, false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); mTestableLooper.processAllMessages(); @@ -1074,8 +1071,8 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { saveImportance.run(); - }, null, null, true, false, false, IMPORTANCE_DEFAULT, - NotificationInfo.ACTION_NONE); + }, null, null, true, false, false, IMPORTANCE_DEFAULT + ); mNotificationInfo.findViewById(R.id.block).performClick(); mTestableLooper.processAllMessages(); @@ -1111,7 +1108,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, (View v, Intent intent) -> { latch.countDown(); - }, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + }, true, false, false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(View.VISIBLE, settingsLink.getVisibility()); settingsLink.performClick(); @@ -1139,7 +1136,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null, (View v, Intent intent) -> { latch.countDown(); - }, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + }, true, false, false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(View.VISIBLE, settingsLink.getVisibility()); settingsLink.performClick(); @@ -1158,7 +1155,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null, - null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + null, true, false, false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(GONE, settingsLink.getVisibility()); } @@ -1179,7 +1176,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(GONE, settingsLink.getVisibility()); } @@ -1202,7 +1199,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false, true, - true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, true, false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(GONE, settingsLink.getVisibility()); } @@ -1219,7 +1216,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -1232,7 +1229,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -1245,7 +1242,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); waitForUndoButton(); @@ -1259,7 +1256,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); waitForUndoButton(); @@ -1273,7 +1270,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -1285,7 +1282,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -1293,60 +1290,4 @@ public class NotificationInfoTest extends SysuiTestCase { waitForStopButton(); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); } - - @Test - public void testBindNotificationWithInitialBlockAction() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_BLOCK); - waitForUndoButton(); - mNotificationInfo.handleCloseControls(true, false); - - mTestableLooper.processAllMessages(); - ArgumentCaptor<NotificationChannel> updated = - ArgumentCaptor.forClass(NotificationChannel.class); - verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( - anyString(), eq(TEST_UID), updated.capture()); - assertTrue((updated.getValue().getUserLockedFields() - & USER_LOCKED_IMPORTANCE) != 0); - assertEquals(IMPORTANCE_NONE, updated.getValue().getImportance()); - } - - @Test - public void testBindNotificationWithInitialSilenceAction() throws Exception { - mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_TOGGLE_SILENT); - waitForUndoButton(); - mNotificationInfo.handleCloseControls(true, false); - - mTestableLooper.processAllMessages(); - ArgumentCaptor<NotificationChannel> updated = - ArgumentCaptor.forClass(NotificationChannel.class); - verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( - anyString(), eq(TEST_UID), updated.capture()); - assertTrue((updated.getValue().getUserLockedFields() - & USER_LOCKED_IMPORTANCE) != 0); - assertEquals(IMPORTANCE_LOW, updated.getValue().getImportance()); - } - - @Test - public void testBindNotificationWithInitialUnSilenceAction() throws Exception { - mNotificationChannel.setImportance(IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_LOW, NotificationInfo.ACTION_TOGGLE_SILENT); - waitForUndoButton(); - mNotificationInfo.handleCloseControls(true, false); - - mTestableLooper.processAllMessages(); - ArgumentCaptor<NotificationChannel> updated = - ArgumentCaptor.forClass(NotificationChannel.class); - verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( - anyString(), eq(TEST_UID), updated.capture()); - assertTrue((updated.getValue().getUserLockedFields() - & USER_LOCKED_IMPORTANCE) != 0); - assertEquals(IMPORTANCE_HIGH, updated.getValue().getImportance()); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java index b6e3fc172c69..df7aeab2ed38 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java @@ -62,6 +62,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicReference; @@ -430,12 +431,18 @@ public class SmartReplyViewTest extends SysuiTestCase { private void setSmartActions(String[] actionTitles) { mView.resetSmartSuggestions(mContainer); - mView.addSmartActions(new SmartReplyView.SmartActions(createActions(actionTitles), false)); + mView.addSmartActions( + new SmartReplyView.SmartActions(createActions(actionTitles), false), + mLogger, + mEntry); } private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) { setSmartReplies(choices); - mView.addSmartActions(new SmartReplyView.SmartActions(createActions(actionTitles), false)); + mView.addSmartActions( + new SmartReplyView.SmartActions(createActions(actionTitles), false), + mLogger, + mEntry); } private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) { @@ -553,7 +560,7 @@ public class SmartReplyViewTest extends SysuiTestCase { mView.getChildAt(2).performClick(); - verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any()); + verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any()); } @Test @@ -738,7 +745,9 @@ public class SmartReplyViewTest extends SysuiTestCase { } private Button inflateActionButton(Notification.Action action) { - return mView.inflateActionButton(getContext(), mView, action); + return mView.inflateActionButton(getContext(), mView, 0, + new SmartReplyView.SmartActions(Collections.singletonList(action), false), + mLogger, mEntry); } @Test diff --git a/packages/overlays/ExperimentNavigationBarDefaultOverlay/Android.mk b/packages/overlays/ExperimentNavigationBarDefaultOverlay/Android.mk new file mode 100644 index 000000000000..ecad42097738 --- /dev/null +++ b/packages/overlays/ExperimentNavigationBarDefaultOverlay/Android.mk @@ -0,0 +1,30 @@ +# +# 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. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_RRO_THEME := ExperimentNavigationBarDefault +LOCAL_CERTIFICATE := platform + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := ExperimentNavigationBarDefaultOverlay +LOCAL_SDK_VERSION := current + +include $(BUILD_RRO_PACKAGE)
\ No newline at end of file diff --git a/packages/overlays/ExperimentNavigationBarDefaultOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarDefaultOverlay/AndroidManifest.xml new file mode 100644 index 000000000000..1639fc5fc302 --- /dev/null +++ b/packages/overlays/ExperimentNavigationBarDefaultOverlay/AndroidManifest.xml @@ -0,0 +1,27 @@ +<!-- +/** + * 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.internal.experiment.navbar.default" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="android" + android:category="com.android.internal.experiment_navbar_default" + android:priority="1"/> + + <application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/> +</manifest>
\ No newline at end of file diff --git a/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/config.xml new file mode 100644 index 000000000000..d8b69cde863b --- /dev/null +++ b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/config.xml @@ -0,0 +1,24 @@ +<?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> + <!-- Height of the bottom navigation / system bar frame; navigation buttons height. --> + <dimen name="navigation_bar_frame_width">48dp</dimen> + <!-- Width of the navigation bar frame when it is placed vertically on the screen --> + <dimen name="navigation_bar_frame_height">48dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-si/strings.xml b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/strings.xml index a1abb64fbf47..c9332903be2e 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-si/strings.xml +++ b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/strings.xml @@ -1,5 +1,5 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- +<?xml version="1.0" encoding="utf-8"?> +<!-- /** * Copyright (c) 2018, The Android Open Source Project * @@ -15,9 +15,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"සිහින් සංචාලන තීරු අත්දැකීම"</string> -</resources> +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Name of overlay [CHAR LIMIT=64] --> + <string name="experiment_navigationbar_overlay">Default Navigation Bar Experiment (48dp)</string> +</resources>
\ No newline at end of file diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/AndroidManifest.xml index b4b2b161ad71..b4cc34f15af7 100644 --- a/packages/overlays/ExperimentNavigationBarFloatingOverlay/AndroidManifest.xml +++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/AndroidManifest.xml @@ -16,7 +16,7 @@ */ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.internal.experiment.navbar.floating" + package="com.android.internal.experiment.navbar.type.floating" android:versionCode="1" android:versionName="1.0"> <overlay android:targetPackage="android" diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values/config.xml index 6a5845321556..30bca3c00164 100644 --- a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values/config.xml +++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values/config.xml @@ -21,8 +21,4 @@ <dimen name="navigation_bar_height">0dp</dimen> <!-- Width of the navigation bar when it is placed vertically on the screen --> <dimen name="navigation_bar_width">0dp</dimen> - <!-- Height of the bottom navigation / system bar frame; navigation buttons height. --> - <dimen name="navigation_bar_frame_height">48dp</dimen> - <!-- Width of the navigation bar frame when it is placed vertically on the screen --> - <dimen name="navigation_bar_frame_width">48dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/ExperimentNavigationBarSlim24Overlay/Android.mk index e642a68f1a3a..58cf134fd04d 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/Android.mk @@ -17,14 +17,14 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_RRO_THEME := ExperimentNavigationBarSlim +LOCAL_RRO_THEME := ExperimentNavigationBarSlim24 LOCAL_CERTIFICATE := platform LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res -LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay +LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay24 LOCAL_SDK_VERSION := current include $(BUILD_RRO_PACKAGE)
\ No newline at end of file diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/AndroidManifest.xml index a1bd58274fc0..aee543a0b23b 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/AndroidManifest.xml @@ -16,11 +16,11 @@ */ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.internal.experiment.navbar.slim" + package="com.android.internal.experiment.navbar.slim24" android:versionCode="1" android:versionName="1.0"> <overlay android:targetPackage="android" - android:category="com.android.internal.experiment_navbar_slim" + android:category="com.android.internal.experiment_navbar_slim24" android:priority="1"/> <application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/> diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-af/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-af/strings.xml index 21a0003bdaa7..21a0003bdaa7 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-af/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-af/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-am/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-am/strings.xml index 6a7d644ff432..6a7d644ff432 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-am/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-am/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ar/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ar/strings.xml index 2cebf40c0cba..2cebf40c0cba 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ar/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ar/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-az/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-az/strings.xml index 266c051abc7d..266c051abc7d 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-az/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-az/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-b+sr+Latn/strings.xml index dc57a3f6a076..dc57a3f6a076 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-b+sr+Latn/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-b+sr+Latn/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-be/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-be/strings.xml index 8b53bcdfe9d7..8b53bcdfe9d7 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-be/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-be/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bg/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-bg/strings.xml index 4bf000e193c0..4bf000e193c0 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bg/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-bg/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bs/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-bs/strings.xml index 981420945aea..981420945aea 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bs/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-bs/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ca/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ca/strings.xml index 4a9b598158e6..4a9b598158e6 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ca/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ca/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-cs/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-cs/strings.xml index d923a3cc4376..d923a3cc4376 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-cs/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-cs/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-da/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-da/strings.xml index 12bb2f23fe1e..12bb2f23fe1e 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-da/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-da/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-de/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-de/strings.xml index 960a7d94e7ae..960a7d94e7ae 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-de/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-de/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-el/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-el/strings.xml index 119b7e611cf9..119b7e611cf9 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-el/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-el/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rAU/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rAU/strings.xml index 5ebf40348199..5ebf40348199 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rAU/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rAU/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rCA/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rCA/strings.xml index 5ebf40348199..5ebf40348199 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rCA/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rCA/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rGB/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rGB/strings.xml index 5ebf40348199..5ebf40348199 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rGB/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rGB/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rIN/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rIN/strings.xml index 5ebf40348199..5ebf40348199 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rIN/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rIN/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rXC/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rXC/strings.xml index 79884ce615cf..79884ce615cf 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rXC/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rXC/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es-rUS/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-es-rUS/strings.xml index 00b044459777..00b044459777 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es-rUS/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-es-rUS/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-es/strings.xml index 58bdaa26115b..58bdaa26115b 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-es/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-et/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-et/strings.xml index 4c5023cf1d14..4c5023cf1d14 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-et/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-et/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-eu/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-eu/strings.xml index fc16eeb8dc3e..fc16eeb8dc3e 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-eu/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-eu/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fa/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fa/strings.xml index e4246d79decb..e4246d79decb 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fa/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fa/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fi/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fi/strings.xml index 9385a470368f..9385a470368f 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fi/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fi/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr-rCA/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fr-rCA/strings.xml index d22848beda3d..d22848beda3d 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr-rCA/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fr-rCA/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fr/strings.xml index cfa1db2a06d3..cfa1db2a06d3 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fr/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-gl/strings.xml index d2194ce1a820..d2194ce1a820 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gl/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-gl/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hi/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hi/strings.xml index d51cee34f895..d51cee34f895 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hi/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hi/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hr/strings.xml index 410c58ef2cbc..410c58ef2cbc 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hr/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hr/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hu/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hu/strings.xml index d7eafed699ab..d7eafed699ab 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hu/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hu/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hy/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hy/strings.xml index b0200cab636b..b0200cab636b 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hy/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hy/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-in/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-in/strings.xml index 3e0bded06e68..3e0bded06e68 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-in/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-in/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-is/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-is/strings.xml index 03ccaf3de068..03ccaf3de068 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-is/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-is/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-it/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-it/strings.xml index f7c5d253e5ab..f7c5d253e5ab 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-it/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-it/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-iw/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-iw/strings.xml index 0d0ec2c7c2a5..0d0ec2c7c2a5 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-iw/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-iw/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ja/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ja/strings.xml index a3d6874e84f6..a3d6874e84f6 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ja/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ja/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ka/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ka/strings.xml index ffddf3bee802..ffddf3bee802 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ka/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ka/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-kk/strings.xml index f34ac0810127..f34ac0810127 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kk/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-kk/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-km/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-km/strings.xml index 114a78270d79..114a78270d79 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-km/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-km/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ko/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ko/strings.xml index fca02c3e49bd..fca02c3e49bd 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ko/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ko/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ky/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ky/strings.xml index 449de4fadfbc..449de4fadfbc 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ky/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ky/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lo/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lo/strings.xml index 6ec48ca13d19..6ec48ca13d19 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lo/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lo/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lt/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lt/strings.xml index 1df54aa3fc02..1df54aa3fc02 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lt/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lt/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lv/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lv/strings.xml index 5c6c5658406b..5c6c5658406b 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lv/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lv/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mk/strings.xml index 3517813f74ad..3517813f74ad 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mk/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mk/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mn/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mn/strings.xml index a2282c4c71c9..a2282c4c71c9 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mn/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mn/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mr/strings.xml index c714370cdc7e..c714370cdc7e 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mr/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mr/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ms/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ms/strings.xml index 68f831e39b46..68f831e39b46 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ms/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ms/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-my/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-my/strings.xml index 84db27962308..84db27962308 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-my/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-my/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nb/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-nb/strings.xml index e1ff863ac5c8..e1ff863ac5c8 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nb/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-nb/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-nl/strings.xml index 01190bc624c8..01190bc624c8 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nl/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-nl/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pl/strings.xml index 1742aadbcdd3..1742aadbcdd3 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pl/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pl/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rBR/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt-rBR/strings.xml index 22194b7e1ca5..22194b7e1ca5 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rBR/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt-rBR/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rPT/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt-rPT/strings.xml index f6c030983578..f6c030983578 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rPT/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt-rPT/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt/strings.xml index 22194b7e1ca5..22194b7e1ca5 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ro/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ro/strings.xml index e1655f27b977..e1655f27b977 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ro/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ro/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ru/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ru/strings.xml index cac66dc921b1..cac66dc921b1 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ru/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ru/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sk/strings.xml index 6a1ce4128cd9..6a1ce4128cd9 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sk/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sk/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sl/strings.xml index beab7b67f60e..beab7b67f60e 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sl/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sl/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sq/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sq/strings.xml index b7a28d57391d..b7a28d57391d 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sq/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sq/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sr/strings.xml index 048f649ddb7b..048f649ddb7b 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sr/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sr/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sv/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sv/strings.xml index b94438f6971a..b94438f6971a 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sv/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sv/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sw/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sw/strings.xml index 3a5a73cd0afa..3a5a73cd0afa 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sw/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sw/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-th/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-th/strings.xml index 945297bec53d..945297bec53d 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-th/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-th/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-tl/strings.xml index 0c8087c68b8b..0c8087c68b8b 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tl/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-tl/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-tr/strings.xml index a3ca75455391..a3ca75455391 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tr/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-tr/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-uk/strings.xml index 656c4a97bd6c..656c4a97bd6c 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uk/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-uk/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ur/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ur/strings.xml index bcd6bc3f257e..bcd6bc3f257e 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ur/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ur/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uz/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-uz/strings.xml index 0d40981784c8..0d40981784c8 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uz/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-uz/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-vi/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-vi/strings.xml index dad56b41ae39..dad56b41ae39 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-vi/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-vi/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rCN/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rCN/strings.xml index b2602e0e0093..b2602e0e0093 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rCN/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rCN/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rHK/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rHK/strings.xml index d5259d38ffa1..d5259d38ffa1 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rHK/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rHK/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rTW/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rTW/strings.xml index 6586a573b494..6586a573b494 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rTW/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rTW/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zu/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zu/strings.xml index 12d04d3b21f6..12d04d3b21f6 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zu/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zu/strings.xml diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/config.xml index 4c3571a1e7d4..58c653d23e44 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/config.xml @@ -18,11 +18,11 @@ --> <resources> <!-- Height of the bottom navigation / system bar. --> - <dimen name="navigation_bar_height">36dp</dimen> + <dimen name="navigation_bar_height">24dp</dimen> <!-- Width of the navigation bar when it is placed vertically on the screen --> - <dimen name="navigation_bar_width">36dp</dimen> + <dimen name="navigation_bar_width">24dp</dimen> <!-- Height of the bottom navigation / system bar frame; navigation buttons height. --> - <dimen name="navigation_bar_frame_width">36dp</dimen> + <dimen name="navigation_bar_frame_width">24dp</dimen> <!-- Width of the navigation bar frame when it is placed vertically on the screen --> - <dimen name="navigation_bar_frame_height">36dp</dimen> + <dimen name="navigation_bar_frame_height">24dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/strings.xml index 5ca9d155cb08..670bc5538474 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/strings.xml @@ -18,5 +18,5 @@ --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- Name of overlay [CHAR LIMIT=64] --> - <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string> + <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment (24dp)</string> </resources>
\ No newline at end of file diff --git a/packages/overlays/ExperimentNavigationBarSlim32Overlay/Android.mk b/packages/overlays/ExperimentNavigationBarSlim32Overlay/Android.mk new file mode 100644 index 000000000000..7ebbb7458922 --- /dev/null +++ b/packages/overlays/ExperimentNavigationBarSlim32Overlay/Android.mk @@ -0,0 +1,30 @@ +# +# 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. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_RRO_THEME := ExperimentNavigationBarSlim32 +LOCAL_CERTIFICATE := platform + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay32 +LOCAL_SDK_VERSION := current + +include $(BUILD_RRO_PACKAGE)
\ No newline at end of file diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-as/strings.xml b/packages/overlays/ExperimentNavigationBarSlim32Overlay/AndroidManifest.xml index 8cce57046c37..10cf6a15a782 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-as/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim32Overlay/AndroidManifest.xml @@ -1,5 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- +<!-- /** * Copyright (c) 2018, The Android Open Source Project * @@ -15,9 +14,14 @@ * 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.internal.experiment.navbar.slim32" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="android" + android:category="com.android.internal.experiment_navbar_slim32" + android:priority="1"/> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"লাহী নেভিগে’শ্বন বাৰ সম্পৰীক্ষা"</string> -</resources> + <application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/> +</manifest>
\ No newline at end of file diff --git a/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/config.xml new file mode 100644 index 000000000000..00dd8fecbf8c --- /dev/null +++ b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/config.xml @@ -0,0 +1,28 @@ +<?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> + <!-- Height of the bottom navigation / system bar. --> + <dimen name="navigation_bar_height">32dp</dimen> + <!-- Width of the navigation bar when it is placed vertically on the screen --> + <dimen name="navigation_bar_width">32dp</dimen> + <!-- Height of the bottom navigation / system bar frame; navigation buttons height. --> + <dimen name="navigation_bar_frame_width">32dp</dimen> + <!-- Width of the navigation bar frame when it is placed vertically on the screen --> + <dimen name="navigation_bar_frame_height">32dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ml/strings.xml b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/strings.xml index b65afe35950c..b48661c387ab 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ml/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/strings.xml @@ -1,5 +1,5 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- +<?xml version="1.0" encoding="utf-8"?> +<!-- /** * Copyright (c) 2018, The Android Open Source Project * @@ -15,9 +15,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"സ്ലിം നാവിഗേഷൻ ബാർ പരീക്ഷണം"</string> -</resources> +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Name of overlay [CHAR LIMIT=64] --> + <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment (32dp)</string> +</resources>
\ No newline at end of file diff --git a/packages/overlays/ExperimentNavigationBarSlim40Overlay/Android.mk b/packages/overlays/ExperimentNavigationBarSlim40Overlay/Android.mk new file mode 100644 index 000000000000..28354e3a3473 --- /dev/null +++ b/packages/overlays/ExperimentNavigationBarSlim40Overlay/Android.mk @@ -0,0 +1,30 @@ +# +# 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. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_RRO_THEME := ExperimentNavigationBarSlim40 +LOCAL_CERTIFICATE := platform + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay40 +LOCAL_SDK_VERSION := current + +include $(BUILD_RRO_PACKAGE)
\ No newline at end of file diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bn/strings.xml b/packages/overlays/ExperimentNavigationBarSlim40Overlay/AndroidManifest.xml index c0ab3b1b321d..ce8133f3ed40 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bn/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim40Overlay/AndroidManifest.xml @@ -1,5 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- +<!-- /** * Copyright (c) 2018, The Android Open Source Project * @@ -15,9 +14,14 @@ * 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.internal.experiment.navbar.slim40" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="android" + android:category="com.android.internal.experiment_navbar_slim40" + android:priority="1"/> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"স্লিম নেভিগেশন বার সম্পর্কিত পরীক্ষা"</string> -</resources> + <application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/> +</manifest>
\ No newline at end of file diff --git a/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/config.xml new file mode 100644 index 000000000000..4e65f3399114 --- /dev/null +++ b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/config.xml @@ -0,0 +1,28 @@ +<?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> + <!-- Height of the bottom navigation / system bar. --> + <dimen name="navigation_bar_height">40dp</dimen> + <!-- Width of the navigation bar when it is placed vertically on the screen --> + <dimen name="navigation_bar_width">40dp</dimen> + <!-- Height of the bottom navigation / system bar frame; navigation buttons height. --> + <dimen name="navigation_bar_frame_width">40dp</dimen> + <!-- Width of the navigation bar frame when it is placed vertically on the screen --> + <dimen name="navigation_bar_frame_height">40dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gu/strings.xml b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/strings.xml index 96418aec29bc..8fe3a5c17bba 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gu/strings.xml +++ b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/strings.xml @@ -1,5 +1,5 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- +<?xml version="1.0" encoding="utf-8"?> +<!-- /** * Copyright (c) 2018, The Android Open Source Project * @@ -15,9 +15,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"સ્લિમ નૅવિગેશન બારનો પ્રયોગ"</string> -</resources> +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Name of overlay [CHAR LIMIT=64] --> + <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment (40dp)</string> +</resources>
\ No newline at end of file diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kn/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kn/strings.xml deleted file mode 100644 index ccdddea95afa..000000000000 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kn/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?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 xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ಸ್ಲಿಮ್ ನ್ಯಾವಿಗೇಷನ್ ಬಾರ್ ಪ್ರಯೋಗ"</string> -</resources> diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ne/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ne/strings.xml deleted file mode 100644 index 6022b7f495d4..000000000000 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ne/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?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 xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"पातलो नेभिगेसन पट्टीको परीक्षण"</string> -</resources> diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-or/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-or/strings.xml deleted file mode 100644 index 1db9783d4503..000000000000 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-or/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?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 xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ସ୍ଲିମ୍ ନାଭିଗେସନ୍ ବାର୍ର ପ୍ରୟୋଗ"</string> -</resources> diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pa/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pa/strings.xml deleted file mode 100644 index a782f46b2e23..000000000000 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pa/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?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 xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ਸਲਿਮ ਦਿਸ਼ਾ-ਨਿਰਦੇਸ਼ ਪੱਟੀ ਪ੍ਰਯੋਗ"</string> -</resources> diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ta/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ta/strings.xml deleted file mode 100644 index 9e95c3872155..000000000000 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ta/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?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 xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"மெலிதான வழிசெலுத்துதல் பட்டி சோதனை"</string> -</resources> diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-te/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-te/strings.xml deleted file mode 100644 index d273ab744d9b..000000000000 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-te/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?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 xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"సన్నని నావిగేషన్ పట్టీ ప్రయోగం"</string> -</resources> diff --git a/services/backup/OWNERS b/services/backup/OWNERS index 645723e655f8..d1dbbffc6708 100644 --- a/services/backup/OWNERS +++ b/services/backup/OWNERS @@ -1,7 +1,5 @@ anniemeng@google.com -artikz@google.com brufino@google.com bryanmawhinney@google.com ctate@google.com jorlow@google.com -mkarpinski@google.com diff --git a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java index ab639c3c9ce1..2bca34d9cef5 100644 --- a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java +++ b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java @@ -16,7 +16,7 @@ package com.android.server.backup; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG_SCHEDULING; +import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING; import android.content.ContentResolver; import android.os.Handler; diff --git a/services/backup/java/com/android/server/backup/BackupManagerConstants.java b/services/backup/java/com/android/server/backup/BackupManagerConstants.java index e3fa0dc3c509..785d3ca8a4a2 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerConstants.java +++ b/services/backup/java/com/android/server/backup/BackupManagerConstants.java @@ -16,7 +16,7 @@ package com.android.server.backup; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG_SCHEDULING; +import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING; import android.app.AlarmManager; import android.content.ContentResolver; diff --git a/services/backup/java/com/android/server/backup/GlobalBackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 9a1662e702ae..0b06f286441a 100644 --- a/services/backup/java/com/android/server/backup/GlobalBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -62,7 +62,7 @@ import java.util.Set; * incoming calls to the appropriate per-user {@link UserBackupManagerService} to handle the * corresponding backup/restore operation. */ -public class GlobalBackupManagerService { +public class BackupManagerService { public static final String TAG = "BackupManagerService"; public static final boolean DEBUG = true; public static final boolean MORE_DEBUG = false; @@ -82,8 +82,8 @@ public class GlobalBackupManagerService { return sInstance; } - /** Helper to create the {@link GlobalBackupManagerService} instance. */ - public static GlobalBackupManagerService create( + /** Helper to create the {@link BackupManagerService} instance. */ + public static BackupManagerService create( Context context, Trampoline parent, HandlerThread backupThread) { @@ -116,7 +116,7 @@ public class GlobalBackupManagerService { // This dir on /cache is managed directly in init.rc File dataDir = new File(Environment.getDownloadCacheDirectory(), "backup_stage"); - return new GlobalBackupManagerService( + return new BackupManagerService( context, parent, backupThread, @@ -127,8 +127,8 @@ public class GlobalBackupManagerService { private UserBackupManagerService mUserBackupManagerService; - /** Instantiate a new instance of {@link GlobalBackupManagerService}. */ - public GlobalBackupManagerService( + /** Instantiate a new instance of {@link BackupManagerService}. */ + public BackupManagerService( Context context, Trampoline trampoline, HandlerThread backupThread, diff --git a/services/backup/java/com/android/server/backup/FileMetadata.java b/services/backup/java/com/android/server/backup/FileMetadata.java index 84d987de3b22..fe75041eeaca 100644 --- a/services/backup/java/com/android/server/backup/FileMetadata.java +++ b/services/backup/java/com/android/server/backup/FileMetadata.java @@ -16,7 +16,7 @@ package com.android.server.backup; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.TAG; import android.app.backup.BackupAgent; import android.util.Slog; diff --git a/services/backup/java/com/android/server/backup/FullBackupJob.java b/services/backup/java/com/android/server/backup/FullBackupJob.java index ce024765b4db..82638b4ecee4 100644 --- a/services/backup/java/com/android/server/backup/FullBackupJob.java +++ b/services/backup/java/com/android/server/backup/FullBackupJob.java @@ -61,7 +61,7 @@ public class FullBackupJob extends JobService { @Override public boolean onStartJob(JobParameters params) { mParams = params; - Trampoline service = GlobalBackupManagerService.getInstance(); + Trampoline service = BackupManagerService.getInstance(); return service.beginFullBackup(this); } @@ -69,7 +69,7 @@ public class FullBackupJob extends JobService { public boolean onStopJob(JobParameters params) { if (mParams != null) { mParams = null; - Trampoline service = GlobalBackupManagerService.getInstance(); + Trampoline service = BackupManagerService.getInstance(); service.endFullBackup(); } return false; diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java index 8156095cf576..f2e74352b004 100644 --- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java +++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java @@ -16,7 +16,7 @@ package com.android.server.backup; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG_SCHEDULING; +import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING; import android.app.AlarmManager; import android.app.job.JobInfo; @@ -118,7 +118,7 @@ public class KeyValueBackupJob extends JobService { } // Time to run a key/value backup! - Trampoline service = GlobalBackupManagerService.getInstance(); + Trampoline service = BackupManagerService.getInstance(); try { service.backupNow(); } catch (RemoteException e) {} diff --git a/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java b/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java index dd91381779e4..edc2379ff641 100644 --- a/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java +++ b/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java @@ -46,7 +46,7 @@ import java.util.Set; final class ProcessedPackagesJournal { private static final String TAG = "ProcessedPackagesJournal"; private static final String JOURNAL_FILE_NAME = "processed"; - private static final boolean DEBUG = GlobalBackupManagerService.DEBUG; + private static final boolean DEBUG = BackupManagerService.DEBUG; // using HashSet instead of ArraySet since we expect 100-500 elements range @GuardedBy("mProcessedPackages") diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java index 22edebc982dd..59629aac7b4d 100644 --- a/services/backup/java/com/android/server/backup/Trampoline.java +++ b/services/backup/java/com/android/server/backup/Trampoline.java @@ -16,7 +16,7 @@ package com.android.server.backup; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.TAG; import android.annotation.Nullable; import android.app.admin.DevicePolicyManager; @@ -52,12 +52,12 @@ import java.io.IOException; import java.io.PrintWriter; /** - * A proxy to the {@link GlobalBackupManagerService} implementation. + * A proxy to the {@link BackupManagerService} implementation. * - * <p>This is an external interface to the {@link GlobalBackupManagerService} which is being - * accessed via published binder {@link GlobalBackupManagerService.Lifecycle}. This lets us turn - * down the heavy implementation object on the fly without disturbing binders that have been cached - * somewhere in the system. + * <p>This is an external interface to the {@link BackupManagerService} which is being accessed via + * published binder {@link BackupManagerService.Lifecycle}. This lets us turn down the heavy + * implementation object on the fly without disturbing binders that have been cached somewhere in + * the system. * * <p>Trampoline determines whether the backup service is available. It can be disabled in the * following two ways: @@ -89,7 +89,7 @@ public class Trampoline extends IBackupManager.Stub { private final boolean mGlobalDisable; private final Object mStateLock = new Object(); - private volatile GlobalBackupManagerService mService; + private volatile BackupManagerService mService; private HandlerThread mHandlerThread; public Trampoline(Context context) { @@ -116,12 +116,12 @@ public class Trampoline extends IBackupManager.Stub { return mContext; } - protected GlobalBackupManagerService createBackupManagerService() { - return GlobalBackupManagerService.create(mContext, this, mHandlerThread); + protected BackupManagerService createBackupManagerService() { + return BackupManagerService.create(mContext, this, mHandlerThread); } /** - * Initialize {@link GlobalBackupManagerService} if the backup service is not disabled. Only the + * Initialize {@link BackupManagerService} if the backup service is not disabled. Only the * system user can initialize the service. */ /* package */ void initializeService(int userId) { @@ -145,11 +145,10 @@ public class Trampoline extends IBackupManager.Stub { } /** - * Called from {@link GlobalBackupManagerService.Lifecycle} when the system user is unlocked. - * Attempts to initialize {@link GlobalBackupManagerService} and set backup state for the system - * user. + * Called from {@link BackupManagerService.Lifecycle} when the system user is unlocked. Attempts + * to initialize {@link BackupManagerService} and set backup state for the system user. * - * @see GlobalBackupManagerService#unlockSystemUser() + * @see BackupManagerService#unlockSystemUser() */ void unlockSystemUser() { mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND); @@ -162,7 +161,7 @@ public class Trampoline extends IBackupManager.Stub { initializeService(UserHandle.USER_SYSTEM); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - GlobalBackupManagerService service = mService; + BackupManagerService service = mService; if (service != null) { Slog.i(TAG, "Unlocking system user"); service.unlockSystemUser(); @@ -234,7 +233,7 @@ public class Trampoline extends IBackupManager.Stub { @Override public void dataChanged(String packageName) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.dataChanged(packageName); } @@ -243,7 +242,7 @@ public class Trampoline extends IBackupManager.Stub { @Override public void initializeTransports(String[] transportNames, IBackupObserver observer) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.initializeTransports(transportNames, observer); } @@ -252,7 +251,7 @@ public class Trampoline extends IBackupManager.Stub { @Override public void clearBackupData(String transportName, String packageName) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.clearBackupData(transportName, packageName); } @@ -260,7 +259,7 @@ public class Trampoline extends IBackupManager.Stub { @Override public void agentConnected(String packageName, IBinder agent) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.agentConnected(packageName, agent); } @@ -268,7 +267,7 @@ public class Trampoline extends IBackupManager.Stub { @Override public void agentDisconnected(String packageName) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.agentDisconnected(packageName); } @@ -276,7 +275,7 @@ public class Trampoline extends IBackupManager.Stub { @Override public void restoreAtInstall(String packageName, int token) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.restoreAtInstall(packageName, token); } @@ -284,7 +283,7 @@ public class Trampoline extends IBackupManager.Stub { @Override public void setBackupEnabled(boolean isEnabled) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.setBackupEnabled(isEnabled); } @@ -292,7 +291,7 @@ public class Trampoline extends IBackupManager.Stub { @Override public void setAutoRestore(boolean doAutoRestore) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.setAutoRestore(doAutoRestore); } @@ -300,7 +299,7 @@ public class Trampoline extends IBackupManager.Stub { @Override public void setBackupProvisioned(boolean isProvisioned) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.setBackupProvisioned(isProvisioned); } @@ -308,25 +307,25 @@ public class Trampoline extends IBackupManager.Stub { @Override public boolean isBackupEnabled() throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.isBackupEnabled() : false; } @Override public boolean setBackupPassword(String currentPw, String newPw) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.setBackupPassword(currentPw, newPw) : false; } @Override public boolean hasBackupPassword() throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.hasBackupPassword() : false; } @Override public void backupNow() throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.backupNow(); } @@ -337,7 +336,7 @@ public class Trampoline extends IBackupManager.Stub { boolean includeShared, boolean doWidgets, boolean allApps, boolean allIncludesSystem, boolean doCompress, boolean doKeyValue, String[] packageNames) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.adbBackup(fd, includeApks, includeObbs, includeShared, doWidgets, allApps, allIncludesSystem, doCompress, doKeyValue, packageNames); @@ -346,7 +345,7 @@ public class Trampoline extends IBackupManager.Stub { @Override public void fullTransportBackup(String[] packageNames) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.fullTransportBackup(packageNames); } @@ -354,7 +353,7 @@ public class Trampoline extends IBackupManager.Stub { @Override public void adbRestore(ParcelFileDescriptor fd) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.adbRestore(fd); } @@ -364,7 +363,7 @@ public class Trampoline extends IBackupManager.Stub { public void acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword, String encryptionPassword, IFullBackupRestoreObserver observer) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.acknowledgeAdbBackupOrRestore(token, allow, curPassword, encryptionPassword, observer); @@ -373,7 +372,7 @@ public class Trampoline extends IBackupManager.Stub { @Override public String getCurrentTransport() throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.getCurrentTransport() : null; } @@ -384,25 +383,25 @@ public class Trampoline extends IBackupManager.Stub { @Override @Nullable public ComponentName getCurrentTransportComponent() { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.getCurrentTransportComponent() : null; } @Override public String[] listAllTransports() throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.listAllTransports() : null; } @Override public ComponentName[] listAllTransportComponents() throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.listAllTransportComponents() : null; } @Override public String[] getTransportWhitelist() { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.getTransportWhitelist() : null; } @@ -414,7 +413,7 @@ public class Trampoline extends IBackupManager.Stub { String currentDestinationString, @Nullable Intent dataManagementIntent, String dataManagementLabel) { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.updateTransportAttributes( transportComponent, @@ -428,14 +427,14 @@ public class Trampoline extends IBackupManager.Stub { @Override public String selectBackupTransport(String transport) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.selectBackupTransport(transport) : null; } @Override public void selectBackupTransportAsync(ComponentName transport, ISelectBackupTransportCallback listener) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.selectBackupTransportAsync(transport, listener); } else { @@ -451,38 +450,38 @@ public class Trampoline extends IBackupManager.Stub { @Override public Intent getConfigurationIntent(String transport) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.getConfigurationIntent(transport) : null; } @Override public String getDestinationString(String transport) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.getDestinationString(transport) : null; } @Override public Intent getDataManagementIntent(String transport) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.getDataManagementIntent(transport) : null; } @Override public String getDataManagementLabel(String transport) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.getDataManagementLabel(transport) : null; } @Override public IRestoreSession beginRestoreSession(String packageName, String transportID) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.beginRestoreSession(packageName, transportID) : null; } @Override public void opComplete(int token, long result) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.opComplete(token, result); } @@ -490,26 +489,26 @@ public class Trampoline extends IBackupManager.Stub { @Override public long getAvailableRestoreToken(String packageName) { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.getAvailableRestoreToken(packageName) : 0; } @Override public boolean isAppEligibleForBackup(String packageName) { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.isAppEligibleForBackup(packageName) : false; } @Override public String[] filterAppsEligibleForBackup(String[] packages) { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.filterAppsEligibleForBackup(packages) : null; } @Override public int requestBackup(String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor, int flags) throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc == null) { return BackupManager.ERROR_BACKUP_NOT_ALLOWED; } @@ -518,7 +517,7 @@ public class Trampoline extends IBackupManager.Stub { @Override public void cancelBackups() throws RemoteException { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.cancelBackups(); } @@ -528,7 +527,7 @@ public class Trampoline extends IBackupManager.Stub { public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.dump(fd, pw, args); } else { @@ -539,12 +538,12 @@ public class Trampoline extends IBackupManager.Stub { // Full backup/restore entry points - non-Binder; called directly // by the full-backup scheduled job /* package */ boolean beginFullBackup(FullBackupJob scheduledJob) { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.beginFullBackup(scheduledJob) : false; } /* package */ void endFullBackup() { - GlobalBackupManagerService svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.endFullBackup(); } diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 4855ae0358bc..fe16afe864ac 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -18,10 +18,10 @@ package com.android.server.backup; import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG_SCHEDULING; -import static com.android.server.backup.GlobalBackupManagerService.MORE_DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.DEBUG; +import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING; +import static com.android.server.backup.BackupManagerService.MORE_DEBUG; +import static com.android.server.backup.BackupManagerService.TAG; import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT; import static com.android.server.backup.internal.BackupHandler.MSG_FULL_CONFIRMATION_TIMEOUT; import static com.android.server.backup.internal.BackupHandler.MSG_OP_COMPLETE; @@ -2665,7 +2665,7 @@ public class UserBackupManagerService { boolean wasEnabled = mEnabled; synchronized (this) { // TODO(b/118520567): Clean up writing backup enabled logic. - GlobalBackupManagerService.writeBackupEnableState(enable, UserHandle.USER_SYSTEM); + BackupManagerService.writeBackupEnableState(enable, UserHandle.USER_SYSTEM); mEnabled = enable; } diff --git a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java index 725bc74a66c6..bace1aa638ec 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java +++ b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java @@ -1,7 +1,7 @@ package com.android.server.backup.fullbackup; -import static com.android.server.backup.GlobalBackupManagerService.MORE_DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.MORE_DEBUG; +import static com.android.server.backup.BackupManagerService.TAG; import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_VERSION; import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_VERSION; import static com.android.server.backup.UserBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN; diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java index 9b484bc3ca29..5e923393843e 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java +++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java @@ -16,9 +16,9 @@ package com.android.server.backup.fullbackup; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.MORE_DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.DEBUG; +import static com.android.server.backup.BackupManagerService.MORE_DEBUG; +import static com.android.server.backup.BackupManagerService.TAG; import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME; import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME; import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT; diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java index 24784ea92c73..e14253702d55 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java +++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java @@ -16,8 +16,8 @@ package com.android.server.backup.fullbackup; -import static com.android.server.backup.GlobalBackupManagerService.MORE_DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.MORE_DEBUG; +import static com.android.server.backup.BackupManagerService.TAG; import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT; import android.app.backup.IBackupManager; diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupTask.java index 0ed75bb4a1b4..8f6923b6c05b 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupTask.java +++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupTask.java @@ -16,7 +16,7 @@ package com.android.server.backup.fullbackup; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.TAG; import android.app.backup.IFullBackupRestoreObserver; import android.os.RemoteException; diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java index 2f7687fc7009..43a80c45d6f7 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java +++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java @@ -16,10 +16,10 @@ package com.android.server.backup.fullbackup; +import static com.android.server.backup.BackupManagerService.DEBUG; +import static com.android.server.backup.BackupManagerService.MORE_DEBUG; +import static com.android.server.backup.BackupManagerService.TAG; import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.MORE_DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.TAG; import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_HEADER_MAGIC; import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_VERSION; import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE; diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java index 0d14e7ef3e55..5b449c5b400e 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java +++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java @@ -16,9 +16,9 @@ package com.android.server.backup.fullbackup; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG_SCHEDULING; -import static com.android.server.backup.GlobalBackupManagerService.MORE_DEBUG; +import static com.android.server.backup.BackupManagerService.DEBUG; +import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING; +import static com.android.server.backup.BackupManagerService.MORE_DEBUG; import static com.android.server.backup.UserBackupManagerService.OP_PENDING; import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP; import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT; diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java index fd0946679655..ba153bf90ebe 100644 --- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java +++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java @@ -16,9 +16,9 @@ package com.android.server.backup.internal; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.MORE_DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.DEBUG; +import static com.android.server.backup.BackupManagerService.MORE_DEBUG; +import static com.android.server.backup.BackupManagerService.TAG; import android.app.backup.RestoreSet; import android.content.Intent; diff --git a/services/backup/java/com/android/server/backup/internal/PerformClearTask.java b/services/backup/java/com/android/server/backup/internal/PerformClearTask.java index 2bad5fe7ae1e..5ffa795d87f0 100644 --- a/services/backup/java/com/android/server/backup/internal/PerformClearTask.java +++ b/services/backup/java/com/android/server/backup/internal/PerformClearTask.java @@ -16,7 +16,7 @@ package com.android.server.backup.internal; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.TAG; import android.content.pm.PackageInfo; import android.util.Slog; diff --git a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java index 1637e559acb7..6b78fbf60899 100644 --- a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java +++ b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java @@ -16,7 +16,7 @@ package com.android.server.backup.internal; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.TAG; import android.annotation.Nullable; import android.app.AlarmManager; diff --git a/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java b/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java index eab86629e75b..7e2ac796d343 100644 --- a/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java +++ b/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java @@ -16,8 +16,8 @@ package com.android.server.backup.internal; -import static com.android.server.backup.GlobalBackupManagerService.MORE_DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.MORE_DEBUG; +import static com.android.server.backup.BackupManagerService.TAG; import android.database.ContentObserver; import android.os.Handler; diff --git a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java index d869f044f5a4..2a5d913226b9 100644 --- a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java +++ b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java @@ -16,9 +16,9 @@ package com.android.server.backup.internal; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.MORE_DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.DEBUG; +import static com.android.server.backup.BackupManagerService.MORE_DEBUG; +import static com.android.server.backup.BackupManagerService.TAG; import static com.android.server.backup.UserBackupManagerService.RUN_BACKUP_ACTION; import static com.android.server.backup.internal.BackupHandler.MSG_RUN_BACKUP; diff --git a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java index 880e608a2fdb..38870cba4812 100644 --- a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java +++ b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java @@ -16,8 +16,8 @@ package com.android.server.backup.internal; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.DEBUG; +import static com.android.server.backup.BackupManagerService.TAG; import static com.android.server.backup.UserBackupManagerService.RUN_INITIALIZE_ACTION; import android.content.BroadcastReceiver; diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java index 437abd22d249..535c7cb29980 100644 --- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java +++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java @@ -28,8 +28,8 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.server.EventLogTags; +import com.android.server.backup.BackupManagerService; import com.android.server.backup.DataChangedJournal; -import com.android.server.backup.GlobalBackupManagerService; import com.android.server.backup.UserBackupManagerService; import com.android.server.backup.remote.RemoteResult; import com.android.server.backup.utils.BackupManagerMonitorUtils; @@ -54,8 +54,8 @@ import java.util.List; @VisibleForTesting public class KeyValueBackupReporter { @VisibleForTesting static final String TAG = "KeyValueBackupTask"; - private static final boolean DEBUG = GlobalBackupManagerService.DEBUG; - @VisibleForTesting static final boolean MORE_DEBUG = GlobalBackupManagerService.MORE_DEBUG; + private static final boolean DEBUG = BackupManagerService.DEBUG; + @VisibleForTesting static final boolean MORE_DEBUG = BackupManagerService.MORE_DEBUG; static void onNewThread(String threadName) { if (DEBUG) { diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java index 5c05371aedda..e273b329d51a 100644 --- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java +++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java @@ -16,8 +16,8 @@ package com.android.server.backup.restore; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.MORE_DEBUG; +import static com.android.server.backup.BackupManagerService.DEBUG; +import static com.android.server.backup.BackupManagerService.MORE_DEBUG; import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT; import static com.android.server.backup.internal.BackupHandler.MSG_RUN_GET_RESTORE_SETS; import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE; diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java index 8196e709f2ee..e4890e009abb 100644 --- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java +++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java @@ -16,8 +16,8 @@ package com.android.server.backup.restore; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.MORE_DEBUG; +import static com.android.server.backup.BackupManagerService.DEBUG; +import static com.android.server.backup.BackupManagerService.MORE_DEBUG; import android.util.Slog; diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java index ee08902aca05..0d26ea56faa2 100644 --- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java +++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java @@ -16,9 +16,9 @@ package com.android.server.backup.restore; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.MORE_DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.DEBUG; +import static com.android.server.backup.BackupManagerService.MORE_DEBUG; +import static com.android.server.backup.BackupManagerService.TAG; import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME; import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME; import static com.android.server.backup.UserBackupManagerService.OP_TYPE_RESTORE_WAIT; diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java index 381252dafe07..c9042566cf67 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java @@ -16,11 +16,11 @@ package com.android.server.backup.restore; +import static com.android.server.backup.BackupManagerService.DEBUG; +import static com.android.server.backup.BackupManagerService.MORE_DEBUG; +import static com.android.server.backup.BackupManagerService.TAG; import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT; import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.MORE_DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.TAG; import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_HEADER_MAGIC; import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_VERSION; import static com.android.server.backup.UserBackupManagerService.SETTINGS_PACKAGE; diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index 7530356fff4d..f7efad604e35 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -16,9 +16,9 @@ package com.android.server.backup.restore; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.MORE_DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.DEBUG; +import static com.android.server.backup.BackupManagerService.MORE_DEBUG; +import static com.android.server.backup.BackupManagerService.TAG; import static com.android.server.backup.UserBackupManagerService.KEY_WIDGET_STATE; import static com.android.server.backup.UserBackupManagerService.OP_TYPE_RESTORE_WAIT; import static com.android.server.backup.UserBackupManagerService.PACKAGE_MANAGER_SENTINEL; diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java index 2452e4868c80..e465c7e5264f 100644 --- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java +++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java @@ -16,8 +16,8 @@ package com.android.server.backup.utils; -import static com.android.server.backup.GlobalBackupManagerService.MORE_DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.MORE_DEBUG; +import static com.android.server.backup.BackupManagerService.TAG; import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE; import android.annotation.Nullable; diff --git a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java index 8b931d43dfe0..6f083760980d 100644 --- a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java +++ b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java @@ -18,8 +18,8 @@ package com.android.server.backup.utils; import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.DEBUG; +import static com.android.server.backup.BackupManagerService.TAG; import android.annotation.Nullable; import android.app.backup.BackupManagerMonitor; diff --git a/services/backup/java/com/android/server/backup/utils/BackupObserverUtils.java b/services/backup/java/com/android/server/backup/utils/BackupObserverUtils.java index 9674c3db9c75..c0cf2ef86920 100644 --- a/services/backup/java/com/android/server/backup/utils/BackupObserverUtils.java +++ b/services/backup/java/com/android/server/backup/utils/BackupObserverUtils.java @@ -16,8 +16,8 @@ package com.android.server.backup.utils; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.DEBUG; +import static com.android.server.backup.BackupManagerService.TAG; import android.app.backup.BackupProgress; import android.app.backup.IBackupObserver; diff --git a/services/backup/java/com/android/server/backup/utils/FullBackupRestoreObserverUtils.java b/services/backup/java/com/android/server/backup/utils/FullBackupRestoreObserverUtils.java index 92cdf0de48c7..fa856ce2c6de 100644 --- a/services/backup/java/com/android/server/backup/utils/FullBackupRestoreObserverUtils.java +++ b/services/backup/java/com/android/server/backup/utils/FullBackupRestoreObserverUtils.java @@ -16,7 +16,7 @@ package com.android.server.backup.utils; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.TAG; import android.app.backup.IFullBackupRestoreObserver; import android.os.RemoteException; diff --git a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java index a6fdbf004726..dbe3cd9225b5 100644 --- a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java +++ b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java @@ -16,7 +16,7 @@ package com.android.server.backup.utils; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.TAG; import android.os.ParcelFileDescriptor; import android.util.Slog; diff --git a/services/backup/java/com/android/server/backup/utils/PasswordUtils.java b/services/backup/java/com/android/server/backup/utils/PasswordUtils.java index 65adf4ee267d..a7eb644713ba 100644 --- a/services/backup/java/com/android/server/backup/utils/PasswordUtils.java +++ b/services/backup/java/com/android/server/backup/utils/PasswordUtils.java @@ -16,7 +16,7 @@ package com.android.server.backup.utils; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.TAG; import android.util.Slog; diff --git a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java index 91567d7aa0e5..df7e6d45ba0f 100644 --- a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java +++ b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java @@ -16,8 +16,8 @@ package com.android.server.backup.utils; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.DEBUG; +import static com.android.server.backup.BackupManagerService.TAG; import android.content.Context; import android.content.IIntentReceiver; diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java index c295684d0732..0f4b6810f15b 100644 --- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java +++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java @@ -34,9 +34,9 @@ import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_SYSTEM_APP_NO import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH; import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER; -import static com.android.server.backup.GlobalBackupManagerService.DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.MORE_DEBUG; -import static com.android.server.backup.GlobalBackupManagerService.TAG; +import static com.android.server.backup.BackupManagerService.DEBUG; +import static com.android.server.backup.BackupManagerService.MORE_DEBUG; +import static com.android.server.backup.BackupManagerService.TAG; import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME; import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_VERSION; import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME; diff --git a/services/core/Android.bp b/services/core/Android.bp index 617430090998..784d398a2b3f 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -32,6 +32,10 @@ java_library_static { "android.hardware.tv.cec-V1.0-java", ], + required: [ + "gps_debug.conf", + ], + static_libs: [ "time_zone_distro", "time_zone_distro_installer", @@ -69,3 +73,9 @@ java_library { name: "services.core", static_libs: ["services.core.priorityboosted"], } + + +prebuilt_etc { + name: "gps_debug.conf", + src: "java/com/android/server/location/gps_debug.conf", +} diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 60bbca833925..bb3b9f72d55e 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2099,14 +2099,7 @@ public class ConnectivityService extends IConnectivityManager.Stub return new MockableSystemProperties(); } - // TODO: Replace nai and newLp with TcpBufferSizes and check default network before calling - // this method. - private void updateTcpBufferSizes(NetworkAgentInfo nai, LinkProperties newLp) { - if (isDefaultNetwork(nai) == false) { - return; - } - - String tcpBufferSizes = newLp.getTcpBufferSizes(); + private void updateTcpBufferSizes(String tcpBufferSizes) { String[] values = null; if (tcpBufferSizes != null) { values = tcpBufferSizes.split(","); @@ -4798,7 +4791,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // for (LinkProperties lp : newLp.getStackedLinks()) { // updateMtu(lp, null); // } - updateTcpBufferSizes(networkAgent, newLp); + if (isDefaultNetwork(networkAgent)) { + updateTcpBufferSizes(newLp.getTcpBufferSizes()); + } updateRoutes(newLp, oldLp, netId); updateDnses(newLp, oldLp, netId); @@ -5289,7 +5284,7 @@ public class ConnectivityService extends IConnectivityManager.Stub notifyLockdownVpn(newNetwork); handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy()); - updateTcpBufferSizes(newNetwork, new LinkProperties(newNetwork.linkProperties)); + updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes()); mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers()); notifyIfacesChangedForNetworkStats(); } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 923ac0063baf..0e6f8dda44f6 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -2686,24 +2686,35 @@ class StorageManagerService extends IStorageManager.Stub class AppFuseMountScope extends AppFuseBridge.MountScope { boolean opened = false; - public AppFuseMountScope(int uid, int pid, int mountId) { - super(uid, pid, mountId); + public AppFuseMountScope(int uid, int mountId) { + super(uid, mountId); } @Override public ParcelFileDescriptor open() throws NativeDaemonConnectorException { try { return new ParcelFileDescriptor( - mVold.mountAppFuse(uid, Process.myPid(), mountId)); + mVold.mountAppFuse(uid, mountId)); } catch (Exception e) { throw new NativeDaemonConnectorException("Failed to mount", e); } } @Override + public ParcelFileDescriptor openFile(int mountId, int fileId, int flags) + throws NativeDaemonConnectorException { + try { + return new ParcelFileDescriptor( + mVold.openAppFuseFile(uid, mountId, fileId, flags)); + } catch (Exception e) { + throw new NativeDaemonConnectorException("Failed to open", e); + } + } + + @Override public void close() throws Exception { if (opened) { - mVold.unmountAppFuse(uid, Process.myPid(), mountId); + mVold.unmountAppFuse(uid, mountId); opened = false; } } @@ -2713,7 +2724,6 @@ class StorageManagerService extends IStorageManager.Stub public @Nullable AppFuseMount mountProxyFileDescriptorBridge() { Slog.v(TAG, "mountProxyFileDescriptorBridge"); final int uid = Binder.getCallingUid(); - final int pid = Binder.getCallingPid(); while (true) { synchronized (mAppFuseLock) { @@ -2727,7 +2737,7 @@ class StorageManagerService extends IStorageManager.Stub final int name = mNextAppFuseName++; try { return new AppFuseMount( - name, mAppFuseBridge.addBridge(new AppFuseMountScope(uid, pid, name))); + name, mAppFuseBridge.addBridge(new AppFuseMountScope(uid, name))); } catch (FuseUnavailableMountException e) { if (newlyCreated) { // If newly created bridge fails, it's a real error. @@ -2748,14 +2758,13 @@ class StorageManagerService extends IStorageManager.Stub public @Nullable ParcelFileDescriptor openProxyFileDescriptor( int mountId, int fileId, int mode) { Slog.v(TAG, "mountProxyFileDescriptor"); - final int pid = Binder.getCallingPid(); try { synchronized (mAppFuseLock) { if (mAppFuseBridge == null) { Slog.e(TAG, "FuseBridge has not been created"); return null; } - return mAppFuseBridge.openFile(pid, mountId, fileId, mode); + return mAppFuseBridge.openFile(mountId, fileId, mode); } } catch (FuseUnavailableMountException | InterruptedException error) { Slog.v(TAG, "The mount point has already been invalid", error); diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index d1b56e9afa4e..e80e9e1b594e 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -105,6 +105,7 @@ public class Watchdog extends Thread { "android.hardware.camera.provider@2.4::ICameraProvider", "android.hardware.graphics.allocator@2.0::IAllocator", "android.hardware.graphics.composer@2.1::IComposer", + "android.hardware.health@2.0::IHealth", "android.hardware.media.omx@1.0::IOmx", "android.hardware.media.omx@1.0::IOmxStore", "android.hardware.sensors@1.0::ISensors", diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 5c77f0a3ad47..8571ae6b07f6 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -16,6 +16,8 @@ package com.android.server.am; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK; + import android.content.ContentResolver; import android.database.ContentObserver; import android.net.Uri; @@ -26,8 +28,6 @@ import android.util.Slog; import java.io.PrintWriter; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK; - /** * Settings constants that can modify the activity manager's behavior. */ @@ -222,6 +222,10 @@ final class ActivityManagerConstants extends ContentObserver { // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED volatile boolean mFlagActivityStartsLoggingEnabled; + // Indicates whether the background activity starts is enabled. + // Controlled by Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED + volatile boolean mFlagBackgroundActivityStartsEnabled; + private final ActivityManagerService mService; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -256,6 +260,10 @@ final class ActivityManagerConstants extends ContentObserver { private static final Uri ACTIVITY_STARTS_LOGGING_ENABLED_URI = Settings.Global.getUriFor( Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED); + private static final Uri BACKGROUND_ACTIVITY_STARTS_ENABLED_URI = + Settings.Global.getUriFor( + Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED); + public ActivityManagerConstants(ActivityManagerService service, Handler handler) { super(handler); mService = service; @@ -266,8 +274,10 @@ final class ActivityManagerConstants extends ContentObserver { mResolver = resolver; mResolver.registerContentObserver(ACTIVITY_MANAGER_CONSTANTS_URI, false, this); mResolver.registerContentObserver(ACTIVITY_STARTS_LOGGING_ENABLED_URI, false, this); + mResolver.registerContentObserver(BACKGROUND_ACTIVITY_STARTS_ENABLED_URI, false, this); updateConstants(); updateActivityStartsLoggingEnabled(); + updateBackgroundActivityStartsEnabled(); } public void setOverrideMaxCachedProcesses(int value) { @@ -290,6 +300,8 @@ final class ActivityManagerConstants extends ContentObserver { updateConstants(); } else if (ACTIVITY_STARTS_LOGGING_ENABLED_URI.equals(uri)) { updateActivityStartsLoggingEnabled(); + } else if (BACKGROUND_ACTIVITY_STARTS_ENABLED_URI.equals(uri)) { + updateBackgroundActivityStartsEnabled(); } } @@ -373,6 +385,11 @@ final class ActivityManagerConstants extends ContentObserver { Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED, 0) == 1; } + private void updateBackgroundActivityStartsEnabled() { + mFlagBackgroundActivityStartsEnabled = Settings.Global.getInt(mResolver, + Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED, 1) == 1; + } + private void updateMaxCachedProcesses() { CUR_MAX_CACHED_PROCESSES = mOverrideMaxCachedProcesses < 0 ? MAX_CACHED_PROCESSES : mOverrideMaxCachedProcesses; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 80e73132f542..b62f648956dd 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -19204,6 +19204,10 @@ public class ActivityManagerService extends IActivityManager.Stub return mConstants.mFlagActivityStartsLoggingEnabled; } + public boolean isBackgroundActivityStartsEnabled() { + return mConstants.mFlagBackgroundActivityStartsEnabled; + } + public void reportCurKeyguardUsageEvent(boolean keyguardShowing) { synchronized(ActivityManagerService.this) { ActivityManagerService.this.reportGlobalUsageEventLocked(keyguardShowing diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING index b817669ce70c..48b414581d45 100644 --- a/services/core/java/com/android/server/am/TEST_MAPPING +++ b/services/core/java/com/android/server/am/TEST_MAPPING @@ -1,17 +1,6 @@ { "presubmit": [ { - "name": "CtsActivityManagerDeviceTestCases", - "options": [ - { - "include-annotation": "android.platform.test.annotations.Presubmit" - }, - { - "exclude-annotation": "android.support.test.filters.FlakyTest" - } - ] - }, - { "name": "CtsActivityManagerDeviceSdk25TestCases", "options": [ { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 67d27c94f41b..6cde4adb9f11 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -649,6 +649,9 @@ public class AudioService extends IAudioService.Stub private String mEnabledSurroundFormats; private boolean mSurroundModeChanged; + @GuardedBy("mSettingsLock") + private int mAssistantUid; + // Intent "extra" data keys. public static final String CONNECT_INTENT_KEY_PORT_NAME = "portName"; public static final String CONNECT_INTENT_KEY_STATE = "state"; @@ -1079,6 +1082,10 @@ public class AudioService extends IAudioService.Stub AudioSystem.setForceUse(AudioSystem.FOR_DOCK, forDock); sendEncodedSurroundMode(mContentResolver, "onAudioServerDied"); sendEnabledSurroundFormats(mContentResolver, true); + updateAssistantUId(true); + } + synchronized (mAccessibilityServiceUidsLock) { + AudioSystem.setA11yServicesUids(mAccessibilityServiceUids); } synchronized (mHdmiClientLock) { if (mHdmiManager != null && mHdmiTvClient != null) { @@ -1404,6 +1411,39 @@ public class AudioService extends IAudioService.Stub } } + @GuardedBy("mSettingsLock") + private void updateAssistantUId(boolean forceUpdate) { + int assistantUid = 0; + + // Consider assistants in the following order of priority: + // 1) voice interaction service + // 2) assistant + String assistantName = Settings.Secure.getStringForUser( + mContentResolver, + Settings.Secure.VOICE_INTERACTION_SERVICE, UserHandle.USER_CURRENT); + if (TextUtils.isEmpty(assistantName)) { + assistantName = Settings.Secure.getStringForUser( + mContentResolver, + Settings.Secure.ASSISTANT, UserHandle.USER_CURRENT); + } + if (!TextUtils.isEmpty(assistantName)) { + String packageName = ComponentName.unflattenFromString(assistantName).getPackageName(); + if (!TextUtils.isEmpty(packageName)) { + try { + assistantUid = mContext.getPackageManager().getPackageUid(packageName, 0); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, + "updateAssistantUId() could not find UID for package: " + packageName); + } + } + } + + if (assistantUid != mAssistantUid || forceUpdate) { + AudioSystem.setAssistantUid(assistantUid); + mAssistantUid = assistantUid; + } + } + private void readPersistedSettings() { final ContentResolver cr = mContentResolver; @@ -1447,6 +1487,7 @@ public class AudioService extends IAudioService.Stub readDockAudioSettings(cr); sendEncodedSurroundMode(cr, "readPersistedSettings"); sendEnabledSurroundFormats(cr, true); + updateAssistantUId(true); } mMuteAffectedStreams = System.getIntForUser(cr, @@ -5811,6 +5852,9 @@ public class AudioService extends IAudioService.Stub mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS); mContentResolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS), false, this); + + mContentResolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.VOICE_INTERACTION_SERVICE), false, this); } @Override @@ -5832,6 +5876,7 @@ public class AudioService extends IAudioService.Stub updateMasterMono(mContentResolver); updateEncodedSurroundOutput(); sendEnabledSurroundFormats(mContentResolver, mSurroundModeChanged); + updateAssistantUId(false); } } @@ -7658,6 +7703,7 @@ public class AudioService extends IAudioService.Stub mAccessibilityServiceUids = uids.toArray(); } } + AudioSystem.setA11yServicesUids(mAccessibilityServiceUids); } } } diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java index 33525fdc52d2..f2c539cb257c 100644 --- a/services/core/java/com/android/server/display/ColorFade.java +++ b/services/core/java/com/android/server/display/ColorFade.java @@ -16,16 +16,7 @@ package com.android.server.display; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; - import android.content.Context; -import android.graphics.PixelFormat; import android.graphics.SurfaceTexture; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener; @@ -34,20 +25,29 @@ import android.opengl.EGLConfig; import android.opengl.EGLContext; import android.opengl.EGLDisplay; import android.opengl.EGLSurface; -import android.opengl.GLES20; import android.opengl.GLES11Ext; +import android.opengl.GLES20; import android.util.Slog; import android.view.DisplayInfo; -import android.view.Surface.OutOfResourcesException; import android.view.Surface; +import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; import android.view.SurfaceSession; -import libcore.io.Streams; - import com.android.server.LocalServices; import com.android.server.policy.WindowManagerPolicy; +import libcore.io.Streams; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + /** * <p> * Animates a screen transition from on to off or off to on by applying @@ -569,37 +569,31 @@ final class ColorFade { mSurfaceSession = new SurfaceSession(); } - SurfaceControl.openTransaction(); - try { - if (mSurfaceControl == null) { - try { - int flags; - if (mMode == MODE_FADE) { - flags = SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN; - } else { - flags = SurfaceControl.OPAQUE | SurfaceControl.HIDDEN; - } - mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession) - .setName("ColorFade") - .setSize(mDisplayWidth, mDisplayHeight) - .setFlags(flags) - .build(); - } catch (OutOfResourcesException ex) { - Slog.e(TAG, "Unable to create surface.", ex); - return false; + if (mSurfaceControl == null) { + Transaction t = new Transaction(); + try { + final SurfaceControl.Builder builder = + new SurfaceControl.Builder(mSurfaceSession).setName("ColorFade"); + if (mMode == MODE_FADE) { + builder.setColorLayer(true); + } else { + builder.setBufferSize(mDisplayWidth, mDisplayHeight); } + mSurfaceControl = builder.build(); + } catch (OutOfResourcesException ex) { + Slog.e(TAG, "Unable to create surface.", ex); + return false; + } - mSurfaceControl.setLayerStack(mDisplayLayerStack); - mSurfaceControl.setSize(mDisplayWidth, mDisplayHeight); - mSurface = new Surface(); - mSurface.copyFrom(mSurfaceControl); + t.setLayerStack(mSurfaceControl, mDisplayLayerStack); + t.setWindowCrop(mSurfaceControl, mDisplayWidth, mDisplayHeight); + mSurface = new Surface(); + mSurface.copyFrom(mSurfaceControl); - mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal, - mDisplayId, mSurfaceControl); - mSurfaceLayout.onDisplayTransaction(); - } - } finally { - SurfaceControl.closeTransaction(); + mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal, + mDisplayId, mSurfaceControl); + mSurfaceLayout.onDisplayTransaction(t); + t.apply(); } return true; } @@ -746,7 +740,7 @@ final class ColorFade { } @Override - public void onDisplayTransaction() { + public void onDisplayTransaction(Transaction t) { synchronized (this) { if (mSurfaceControl == null) { return; @@ -755,21 +749,21 @@ final class ColorFade { DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId); switch (displayInfo.rotation) { case Surface.ROTATION_0: - mSurfaceControl.setPosition(0, 0); - mSurfaceControl.setMatrix(1, 0, 0, 1); + t.setPosition(mSurfaceControl, 0, 0); + t.setMatrix(mSurfaceControl, 1, 0, 0, 1); break; case Surface.ROTATION_90: - mSurfaceControl.setPosition(0, displayInfo.logicalHeight); - mSurfaceControl.setMatrix(0, -1, 1, 0); + t.setPosition(mSurfaceControl, 0, displayInfo.logicalHeight); + t.setMatrix(mSurfaceControl, 0, -1, 1, 0); break; case Surface.ROTATION_180: - mSurfaceControl.setPosition(displayInfo.logicalWidth, + t.setPosition(mSurfaceControl, displayInfo.logicalWidth, displayInfo.logicalHeight); - mSurfaceControl.setMatrix(-1, 0, 0, -1); + t.setMatrix(mSurfaceControl, -1, 0, 0, -1); break; case Surface.ROTATION_270: - mSurfaceControl.setPosition(displayInfo.logicalWidth, 0); - mSurfaceControl.setMatrix(0, 1, -1, 0); + t.setPosition(mSurfaceControl, displayInfo.logicalWidth, 0); + t.setMatrix(mSurfaceControl, 0, 1, -1, 0); break; } } diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index 7bfe9ce7017c..6ee5665b9e42 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -225,6 +225,8 @@ abstract class DisplayDevice { viewport.deviceHeight = isRotated ? info.width : info.height; viewport.uniqueId = info.uniqueId; + // TODO(b/112898898) Use an actual port here. + viewport.physicalPort = null; } /** diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index d04fa237a599..360a7d105cce 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -497,7 +497,7 @@ public final class DisplayManagerService extends SystemService { // List is self-synchronized copy-on-write. for (DisplayTransactionListener listener : mDisplayTransactionListeners) { - listener.onDisplayTransaction(); + listener.onDisplayTransaction(t); } } diff --git a/services/core/java/com/android/server/input/ConfigurationProcessor.java b/services/core/java/com/android/server/input/ConfigurationProcessor.java new file mode 100644 index 000000000000..970e86acf8b8 --- /dev/null +++ b/services/core/java/com/android/server/input/ConfigurationProcessor.java @@ -0,0 +1,121 @@ +/* + * 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.input; + +import android.text.TextUtils; +import android.util.Pair; +import android.util.Slog; +import android.util.Xml; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParser; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + + +class ConfigurationProcessor { + private static final String TAG = "ConfigurationProcessor"; + + static List<String> processExcludedDeviceNames(InputStream xml) throws Exception { + List<String> names = new ArrayList<>(); + try (InputStreamReader confReader = new InputStreamReader(xml)) { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(confReader); + XmlUtils.beginDocument(parser, "devices"); + while (true) { + XmlUtils.nextElement(parser); + if (!"device".equals(parser.getName())) { + break; + } + String name = parser.getAttributeValue(null, "name"); + if (name != null) { + names.add(name); + } + } + } + return names; + } + + /** + * Parse the configuration for input port associations. + * + * Configuration format: + * <code> + * <ports> + * <port display="0" input="usb-xhci-hcd.0.auto-1.4.3/input0" /> + * <port display="1" input="usb-xhci-hcd.0.auto-1.4.2/input0" /> + * </ports> + * </code> + * + * In this example, any input device that has physical port of + * "usb-xhci-hcd.0.auto-1.4.3/input0" will be associated with a display + * that has the physical port "0". If such a display does not exist, the input device + * will be disabled and no input events will be dispatched from that input device until a + * matching display appears. Likewise, an input device that has port "..1.4.2.." will have + * its input events forwarded to a display that has physical port of "1". + * + * Note: display port must be a numeric value, and this is checked at runtime for validity. + * At the same time, it is specified as a string for simplicity. + * + * Note: do not confuse "display id" with "display port". + * The "display port" is the physical port on which the display is connected. This could + * be something like HDMI0, HDMI1, etc. For virtual displays, "display port" will be null. + * The "display id" is a way to identify a particular display, and is not a stable API. + * All displays, including virtual ones, will have a display id. + * + * Return the pairs of associations. The first item in the pair is the input port, + * the second item in the pair is the display port. + */ + @VisibleForTesting + static List<Pair<String, String>> processInputPortAssociations(InputStream xml) + throws Exception { + List<Pair<String, String>> associations = new ArrayList<>(); + try (InputStreamReader confReader = new InputStreamReader(xml)) { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(confReader); + XmlUtils.beginDocument(parser, "ports"); + + while (true) { + XmlUtils.nextElement(parser); + String entryName = parser.getName(); + if (!"port".equals(entryName)) { + break; + } + String inputPort = parser.getAttributeValue(null, "input"); + String displayPort = parser.getAttributeValue(null, "display"); + if (TextUtils.isEmpty(inputPort) || TextUtils.isEmpty(displayPort)) { + // This is likely an error by an OEM during device configuration + Slog.wtf(TAG, "Ignoring incomplete entry"); + continue; + } + try { + Integer.parseUnsignedInt(displayPort); + } catch (NumberFormatException e) { + Slog.wtf(TAG, "Display port should be an integer"); + continue; + } + associations.add(new Pair<>(inputPort, displayPort)); + } + } + return associations; + } +} diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 3339a49c5ed0..d96b6cba119b 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -64,18 +64,18 @@ import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; import android.util.Log; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; -import android.util.Xml; import android.view.Display; import android.view.IInputFilter; import android.view.IInputFilterHost; import android.view.IWindow; -import android.view.InputChannel; import android.view.InputApplicationHandle; -import android.view.InputWindowHandle; +import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; +import android.view.InputWindowHandle; import android.view.KeyEvent; import android.view.PointerIcon; import android.view.Surface; @@ -97,14 +97,13 @@ import com.android.server.policy.WindowManagerPolicy; import libcore.io.IoUtils; import libcore.io.Streams; -import org.xmlpull.v1.XmlPullParser; - import java.io.File; import java.io.FileDescriptor; +import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.ArrayList; @@ -124,6 +123,7 @@ public class InputManagerService extends IInputManager.Stub static final boolean DEBUG = false; private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml"; + private static final String PORT_ASSOCIATIONS_PATH = "etc/input-port-associations.xml"; private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1; private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 2; @@ -1852,11 +1852,9 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. - private String[] getExcludedDeviceNames() { - ArrayList<String> names = new ArrayList<String>(); - + private static String[] getExcludedDeviceNames() { + List<String> names = new ArrayList<>(); // Read partner-provided list of excluded input devices - XmlPullParser parser = null; // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system". final File[] baseDirs = { Environment.getRootDirectory(), @@ -1864,33 +1862,52 @@ public class InputManagerService extends IInputManager.Stub }; for (File baseDir: baseDirs) { File confFile = new File(baseDir, EXCLUDED_DEVICES_PATH); - FileReader confreader = null; try { - confreader = new FileReader(confFile); - parser = Xml.newPullParser(); - parser.setInput(confreader); - XmlUtils.beginDocument(parser, "devices"); - - while (true) { - XmlUtils.nextElement(parser); - if (!"device".equals(parser.getName())) { - break; - } - String name = parser.getAttributeValue(null, "name"); - if (name != null) { - names.add(name); - } - } + InputStream stream = new FileInputStream(confFile); + names.addAll(ConfigurationProcessor.processExcludedDeviceNames(stream)); } catch (FileNotFoundException e) { // It's ok if the file does not exist. } catch (Exception e) { - Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e); - } finally { - try { if (confreader != null) confreader.close(); } catch (IOException e) { } + Slog.e(TAG, "Could not parse '" + confFile.getAbsolutePath() + "'", e); } } + return names.toArray(new String[0]); + } + + /** + * Flatten a list of pairs into a list, with value positioned directly next to the key + * @return Flattened list + */ + private static <T> List<T> flatten(@NonNull List<Pair<T, T>> pairs) { + List<T> list = new ArrayList<>(pairs.size() * 2); + for (Pair<T, T> pair : pairs) { + list.add(pair.first); + list.add(pair.second); + } + return list; + } + + /** + * Ports are highly platform-specific, so only allow these to be specified in the vendor + * directory. + */ + // Native callback + private static String[] getInputPortAssociations() { + File baseDir = Environment.getVendorDirectory(); + File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH); - return names.toArray(new String[names.size()]); + try { + InputStream stream = new FileInputStream(confFile); + List<Pair<String, String>> associations = + ConfigurationProcessor.processInputPortAssociations(stream); + List<String> associationList = flatten(associations); + return associationList.toArray(new String[0]); + } catch (FileNotFoundException e) { + // Most of the time, file will not exist, which is expected. + } catch (Exception e) { + Slog.e(TAG, "Could not parse '" + confFile.getAbsolutePath() + "'", e); + } + return new String[0]; } // Native callback. diff --git a/services/core/java/com/android/server/location/gps_debug.conf b/services/core/java/com/android/server/location/gps_debug.conf new file mode 100644 index 000000000000..34ce96f3c8b3 --- /dev/null +++ b/services/core/java/com/android/server/location/gps_debug.conf @@ -0,0 +1,52 @@ +# Sample file for use for on device debug override only +# Prefer frameworks/base/core/res/res/values/config.xml and +# frameworks/base/core/res/res/values-mcc*-mnc*/config.xml + +################################ +##### AGPS server settings ##### +################################ +# FOR SUPL SUPPORT, set the following +# SUPL_HOST=supl.google.com or IP +# SUPL_PORT=7275 + +# supl version 2.0 +# SUPL_VER=0x20000 + +#SUPL_MODE is a bit mask set in config.xml per carrier by default. +#If it is uncommented here, this value will overwrite the value from +#config.xml. +#MSA=0X2 +#MSB=0X1 +#SUPL_MODE=1 + +# Emergency SUPL, 1=enable, 0=disable +#SUPL_ES=0 + +#Choose PDN for Emergency SUPL +#1 - Use emergency PDN +#0 - Use regular SUPL PDN for Emergency SUPL +#USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=0 + +#################################### +# LTE Positioning Profile Settings +#################################### +# 0: Enable RRLP on LTE(Default) +# 1: Enable LPP_User_Plane on LTE +# 2: Enable LPP_Control_Plane +# 3: Enable both LPP_User_Plane and LPP_Control_Plane +#LPP_PROFILE = 2 + +################################################## +# Select Positioning Protocol on A-GLONASS system +################################################## +# 0x1: RRC CPlane +# 0x2: RRLP UPlane +# 0x4: LLP Uplane +#A_GLONASS_POS_PROTOCOL_SELECT = 0 + +# Below bit mask configures how GPS functionalities +# should be locked when user turns off GPS on Settings +# Set bit 0x1 if MO GPS functionalities are to be locked +# Set bit 0x2 if NI GPS functionalities are to be locked +# default - non is locked for backward compatibility +#GPS_LOCK = 0 diff --git a/services/core/java/com/android/server/locksettings/SP800Derive.java b/services/core/java/com/android/server/locksettings/SP800Derive.java new file mode 100644 index 000000000000..77561fc30db9 --- /dev/null +++ b/services/core/java/com/android/server/locksettings/SP800Derive.java @@ -0,0 +1,82 @@ +/* + * 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.locksettings; + +import java.nio.ByteBuffer; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +/** + * Implementation of NIST SP800-108 + * "Recommendation for Key Derivation Using Pseudorandom Functions" + * Hardcoded: + * [PRF=HMAC_SHA256] + * [CTRLOCATION=BEFORE_FIXED] + * [RLEN=32_BITS] + * L = 256 + * L suffix: 32 bits + */ +class SP800Derive { + private final byte[] mKeyBytes; + + SP800Derive(byte[] keyBytes) { + mKeyBytes = keyBytes; + } + + private Mac getMac() { + try { + final Mac m = Mac.getInstance("HmacSHA256"); + m.init(new SecretKeySpec(mKeyBytes, m.getAlgorithm())); + return m; + } catch (InvalidKeyException | NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + private static void update32(Mac m, int v) { + m.update(ByteBuffer.allocate(Integer.BYTES).putInt(v).array()); + } + + /** + * Generate output from a single, fixed input. + */ + public byte[] fixedInput(byte[] fixedInput) { + final Mac m = getMac(); + update32(m, 1); // Hardwired counter value + m.update(fixedInput); + return m.doFinal(); + } + + /** + * Generate output from a label and context. We add a length field at the end of the context to + * disambiguate it from the length even in the presence of zero bytes. + */ + public byte[] withContext(byte[] label, byte[] context) { + final Mac m = getMac(); + // Hardwired counter value: 1 + update32(m, 1); // Hardwired counter value + m.update(label); + m.update((byte) 0); + m.update(context); + update32(m, context.length * 8); // Disambiguate context + update32(m, 256); // Hardwired output length + return m.doFinal(); + } +} diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index 596daeb1427b..d32c299074a9 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java @@ -26,9 +26,9 @@ import android.hardware.weaver.V1_0.WeaverConfig; import android.hardware.weaver.V1_0.WeaverReadResponse; import android.hardware.weaver.V1_0.WeaverReadStatus; import android.hardware.weaver.V1_0.WeaverStatus; -import android.security.GateKeeper; import android.os.RemoteException; import android.os.UserManager; +import android.security.GateKeeper; import android.service.gatekeeper.GateKeeperResponse; import android.service.gatekeeper.IGateKeeperService; import android.util.ArrayMap; @@ -102,7 +102,8 @@ public class SyntheticPasswordManager { private static final int INVALID_WEAVER_SLOT = -1; private static final byte SYNTHETIC_PASSWORD_VERSION_V1 = 1; - private static final byte SYNTHETIC_PASSWORD_VERSION = 2; + private static final byte SYNTHETIC_PASSWORD_VERSION_V2 = 2; + private static final byte SYNTHETIC_PASSWORD_VERSION_V3 = 3; private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0; private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1; @@ -128,6 +129,8 @@ public class SyntheticPasswordManager { private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes(); private static final byte[] PERSONALISATION_WEAVER_KEY = "weaver-key".getBytes(); private static final byte[] PERSONALISATION_WEAVER_TOKEN = "weaver-token".getBytes(); + private static final byte[] PERSONALISATION_CONTEXT = + "android-synthetic-password-personalization-context".getBytes(); static class AuthenticationResult { public AuthenticationToken authToken; @@ -136,6 +139,7 @@ public class SyntheticPasswordManager { } static class AuthenticationToken { + private final byte mVersion; /* * Here is the relationship between all three fields: * P0 and P1 are two randomly-generated blocks. P1 is stored on disk but P0 is not. @@ -146,29 +150,38 @@ public class SyntheticPasswordManager { private @Nullable byte[] P1; private @NonNull String syntheticPassword; + AuthenticationToken(byte version) { + mVersion = version; + } + + private byte[] derivePassword(byte[] personalization) { + if (mVersion == SYNTHETIC_PASSWORD_VERSION_V3) { + return (new SP800Derive(syntheticPassword.getBytes())) + .withContext(personalization, PERSONALISATION_CONTEXT); + } else { + return SyntheticPasswordCrypto.personalisedHash(personalization, + syntheticPassword.getBytes()); + } + } + public String deriveKeyStorePassword() { - return bytesToHex(SyntheticPasswordCrypto.personalisedHash( - PERSONALIZATION_KEY_STORE_PASSWORD, syntheticPassword.getBytes())); + return bytesToHex(derivePassword(PERSONALIZATION_KEY_STORE_PASSWORD)); } public byte[] deriveGkPassword() { - return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_SP_GK_AUTH, - syntheticPassword.getBytes()); + return derivePassword(PERSONALIZATION_SP_GK_AUTH); } public byte[] deriveDiskEncryptionKey() { - return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_FBE_KEY, - syntheticPassword.getBytes()); + return derivePassword(PERSONALIZATION_FBE_KEY); } public byte[] deriveVendorAuthSecret() { - return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_AUTHSECRET_KEY, - syntheticPassword.getBytes()); + return derivePassword(PERSONALIZATION_AUTHSECRET_KEY); } public byte[] derivePasswordHashFactor() { - return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_PASSWORD_HASH, - syntheticPassword.getBytes()); + return derivePassword(PERSONALIZATION_PASSWORD_HASH); } private void initialize(byte[] P0, byte[] P1) { @@ -185,7 +198,7 @@ public class SyntheticPasswordManager { } protected static AuthenticationToken create() { - AuthenticationToken result = new AuthenticationToken(); + AuthenticationToken result = new AuthenticationToken(SYNTHETIC_PASSWORD_VERSION_V3); result.initialize(secureRandom(SYNTHETIC_PASSWORD_LENGTH), secureRandom(SYNTHETIC_PASSWORD_LENGTH)); return result; @@ -802,7 +815,16 @@ public class SyntheticPasswordManager { } byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid); byte[] blob = new byte[content.length + 1 + 1]; - blob[0] = SYNTHETIC_PASSWORD_VERSION; + /* + * We can upgrade from v1 to v2 because that's just a change in the way that + * the SP is stored. However, we can't upgrade to v3 because that is a change + * in the way that passwords are derived from the SP. + */ + if (authToken.mVersion == SYNTHETIC_PASSWORD_VERSION_V3) { + blob[0] = SYNTHETIC_PASSWORD_VERSION_V3; + } else { + blob[0] = SYNTHETIC_PASSWORD_VERSION_V2; + } blob[1] = type; System.arraycopy(content, 0, blob, 2, content.length); saveState(SP_BLOB_NAME, blob, handle, userId); @@ -940,7 +962,9 @@ public class SyntheticPasswordManager { return null; } final byte version = blob[0]; - if (version != SYNTHETIC_PASSWORD_VERSION && version != SYNTHETIC_PASSWORD_VERSION_V1) { + if (version != SYNTHETIC_PASSWORD_VERSION_V3 + && version != SYNTHETIC_PASSWORD_VERSION_V2 + && version != SYNTHETIC_PASSWORD_VERSION_V1) { throw new RuntimeException("Unknown blob version"); } if (blob[1] != type) { @@ -958,7 +982,7 @@ public class SyntheticPasswordManager { Log.e(TAG, "Fail to decrypt SP for user " + userId); return null; } - AuthenticationToken result = new AuthenticationToken(); + AuthenticationToken result = new AuthenticationToken(version); if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) { if (!loadEscrowData(result, userId)) { Log.e(TAG, "User is not escrowable: " + userId); diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index c18a79fc183c..93b66208e752 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -923,7 +923,7 @@ public class MediaSessionService extends SystemService implements Monitor { @Override public void dispatchMediaKeyEvent(String packageName, boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { - if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) { + if (keyEvent == null || !KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) { Log.w(TAG, "Attempted to dispatch null or non-media key event."); return; } diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index 4f4b6bfdb358..4bd8f450c76b 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -64,6 +64,7 @@ public class NetworkPolicyLogger { private static final int EVENT_UID_FIREWALL_RULE_CHANGED = 11; private static final int EVENT_FIREWALL_CHAIN_ENABLED = 12; private static final int EVENT_UPDATE_METERED_RESTRICTED_PKGS = 13; + private static final int EVENT_APP_IDLE_WL_CHANGED = 14; static final int NTWK_BLOCKED_POWER = 0; static final int NTWK_ALLOWED_NON_METERED = 1; @@ -145,6 +146,13 @@ public class NetworkPolicyLogger { } } + void appIdleWlChanged(int uid, boolean isWhitelisted) { + synchronized (mLock) { + if (LOGD) Slog.d(TAG, getAppIdleWlChangedLog(uid, isWhitelisted)); + mEventsBuffer.appIdleWlChanged(uid, isWhitelisted); + } + } + void paroleStateChanged(boolean paroleOn) { synchronized (mLock) { if (LOGD) Slog.d(TAG, getParoleStateChanged(paroleOn)); @@ -259,6 +267,10 @@ public class NetworkPolicyLogger { return "App idle state of uid " + uid + ": " + idle; } + private static String getAppIdleWlChangedLog(int uid, boolean isWhitelisted) { + return "App idle whitelist state of uid " + uid + ": " + isWhitelisted; + } + private static String getParoleStateChanged(boolean paroleOn) { return "Parole state: " + paroleOn; } @@ -409,6 +421,17 @@ public class NetworkPolicyLogger { data.timeStamp = System.currentTimeMillis(); } + public void appIdleWlChanged(int uid, boolean isWhitelisted) { + final Data data = getNextSlot(); + if (data == null) return; + + data.reset(); + data.type = EVENT_APP_IDLE_WL_CHANGED; + data.ifield1 = uid; + data.bfield1 = isWhitelisted; + data.timeStamp = System.currentTimeMillis(); + } + public void paroleStateChanged(boolean paroleOn) { final Data data = getNextSlot(); if (data == null) return; @@ -487,6 +510,8 @@ public class NetworkPolicyLogger { return getDeviceIdleModeEnabled(data.bfield1); case EVENT_APP_IDLE_STATE_CHANGED: return getAppIdleChangedLog(data.ifield1, data.bfield1); + case EVENT_APP_IDLE_WL_CHANGED: + return getAppIdleWlChangedLog(data.ifield1, data.bfield1); case EVENT_PAROLE_STATE_CHANGED: return getParoleStateChanged(data.bfield1); case EVENT_TEMP_POWER_SAVE_WL_CHANGED: diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java index 099671d81a3e..7f650ee7e138 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java @@ -105,6 +105,12 @@ public abstract class NetworkPolicyManagerInternal { public abstract void onAdminDataAvailable(); /** + * Control if a UID should be whitelisted even if it's in app idle mode. Other restrictions may + * still be in effect. + */ + public abstract void setAppIdleWhitelist(int uid, boolean shouldWhitelist); + + /** * Sets a list of packages which are restricted by admin from accessing metered data. * * @param packageNames the list of restricted packages. diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index d7996422870c..0d6dadfb1ad8 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -464,6 +464,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray(); + // "Power save mode" is the concept used in the DeviceIdleController that includes various + // features including Doze and Battery Saver. It include Battery Saver, but "power save mode" + // and "battery saver" are not equivalent. + /** * UIDs that have been white-listed to always be able to have network access * in power save mode, except device idle (doze) still applies. @@ -484,6 +488,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private final SparseBooleanArray mPowerSaveTempWhitelistAppIds = new SparseBooleanArray(); /** + * UIDs that have been white-listed temporarily to be able to have network access despite being + * idle. Other power saving restrictions still apply. + */ + @GuardedBy("mUidRulesFirstLock") + private final SparseBooleanArray mAppIdleTempWhitelistAppIds = new SparseBooleanArray(); + + /** * UIDs that have been initially white-listed by system to avoid restricted background. */ @GuardedBy("mUidRulesFirstLock") @@ -543,7 +554,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final Handler mHandler; @VisibleForTesting - public final Handler mUidEventHandler; + final Handler mUidEventHandler; private final ServiceThread mUidEventThread; @@ -1454,7 +1465,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @VisibleForTesting - public void updateNetworks() throws InterruptedException { + void updateNetworks() throws InterruptedException { updateNetworksInternal(); final CountDownLatch latch = new CountDownLatch(1); mHandler.post(() -> { @@ -1499,7 +1510,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * @return cycleDay to use in the mobile NetworkPolicy. */ @VisibleForTesting - public int getCycleDayFromCarrierConfig(@Nullable PersistableBundle config, + int getCycleDayFromCarrierConfig(@Nullable PersistableBundle config, int fallbackCycleDay) { if (config == null) { return fallbackCycleDay; @@ -1531,7 +1542,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * @return warningBytes to use in the mobile NetworkPolicy. */ @VisibleForTesting - public long getWarningBytesFromCarrierConfig(@Nullable PersistableBundle config, + long getWarningBytesFromCarrierConfig(@Nullable PersistableBundle config, long fallbackWarningBytes) { if (config == null) { return fallbackWarningBytes; @@ -1564,7 +1575,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * @return limitBytes to use in the mobile NetworkPolicy. */ @VisibleForTesting - public long getLimitBytesFromCarrierConfig(@Nullable PersistableBundle config, + long getLimitBytesFromCarrierConfig(@Nullable PersistableBundle config, long fallbackLimitBytes) { if (config == null) { return fallbackLimitBytes; @@ -2028,7 +2039,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @VisibleForTesting - public NetworkPolicy buildDefaultMobilePolicy(int subId, String subscriberId) { + NetworkPolicy buildDefaultMobilePolicy(int subId, String subscriberId) { final NetworkTemplate template = buildTemplateMobileAll(subscriberId); final RecurrenceRule cycleRule = NetworkPolicy .buildRule(ZonedDateTime.now().getDayOfMonth(), ZoneId.systemDefault()); @@ -3372,6 +3383,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.decreaseIndent(); } + size = mAppIdleTempWhitelistAppIds.size(); + if (size > 0) { + fout.println("App idle whitelist app ids:"); + fout.increaseIndent(); + for (int i = 0; i < size; i++) { + fout.print("UID="); + fout.print(mAppIdleTempWhitelistAppIds.keyAt(i)); + fout.print(": "); + fout.print(mAppIdleTempWhitelistAppIds.valueAt(i)); + fout.println(); + } + fout.decreaseIndent(); + } + size = mDefaultRestrictBackgroundWhitelistUids.size(); if (size > 0) { fout.println("Default restrict background whitelist uids:"); @@ -3464,7 +3489,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @VisibleForTesting - public boolean isUidForeground(int uid) { + boolean isUidForeground(int uid) { synchronized (mUidRulesFirstLock) { return isUidStateForeground( mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY)); @@ -3640,12 +3665,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } /** + * Returns whether a uid is whitelisted from power saving restrictions (eg: Battery Saver, Doze + * mode, and app idle). + * * @param deviceIdleMode if true then we don't consider * {@link #mPowerSaveWhitelistExceptIdleAppIds} for checking if the {@param uid} is * whitelisted. */ @GuardedBy("mUidRulesFirstLock") - private boolean isWhitelistedBatterySaverUL(int uid, boolean deviceIdleMode) { + private boolean isWhitelistedFromPowerSaveUL(int uid, boolean deviceIdleMode) { final int appId = UserHandle.getAppId(uid); boolean isWhitelisted = mPowerSaveTempWhitelistAppIds.get(appId) || mPowerSaveWhitelistAppIds.get(appId); @@ -3660,7 +3688,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") private void updateRulesForWhitelistedPowerSaveUL(int uid, boolean enabled, int chain) { if (enabled) { - final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid, + final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, chain == FIREWALL_CHAIN_DOZABLE); if (isWhitelisted || isUidForegroundOnRestrictPowerUL(uid)) { setUidFirewallRule(chain, uid, FIREWALL_RULE_ALLOW); @@ -3712,8 +3740,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid) && !isUidForegroundOnRestrictPowerUL(uid)) { setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY); + if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL DENY " + uid); } else { setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT); + if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL " + uid + " to DEFAULT"); } } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); @@ -3896,7 +3926,61 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return UserHandle.isApp(uid) && hasInternetPermissions(uid); } - private boolean isUidIdle(int uid) { + /** + * Set whether or not an app should be whitelisted for network access while in app idle. Other + * power saving restrictions may still apply. + */ + @VisibleForTesting + void setAppIdleWhitelist(int uid, boolean shouldWhitelist) { + mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + + synchronized (mUidRulesFirstLock) { + if (mAppIdleTempWhitelistAppIds.get(uid) == shouldWhitelist) { + // No change. + return; + } + + final long token = Binder.clearCallingIdentity(); + try { + mLogger.appIdleWlChanged(uid, shouldWhitelist); + if (shouldWhitelist) { + mAppIdleTempWhitelistAppIds.put(uid, true); + } else { + mAppIdleTempWhitelistAppIds.delete(uid); + } + updateRuleForAppIdleUL(uid); + updateRulesForPowerRestrictionsUL(uid); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + + /** Return the list of UIDs currently in the app idle whitelist. */ + @VisibleForTesting + int[] getAppIdleWhitelist() { + mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + + synchronized (mUidRulesFirstLock) { + final int len = mAppIdleTempWhitelistAppIds.size(); + int[] uids = new int[len]; + for (int i = 0; i < len; ++i) { + uids[i] = mAppIdleTempWhitelistAppIds.keyAt(i); + } + return uids; + } + } + + /** Returns if the UID is currently considered idle. */ + @VisibleForTesting + boolean isUidIdle(int uid) { + synchronized (mUidRulesFirstLock) { + if (mAppIdleTempWhitelistAppIds.get(uid)) { + // UID is temporarily whitelisted. + return false; + } + } + final String[] packages = mContext.getPackageManager().getPackagesForUid(uid); final int userId = UserHandle.getUserId(uid); @@ -3940,6 +4024,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mPowerSaveWhitelistExceptIdleAppIds.delete(uid); mPowerSaveWhitelistAppIds.delete(uid); mPowerSaveTempWhitelistAppIds.delete(uid); + mAppIdleTempWhitelistAppIds.delete(uid); // ...then update iptables asynchronously. mHandler.obtainMessage(MSG_RESET_FIREWALL_RULES_BY_UID, uid, 0).sendToTarget(); @@ -3984,7 +4069,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * <li>@{code bw_happy_box}: UIDs added to this chain have access (whitelist), unless they're * also blacklisted. * <li>@{code bw_data_saver}: when enabled (through {@link #setRestrictBackground(boolean)}), - * no UIDs other those whitelisted will have access. + * no UIDs other than those whitelisted will have access. * <ul> * * <p>The @{code bw_penalty_box} and @{code bw_happy_box} are primarily managed through the @@ -4194,7 +4279,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode; final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid); - final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid, mDeviceIdleMode); + final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode); final int oldRule = oldUidRules & MASK_ALL_NETWORKS; int newRule = RULE_NONE; @@ -4761,13 +4846,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @VisibleForTesting - public void addIdleHandler(IdleHandler handler) { + void addIdleHandler(IdleHandler handler) { mHandler.getLooper().getQueue().addIdleHandler(handler); } @GuardedBy("mUidRulesFirstLock") @VisibleForTesting - public void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) { + void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) { mRestrictBackgroundPowerState = result; boolean restrictBackground = result.batterySaverEnabled; @@ -5023,6 +5108,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @Override + public void setAppIdleWhitelist(int uid, boolean shouldWhitelist) { + NetworkPolicyManagerService.this.setAppIdleWhitelist(uid, shouldWhitelist); + } + + @Override public void setMeteredRestrictedPackages(Set<String> packageNames, int userId) { setMeteredRestrictedPackagesInternal(packageNames, userId); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java index 56d41c5a72aa..156c01dbcbb3 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java @@ -78,6 +78,8 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { pw.println(" Adds a UID to the whitelist for restrict background usage."); pw.println(" add restrict-background-blacklist UID"); pw.println(" Adds a UID to the blacklist for restrict background usage."); + pw.println(" add app-idle-whitelist UID"); + pw.println(" Adds a UID to the temporary app idle whitelist."); pw.println(" get restrict-background"); pw.println(" Gets the global restrict background usage status."); pw.println(" list wifi-networks [true|false]"); @@ -92,6 +94,8 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { pw.println(" Removes a UID from the whitelist for restrict background usage."); pw.println(" remove restrict-background-blacklist UID"); pw.println(" Removes a UID from the blacklist for restrict background usage."); + pw.println(" remove app-idle-whitelist UID"); + pw.println(" Removes a UID from the temporary app idle whitelist."); pw.println(" set metered-network ID [undefined|true|false]"); pw.println(" Toggles whether the given wi-fi network is metered."); pw.println(" set restrict-background BOOLEAN"); @@ -142,6 +146,8 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { return -1; } switch(type) { + case "app-idle-whitelist": + return listAppIdleWhitelist(); case "wifi-networks": return listWifiNetworks(); case "restrict-background-whitelist": @@ -165,6 +171,8 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { return addRestrictBackgroundWhitelist(); case "restrict-background-blacklist": return addRestrictBackgroundBlacklist(); + case "app-idle-whitelist": + return addAppIdleWhitelist(); } pw.println("Error: unknown add type '" + type + "'"); return -1; @@ -182,14 +190,20 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { return removeRestrictBackgroundWhitelist(); case "restrict-background-blacklist": return removeRestrictBackgroundBlacklist(); + case "app-idle-whitelist": + return removeAppIdleWhitelist(); } pw.println("Error: unknown remove type '" + type + "'"); return -1; } private int listUidPolicies(String msg, int policy) throws RemoteException { - final PrintWriter pw = getOutPrintWriter(); final int[] uids = mInterface.getUidsWithPolicy(policy); + return listUidList(msg, uids); + } + + private int listUidList(String msg, int[] uids) { + final PrintWriter pw = getOutPrintWriter(); pw.print(msg); pw.print(": "); if (uids.length == 0) { pw.println("none"); @@ -214,6 +228,12 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { POLICY_REJECT_METERED_BACKGROUND); } + private int listAppIdleWhitelist() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + final int[] uids = mInterface.getAppIdleWhitelist(); + return listUidList("App Idle whitelisted UIDs", uids); + } + private int getRestrictBackground() throws RemoteException { final PrintWriter pw = getOutPrintWriter(); pw.print("Restrict background status: "); @@ -277,6 +297,23 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { return resetUidPolicy("not blacklisted", POLICY_REJECT_METERED_BACKGROUND); } + private int setAppIdleWhitelist(boolean isWhitelisted) { + final int uid = getUidFromNextArg(); + if (uid < 0) { + return uid; + } + mInterface.setAppIdleWhitelist(uid, isWhitelisted); + return 0; + } + + private int addAppIdleWhitelist() throws RemoteException { + return setAppIdleWhitelist(true); + } + + private int removeAppIdleWhitelist() throws RemoteException { + return setAppIdleWhitelist(false); + } + private int listWifiNetworks() { final PrintWriter pw = getOutPrintWriter(); final String arg = getNextArg(); diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java index 9222740e0506..84bb13ec92d3 100644 --- a/services/core/java/com/android/server/notification/NotificationDelegate.java +++ b/services/core/java/com/android/server/notification/NotificationDelegate.java @@ -16,6 +16,7 @@ package com.android.server.notification; +import android.app.Notification; import android.service.notification.NotificationStats; import com.android.internal.statusbar.NotificationVisibility; @@ -26,7 +27,7 @@ public interface NotificationDelegate { void onNotificationClick(int callingUid, int callingPid, String key, NotificationVisibility nv); void onNotificationActionClick(int callingUid, int callingPid, String key, int actionIndex, - NotificationVisibility nv); + Notification.Action action, NotificationVisibility nv, boolean generatedByAssistant); void onNotificationClear(int callingUid, int callingPid, String pkg, String tag, int id, int userId, String key, @NotificationStats.DismissalSurface int dismissalSurface, diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 60058724e9a7..ae27d0c07ea8 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -752,7 +752,8 @@ public class NotificationManagerService extends SystemService { @Override public void onNotificationActionClick(int callingUid, int callingPid, String key, - int actionIndex, NotificationVisibility nv) { + int actionIndex, Notification.Action action, NotificationVisibility nv, + boolean generatedByAssistant) { exitIdle(); synchronized (mNotificationLock) { NotificationRecord r = mNotificationsByKey.get(key); @@ -772,6 +773,8 @@ public class NotificationManagerService extends SystemService { nv.rank, nv.count); nv.recycle(); reportUserInteraction(r); + mAssistants.notifyAssistantActionClicked( + r.sbn, actionIndex, action, generatedByAssistant); } } @@ -6923,6 +6926,27 @@ public class NotificationManagerService extends SystemService { }); } + @GuardedBy("mNotificationLock") + void notifyAssistantActionClicked( + final StatusBarNotification sbn, int actionIndex, Notification.Action action, + boolean generatedByAssistant) { + final String key = sbn.getKey(); + notifyAssistantLocked( + sbn, + false /* sameUserOnly */, + (assistant, sbnHolder) -> { + try { + assistant.onActionClicked( + key, + action, + generatedByAssistant + ? NotificationAssistantService.SOURCE_FROM_ASSISTANT + : NotificationAssistantService.SOURCE_FROM_APP); + } catch (RemoteException ex) { + Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex); + } + }); + } /** * asynchronously notify the assistant that a notification has been snoozed until a diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index f279af03753e..94d276c8496d 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -309,6 +309,7 @@ public class ZenModeHelper { newConfig = mConfig.copy(); ZenRule rule = new ZenRule(); populateZenRule(automaticZenRule, rule, true); + newConfig.automaticRules.put(rule.id, rule); if (setConfigLocked(newConfig, reason, rule.component, true)) { return rule.id; } else { diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index b4903817f787..7f1fb6c97e39 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -310,7 +310,7 @@ public class LauncherAppsService extends SystemService { .setPackage(packageName), user); if (Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 0) == 0) { + Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 1) == 0) { return launcherActivities; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 14682f325940..c125e9719ed0 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -53,7 +53,6 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY; import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED; -import static android.content.pm.PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE; import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY; import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; @@ -15191,8 +15190,11 @@ public class PackageManagerService extends IPackageManager.Stub } } private static class ReconcileFailure extends PackageManagerException { - public ReconcileFailure(String message) { - super("Invalid reconcile request: " + message); + ReconcileFailure(String message) { + super("Reconcile failed: " + message); + } + ReconcileFailure(int reason, String message) { + super(reason, "Reconcile failed: " + message); } } @@ -15211,10 +15213,12 @@ public class PackageManagerService extends IPackageManager.Stub @PackageManager.InstallFlags public final int installFlags; public final InstallArgs installArgs; + public final DeletePackageAction deletePackageAction; private ReconciledPackage(InstallArgs installArgs, PackageSetting pkgSetting, UserHandle installForUser, PackageInstalledInfo installResult, int installFlags, - String volumeUuid, PrepareResult prepareResult, ScanResult scanResult) { + String volumeUuid, PrepareResult prepareResult, ScanResult scanResult, + DeletePackageAction deletePackageAction) { this.installArgs = installArgs; this.pkgSetting = pkgSetting; this.installForUser = installForUser; @@ -15223,6 +15227,7 @@ public class PackageManagerService extends IPackageManager.Stub this.volumeUuid = volumeUuid; this.prepareResult = prepareResult; this.scanResult = scanResult; + this.deletePackageAction = deletePackageAction; } } @@ -15235,14 +15240,30 @@ public class PackageManagerService extends IPackageManager.Stub final ScanResult scanResult = request.scannedPackages.get(installPackageName); final InstallArgs installArgs = request.installArgs.get(installPackageName); final PackageInstalledInfo res = request.installResults.get(installPackageName); + final PrepareResult prepareResult = request.preparedPackages.get(installPackageName); if (scanResult == null || installArgs == null || res == null) { throw new ReconcileFailure( "inputs not balanced; missing argument for " + installPackageName); } + final DeletePackageAction deletePackageAction; + // we only want to try to delete for non system apps + if (prepareResult.replace && !prepareResult.system) { + deletePackageAction = mayDeletePackageLocked(res.removedInfo, + prepareResult.originalPs, prepareResult.disabledPs, + prepareResult.childPackageSettings); + if (deletePackageAction == null) { + throw new ReconcileFailure( + PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE, + "May not delete " + installPackageName + " to replace"); + } + } else { + deletePackageAction = null; + } result.put(installPackageName, new ReconciledPackage(installArgs, scanResult.pkgSetting, installArgs.getUser(), res, installArgs.installFlags, installArgs.volumeUuid, - request.preparedPackages.get(installPackageName), scanResult)); + request.preparedPackages.get(installPackageName), scanResult, + deletePackageAction)); } return result; } @@ -15314,28 +15335,28 @@ public class PackageManagerService extends IPackageManager.Stub final boolean killApp = (scanRequest.scanFlags & SCAN_DONT_KILL_APP) == 0; final int deleteFlags = PackageManager.DELETE_KEEP_DATA | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP); - // First delete the existing package while retaining the data directory - if (!deletePackageLIF(packageName, null, true, request.mAllUsers, deleteFlags, - res.removedInfo, true, pkg)) { - // If the existing package wasn't successfully deleted - res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE, - "replaceNonSystemPackageLI"); - return false; - } else { - // Successfully deleted the old package; proceed with replace. + try { + executeDeletePackageLIF(reconciledPkg.deletePackageAction, packageName, + null, true, request.mAllUsers, deleteFlags, true, pkg); + } catch (SystemDeleteException e) { + if (Build.IS_ENG) { + throw new RuntimeException("Unexpected failure", e); + // ignore; not possible for non-system app + } + } + // Successfully deleted the old package; proceed with replace. - // If deleted package lived in a container, give users a chance to - // relinquish resources before killing. - if (oldPackage.isForwardLocked() || isExternal(oldPackage)) { - if (DEBUG_INSTALL) { - Slog.i(TAG, "upgrading pkg " + oldPackage - + " is ASEC-hosted -> UNAVAILABLE"); - } - final int[] uidArray = new int[]{oldPackage.applicationInfo.uid}; - final ArrayList<String> pkgList = new ArrayList<>(1); - pkgList.add(oldPackage.applicationInfo.packageName); - sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null); + // If deleted package lived in a container, give users a chance to + // relinquish resources before killing. + if (oldPackage.isForwardLocked() || isExternal(oldPackage)) { + if (DEBUG_INSTALL) { + Slog.i(TAG, "upgrading pkg " + oldPackage + + " is ASEC-hosted -> UNAVAILABLE"); } + final int[] uidArray = new int[]{oldPackage.applicationInfo.uid}; + final ArrayList<String> pkgList = new ArrayList<>(1); + pkgList.add(oldPackage.applicationInfo.packageName); + sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null); } // Update the in-memory copy of the previous code paths. @@ -15744,13 +15765,16 @@ public class PackageManagerService extends IPackageManager.Stub @Nullable public final String renamedPackage; public final PackageFreezer freezer; + public final PackageSetting originalPs; + public final PackageSetting disabledPs; + public final PackageSetting[] childPackageSettings; private PrepareResult(int installReason, String volumeUuid, String installerPackageName, UserHandle user, boolean replace, int scanFlags, int parseFlags, PackageParser.Package existingPackage, PackageParser.Package packageToScan, boolean clearCodeCache, boolean system, - String renamedPackage, - PackageFreezer freezer) { + String renamedPackage, PackageFreezer freezer, PackageSetting originalPs, + PackageSetting disabledPs, PackageSetting[] childPackageSettings) { this.installReason = installReason; this.volumeUuid = volumeUuid; this.installerPackageName = installerPackageName; @@ -15764,6 +15788,9 @@ public class PackageManagerService extends IPackageManager.Stub this.system = system; this.renamedPackage = renamedPackage; this.freezer = freezer; + this.originalPs = originalPs; + this.disabledPs = disabledPs; + this.childPackageSettings = childPackageSettings; } } @@ -16267,7 +16294,9 @@ public class PackageManagerService extends IPackageManager.Stub String targetVolumeUuid = volumeUuid; int targetScanFlags = scanFlags; int targetParseFlags = parseFlags; - + final PackageSetting ps; + final PackageSetting disabledPs; + final PackageSetting[] childPackages; if (replace) { targetVolumeUuid = null; if (pkg.applicationInfo.isStaticSharedLibrary()) { @@ -16287,7 +16316,6 @@ public class PackageManagerService extends IPackageManager.Stub final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0; final PackageParser.Package oldPackage; - final PackageSetting ps; final String pkgName11 = pkg.packageName; final int[] allUsers; final int[] installedUsers; @@ -16314,6 +16342,7 @@ public class PackageManagerService extends IPackageManager.Stub } ps = mSettings.mPackages.get(pkgName11); + disabledPs = mSettings.getDisabledSystemPkgLPr(ps); // verify signatures are valid final KeySetManagerService ksms = mSettings.mKeySetManagerService; @@ -16410,49 +16439,52 @@ public class PackageManagerService extends IPackageManager.Stub res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId)); } - final int childCount = (oldPackage.childPackages != null) - ? oldPackage.childPackages.size() : 0; - for (int i = 0; i < childCount; i++) { - boolean childPackageUpdated = false; - PackageParser.Package childPkg = oldPackage.childPackages.get(i); - final PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName); - if (res.addedChildPackages != null) { - PackageInstalledInfo childRes = res.addedChildPackages.get( - childPkg.packageName); - if (childRes != null) { - childRes.removedInfo.uid = childPkg.applicationInfo.uid; - childRes.removedInfo.removedPackage = childPkg.packageName; - if (childPs != null) { - childRes.removedInfo.installerPackageName = - childPs.installerPackageName; + childPackages = mSettings.getChildSettingsLPr(ps); + if (childPackages != null) { + for (PackageSetting childPs : childPackages) { + boolean childPackageUpdated = false; + PackageParser.Package childPkg = (childPs == null) ? null : childPs.pkg; + if (res.addedChildPackages != null) { + PackageInstalledInfo childRes = res.addedChildPackages.get( + childPkg.packageName); + if (childRes != null) { + childRes.removedInfo.uid = childPkg.applicationInfo.uid; + childRes.removedInfo.removedPackage = childPkg.packageName; + if (childPs != null) { + childRes.removedInfo.installerPackageName = + childPs.installerPackageName; + } + childRes.removedInfo.isUpdate = true; + childRes.removedInfo.installReasons = + res.removedInfo.installReasons; + childPackageUpdated = true; } - childRes.removedInfo.isUpdate = true; - childRes.removedInfo.installReasons = res.removedInfo.installReasons; - childPackageUpdated = true; - } - } - if (!childPackageUpdated) { - PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this); - childRemovedRes.removedPackage = childPkg.packageName; - if (childPs != null) { - childRemovedRes.installerPackageName = childPs.installerPackageName; } - childRemovedRes.isUpdate = false; - childRemovedRes.dataRemoved = true; - synchronized (mPackages) { + if (!childPackageUpdated) { + PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this); + childRemovedRes.removedPackage = childPkg.packageName; if (childPs != null) { - childRemovedRes.origUsers = childPs.queryInstalledUsers(allUsers, - true); + childRemovedRes.installerPackageName = childPs.installerPackageName; } + childRemovedRes.isUpdate = false; + childRemovedRes.dataRemoved = true; + synchronized (mPackages) { + if (childPs != null) { + childRemovedRes.origUsers = childPs.queryInstalledUsers( + allUsers, + true); + } + } + if (res.removedInfo.removedChildPackages == null) { + res.removedInfo.removedChildPackages = new ArrayMap<>(); + } + res.removedInfo.removedChildPackages.put(childPkg.packageName, + childRemovedRes); } - if (res.removedInfo.removedChildPackages == null) { - res.removedInfo.removedChildPackages = new ArrayMap<>(); - } - res.removedInfo.removedChildPackages.put(childPkg.packageName, - childRemovedRes); } } + sysPkg = (isSystemApp(oldPackage)); if (sysPkg) { // Set the system/privileged/oem/vendor/product flags as needed @@ -16495,6 +16527,9 @@ public class PackageManagerService extends IPackageManager.Stub } } else { // new package install + ps = null; + childPackages = null; + disabledPs = null; replace = false; existingPackage = null; // Remember this for later, in case we need to rollback this install @@ -16525,9 +16560,11 @@ public class PackageManagerService extends IPackageManager.Stub } // we're passing the freezer back to be closed in a later phase of install shouldCloseFreezerBeforeReturn = false; + return new PrepareResult(args.installReason, targetVolumeUuid, installerPackageName, args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg, - replace /* clearCodeCache */, sysPkg, renamedPackage, freezer); + replace /* clearCodeCache */, sysPkg, renamedPackage, freezer, + ps, disabledPs, childPackages); } finally { if (shouldCloseFreezerBeforeReturn) { freezer.close(); @@ -17477,34 +17514,20 @@ public class PackageManagerService extends IPackageManager.Stub /* * Tries to delete system package. */ - private boolean deleteSystemPackageLIF(PackageParser.Package deletedPkg, - PackageSetting deletedPs, int[] allUserHandles, int flags, PackageRemovedInfo outInfo, - boolean writeSettings) { - if (deletedPs.parentPackageName != null) { - Slog.w(TAG, "Attempt to delete child system package " + deletedPkg.packageName); - return false; - } - + private void deleteSystemPackageLIF(DeletePackageAction action, + PackageParser.Package deletedPkg, PackageSetting deletedPs, int[] allUserHandles, + int flags, PackageRemovedInfo outInfo, boolean writeSettings) + throws SystemDeleteException { final boolean applyUserRestrictions = (allUserHandles != null) && (outInfo.origUsers != null); - final PackageSetting disabledPs; // Confirm if the system package has been updated // An updated system app can be deleted. This will also have to restore // the system pkg from system partition // reader - synchronized (mPackages) { - disabledPs = mSettings.getDisabledSystemPkgLPr(deletedPs.name); - } - + final PackageSetting disabledPs = action.disabledPs; if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.packageName + " disabledPs=" + disabledPs); - - if (disabledPs == null) { - Slog.w(TAG, "Attempt to delete unknown system package "+ deletedPkg.packageName); - return false; - } else if (DEBUG_REMOVE) { - Slog.d(TAG, "Deleting system pkg from data partition"); - } + Slog.d(TAG, "Deleting system pkg from data partition"); if (DEBUG_REMOVE) { if (applyUserRestrictions) { @@ -17542,11 +17565,8 @@ public class PackageManagerService extends IPackageManager.Stub flags |= PackageManager.DELETE_KEEP_DATA; } - boolean ret = deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles, + deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles, outInfo, writeSettings, disabledPs.pkg); - if (!ret) { - return false; - } // writer synchronized (mPackages) { @@ -17563,25 +17583,25 @@ public class PackageManagerService extends IPackageManager.Stub // Install the system package if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs); try { - installPackageFromSystemLIF(disabledPs.codePathString, false, allUserHandles, + installPackageFromSystemLIF(disabledPs.codePathString, allUserHandles, outInfo.origUsers, deletedPs.getPermissionsState(), writeSettings); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to restore system package:" + deletedPkg.packageName + ": " + e.getMessage()); - return false; + // TODO(patb): can we avoid this; throw would come from scan... + throw new SystemDeleteException(e); } finally { if (disabledPs.pkg.isStub) { mSettings.disableSystemPackageLPw(disabledPs.name, true /*replaced*/); } } - return true; } /** * Installs a package that's already on the system partition. */ private PackageParser.Package installPackageFromSystemLIF(@NonNull String codePathString, - boolean isPrivileged, @Nullable int[] allUserHandles, @Nullable int[] origUserHandles, + @Nullable int[] allUserHandles, @Nullable int[] origUserHandles, @Nullable PermissionsState origPermissionState, boolean writeSettings) throws PackageManagerException { @ParseFlags int parseFlags = @@ -17589,7 +17609,7 @@ public class PackageManagerService extends IPackageManager.Stub | PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM_DIR; @ScanFlags int scanFlags = SCAN_AS_SYSTEM; - if (isPrivileged || locationIsPrivileged(codePathString)) { + if (locationIsPrivileged(codePathString)) { scanFlags |= SCAN_AS_PRIVILEGED; } if (locationIsOem(codePathString)) { @@ -17665,7 +17685,7 @@ public class PackageManagerService extends IPackageManager.Stub return pkg; } - private boolean deleteInstalledPackageLIF(PackageSetting ps, + private void deleteInstalledPackageLIF(PackageSetting ps, boolean deleteCodeAndResources, int flags, int[] allUserHandles, PackageRemovedInfo outInfo, boolean writeSettings, PackageParser.Package replacingPackage) { @@ -17680,9 +17700,6 @@ public class PackageManagerService extends IPackageManager.Stub for (int i = 0; i < childCount; i++) { String childPackageName = ps.childPackageNames.get(i); PackageSetting childPs = mSettings.mPackages.get(childPackageName); - if (childPs == null) { - return false; - } PackageRemovedInfo childInfo = outInfo.removedChildPackages.get( childPackageName); if (childInfo != null) { @@ -17723,8 +17740,6 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args); } } - - return true; } @Override @@ -17780,26 +17795,59 @@ public class PackageManagerService extends IPackageManager.Stub private static class DeletePackageAction { public final PackageSetting deletingPs; + public final PackageSetting disabledPs; + public final PackageRemovedInfo outInfo; - private DeletePackageAction(PackageSetting deletingPs) { + private DeletePackageAction(PackageSetting deletingPs, PackageSetting disabledPs, + PackageRemovedInfo outInfo) { this.deletingPs = deletingPs; + this.disabledPs = disabledPs; + this.outInfo = outInfo; } } /** - * @return a {@link DeletePackageAction} if the provided package may be deleted, {@code null} - * otherwise. + * @return a {@link DeletePackageAction} if the provided package and related state may be + * deleted, {@code null} otherwise. */ @Nullable - private DeletePackageAction mayDeletePackageLIF(@NonNull String packageName) { - synchronized (mPackages) { - final PackageSetting ps; - ps = mSettings.mPackages.get(packageName); - if (ps == null) { + @GuardedBy("mPackages") + private static DeletePackageAction mayDeletePackageLocked( + PackageRemovedInfo outInfo, PackageSetting ps, @Nullable PackageSetting disabledPs, + @Nullable PackageSetting[] children) { + if (ps == null) { + return null; + } + if (isSystemApp(ps)) { + if (ps.parentPackageName != null) { + Slog.w(TAG, "Attempt to delete child system package " + ps.pkg.packageName); + return null; + } + + // Confirm if the system package has been updated + // An updated system app can be deleted. This will also have to restore + // the system pkg from system partition + // reader + if (disabledPs == null) { + Slog.w(TAG, + "Attempt to delete unknown system package " + ps.pkg.packageName); return null; } - return new DeletePackageAction(ps); } + final int parentReferenceCount = + (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0; + final int childCount = children != null ? children.length : 0; + if (childCount != parentReferenceCount) { + return null; + } + if (childCount != 0 && outInfo != null && outInfo.removedChildPackages != null) { + for (PackageSetting child : children) { + if (child == null || !ps.childPackageNames.contains(child.name)) { + return null; + } + } + } + return new DeletePackageAction(ps, disabledPs, outInfo); } /* @@ -17809,22 +17857,43 @@ public class PackageManagerService extends IPackageManager.Stub boolean deleteCodeAndResources, int[] allUserHandles, int flags, PackageRemovedInfo outInfo, boolean writeSettings, PackageParser.Package replacingPackage) { - final DeletePackageAction action = mayDeletePackageLIF(packageName); + final DeletePackageAction action; + synchronized (mPackages) { + final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps); + PackageSetting[] children = mSettings.getChildSettingsLPr(ps); + action = mayDeletePackageLocked(outInfo, ps, disabledPs, children); + } if (null == action) { return false; } if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user); - return executeDeletePackageLIF(action, packageName, user, deleteCodeAndResources, - allUserHandles, flags, outInfo, writeSettings, replacingPackage); + try { + executeDeletePackageLIF(action, packageName, user, deleteCodeAndResources, + allUserHandles, flags, writeSettings, replacingPackage); + } catch (SystemDeleteException e) { + return false; + } + return true; + } + + private static class SystemDeleteException extends Exception { + public final PackageManagerException reason; + + private SystemDeleteException(PackageManagerException reason) { + this.reason = reason; + } } - private boolean executeDeletePackageLIF(DeletePackageAction action, + /** Deletes a package. Only throws when install of a disabled package fails. */ + private void executeDeletePackageLIF(DeletePackageAction action, String packageName, UserHandle user, boolean deleteCodeAndResources, - int[] allUserHandles, int flags, PackageRemovedInfo outInfo, - boolean writeSettings, PackageParser.Package replacingPackage) { + int[] allUserHandles, int flags, boolean writeSettings, + PackageParser.Package replacingPackage) throws SystemDeleteException { final PackageSetting ps = action.deletingPs; + final PackageRemovedInfo outInfo = action.outInfo; final boolean systemApp = isSystemApp(ps); synchronized (mPackages) { @@ -17840,7 +17909,7 @@ public class PackageManagerService extends IPackageManager.Stub clearPackageStateForUserLIF(ps, removedUserId, outInfo); markPackageUninstalledForUserLPw(ps, user); scheduleWritePackageRestrictionsLocked(user); - return true; + return; } } @@ -17869,7 +17938,7 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users"); clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo); scheduleWritePackageRestrictionsLocked(user); - return true; + return; } else { // We need to set it back to 'installed' so the uninstall // broadcasts will be sent correctly. @@ -17885,7 +17954,7 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app"); clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo); scheduleWritePackageRestrictionsLocked(user); - return true; + return; } } @@ -17910,15 +17979,15 @@ public class PackageManagerService extends IPackageManager.Stub } // TODO(b/109941548): break reasons for ret = false out into mayDelete method - final boolean ret; if (systemApp) { if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.name); // When an updated system application is deleted we delete the existing resources // as well and fall back to existing code in system partition - ret = deleteSystemPackageLIF(ps.pkg, ps, allUserHandles, flags, outInfo, writeSettings); + deleteSystemPackageLIF( + action, ps.pkg, ps, allUserHandles, flags, outInfo, writeSettings); } else { if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name); - ret = deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles, + deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles, outInfo, writeSettings, replacingPackage); } @@ -17967,8 +18036,6 @@ public class PackageManagerService extends IPackageManager.Stub } } } - - return ret; } @GuardedBy("mPackages") @@ -19694,7 +19761,7 @@ public class PackageManagerService extends IPackageManager.Stub enableSystemPackageLPw(deletedPkg); } installPackageFromSystemLIF(deletedPkg.codePath, - false /*isPrivileged*/, null /*allUserHandles*/, + /*isPrivileged*/ null /*allUserHandles*/, null /*origUserHandles*/, null /*origPermissionsState*/, true /*writeSettings*/); } catch (PackageManagerException pme) { @@ -23528,6 +23595,30 @@ public class PackageManagerService extends IPackageManager.Stub return mProtectedPackages.isPackageStateProtected(userId, packageName); } + @Override + public void sendDeviceCustomizationReadyBroadcast() { + mContext.enforceCallingPermission(Manifest.permission.SEND_DEVICE_CUSTOMIZATION_READY, + "sendDeviceCustomizationReadyBroadcast"); + + final long ident = Binder.clearCallingIdentity(); + try { + final Intent intent = new Intent(Intent.ACTION_DEVICE_CUSTOMIZATION_READY); + intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + final IActivityManager am = ActivityManager.getService(); + final String[] requiredPermissions = { + Manifest.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY, + }; + try { + am.broadcastIntent(null, intent, null, null, 0, null, null, requiredPermissions, + android.app.AppOpsManager.OP_NONE, null, false, false, UserHandle.USER_ALL); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + static class ActiveInstallSession { private final String mPackageName; private final File mStagedDir; diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 9e5a4c6a9888..c524dba01ba6 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4255,11 +4255,48 @@ public final class Settings { return false; } + /** + * Returns the disabled {@link PackageSetting} for the provided package name if one exists, + * {@code null} otherwise. + */ + @Nullable public PackageSetting getDisabledSystemPkgLPr(String name) { PackageSetting ps = mDisabledSysPackages.get(name); return ps; } + /** + * Returns the disabled {@link PackageSetting} for the provided enabled {@link PackageSetting} + * if one exists, {@code null} otherwise. + */ + @Nullable + public PackageSetting getDisabledSystemPkgLPr(PackageSetting enabledPackageSetting) { + if (enabledPackageSetting == null) { + return null; + } + return getDisabledSystemPkgLPr(enabledPackageSetting.name); + } + + /** + * Fetches an array of the child {@link PackageSetting}s for all child package names referenced + * by the provided parent {@link PackageSetting} or {@code null} if no children are referenced. + * + * Note: Any child packages not found will be null in the returned array. + */ + @Nullable + public PackageSetting[] getChildSettingsLPr(PackageSetting parentPackageSetting) { + if (parentPackageSetting == null || !parentPackageSetting.hasChildPackages()) { + return null; + } + final int childCount = parentPackageSetting.childPackageNames.size(); + PackageSetting[] children = + new PackageSetting[childCount]; + for (int i = 0; i < childCount; i++) { + children[i] = mPackages.get(parentPackageSetting.childPackageNames.get(i)); + } + return children; + } + boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) { final PackageSetting ps = mPackages.get(componentInfo.packageName); if (ps == null) return false; diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java index e194d1541ea7..2d583ca39adb 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/BasePermission.java @@ -105,6 +105,8 @@ public final class BasePermission { */ private boolean perUser; + boolean usageInfoRequired; + public BasePermission(String _name, String _sourcePackageName, @PermissionType int _type) { name = _name; sourcePackageName = _sourcePackageName; @@ -351,6 +353,7 @@ public final class BasePermission { } if (bp.perm == p) { bp.protectionLevel = p.info.protectionLevel; + bp.usageInfoRequired = p.info.usageInfoRequired; } if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) { Log.d(TAG, " Permissions: " + r); @@ -430,6 +433,7 @@ public final class BasePermission { permissionInfo.packageName = sourcePackageName; permissionInfo.nonLocalizedLabel = name; permissionInfo.protectionLevel = protectionLevel; + permissionInfo.usageInfoRequired = usageInfoRequired; return permissionInfo; } @@ -458,6 +462,7 @@ public final class BasePermission { bp.protectionLevel = readInt(parser, null, "protection", PermissionInfo.PROTECTION_NORMAL); bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel); + bp.usageInfoRequired = readInt(parser, null, "usageInfoRequired", 0) != 0; if (dynamic) { final PermissionInfo pi = new PermissionInfo(); pi.packageName = sourcePackage.intern(); @@ -465,6 +470,7 @@ public final class BasePermission { pi.icon = readInt(parser, null, "icon", 0); pi.nonLocalizedLabel = parser.getAttributeValue(null, "label"); pi.protectionLevel = bp.protectionLevel; + pi.usageInfoRequired = bp.usageInfoRequired; bp.pendingPermissionInfo = pi; } out.put(bp.name, bp); @@ -497,6 +503,7 @@ public final class BasePermission { if (protectionLevel != PermissionInfo.PROTECTION_NORMAL) { serializer.attribute(null, "protection", Integer.toString(protectionLevel)); } + serializer.attribute(null, "usageInfoRequired", usageInfoRequired ? "1" : "0"); if (type == BasePermission.TYPE_DYNAMIC) { final PermissionInfo pi = perm != null ? perm.info : pendingPermissionInfo; if (pi != null) { @@ -533,6 +540,7 @@ public final class BasePermission { if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false; // We'll take care of setting this one. if (!compareStrings(pi1.packageName, pi2.packageName)) return false; + if (pi1.usageInfoRequired != pi2.usageInfoRequired) return false; // These are not currently stored in settings. //if (!compareStrings(pi1.group, pi2.group)) return false; //if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false; @@ -580,6 +588,8 @@ public final class BasePermission { pw.print(" enforced="); pw.println(readEnforced); } + pw.print(" usageInfoRequired="); + pw.println(usageInfoRequired); return true; } } diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 21cc14e20bc7..e9b9930600a0 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -144,6 +144,11 @@ public final class DefaultPermissionGrantPolicy { LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION); } + private static final Set<String> ACTIVITY_RECOGNITION_PERMISSIONS = new ArraySet<>(); + static { + ACTIVITY_RECOGNITION_PERMISSIONS.add(Manifest.permission.ACTIVITY_RECOGNITION); + } + private static final Set<String> COARSE_LOCATION_PERMISSIONS = new ArraySet<>(); static { COARSE_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION); @@ -624,7 +629,7 @@ public final class DefaultPermissionGrantPolicy { PHONE_PERMISSIONS, SMS_PERMISSIONS, CAMERA_PERMISSIONS, SENSORS_PERMISSIONS, STORAGE_PERMISSIONS); grantSystemFixedPermissionsToSystemPackage(packageName, userId, - LOCATION_PERMISSIONS); + LOCATION_PERMISSIONS, ACTIVITY_RECOGNITION_PERMISSIONS); } } diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index 4124210b27cb..b390eebf3d7e 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -30,6 +30,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.os.ResultReceiver; +import android.os.ShellCallback; import android.os.UserHandle; import android.os.UserManagerInternal; import android.text.TextUtils; @@ -42,6 +44,7 @@ import com.android.internal.util.Preconditions; import com.android.server.LocalServices; import com.android.server.SystemService; +import java.io.FileDescriptor; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -334,5 +337,13 @@ public class RoleManagerService extends SystemService { return ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId, false, true, name, null); } + + @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, + FileDescriptor err, String[] args, ShellCallback callback, + ResultReceiver resultReceiver) { + (new RoleManagerShellCommand(this)).exec( + this, in, out, err, args, callback, resultReceiver); + } } } diff --git a/services/core/java/com/android/server/role/RoleManagerShellCommand.java b/services/core/java/com/android/server/role/RoleManagerShellCommand.java new file mode 100644 index 000000000000..e1977ef083b4 --- /dev/null +++ b/services/core/java/com/android/server/role/RoleManagerShellCommand.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 com.android.server.role; + +import android.app.role.IRoleManager; +import android.app.role.IRoleManagerCallback; +import android.os.RemoteException; +import android.os.ShellCommand; +import android.os.UserHandle; + +import java.io.PrintWriter; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +class RoleManagerShellCommand extends ShellCommand { + private final IRoleManager mRoleManager; + + RoleManagerShellCommand(IRoleManager roleManager) { + mRoleManager = roleManager; + } + + private class Callback extends IRoleManagerCallback.Stub { + private final CompletableFuture<Void> mResult = new CompletableFuture<>(); + + public int waitForResult() { + try { + mResult.get(5, TimeUnit.SECONDS); + return 0; + } catch (Exception e) { + getErrPrintWriter().println("Error: " + e.toString()); + return -1; + } + } + + @Override + public void onSuccess() { + mResult.complete(null); + } + + @Override + public void onFailure() { + mResult.completeExceptionally(new RuntimeException("Failed")); + } + } + + @Override + public int onCommand(String cmd) { + if (cmd == null) { + return handleDefaultCommands(cmd); + } + + PrintWriter pw = getOutPrintWriter(); + try { + switch (cmd) { + case "add-role-holder": + return runAddRoleHolder(); + case "remove-role-holder": + return runRemoveRoleHolder(); + case "clear-role-holders": + return runClearRoleHolders(); + default: + return handleDefaultCommands(cmd); + } + } catch (RemoteException e) { + pw.println("Remote exception: " + e); + } + return -1; + } + + private int getUserIdMaybe() { + int userId = UserHandle.USER_SYSTEM; + String option = getNextOption(); + if (option != null && option.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } + return userId; + } + + private int runAddRoleHolder() throws RemoteException { + int userId = getUserIdMaybe(); + String roleName = getNextArgRequired(); + String packageName = getNextArgRequired(); + + Callback callback = new Callback(); + mRoleManager.addRoleHolderAsUser(roleName, packageName, userId, callback); + return callback.waitForResult(); + } + + private int runRemoveRoleHolder() throws RemoteException { + int userId = getUserIdMaybe(); + String roleName = getNextArgRequired(); + String packageName = getNextArgRequired(); + + Callback callback = new Callback(); + mRoleManager.removeRoleHolderAsUser(roleName, packageName, userId, callback); + return callback.waitForResult(); + } + + private int runClearRoleHolders() throws RemoteException { + int userId = getUserIdMaybe(); + String roleName = getNextArgRequired(); + + Callback callback = new Callback(); + mRoleManager.clearRoleHoldersAsUser(roleName, userId, callback); + return callback.waitForResult(); + } + + @Override + public void onHelp() { + PrintWriter pw = getOutPrintWriter(); + pw.println("Role manager (role) commands:"); + pw.println(" help"); + pw.println(" Print this help text."); + pw.println(); + pw.println(" add-role-holder [--user USER_ID] ROLE PACKAGE"); + pw.println(" remove-role-holder [--user USER_ID] ROLE PACKAGE"); + pw.println(" clear-role-holders [--user USER_ID] ROLE"); + pw.println(); + } +} diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 361622fd2934..0d66a2c8b442 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -19,6 +19,7 @@ package com.android.server.statusbar; import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS; import android.app.ActivityThread; +import android.app.Notification; import android.app.StatusBarManager; import android.content.ComponentName; import android.content.Context; @@ -1080,14 +1081,16 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } @Override - public void onNotificationActionClick(String key, int actionIndex, NotificationVisibility nv) { + public void onNotificationActionClick( + String key, int actionIndex, Notification.Action action, NotificationVisibility nv, + boolean generatedByAssistant) { enforceStatusBarService(); final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); long identity = Binder.clearCallingIdentity(); try { mNotificationDelegate.onNotificationActionClick(callingUid, callingPid, key, - actionIndex, nv); + actionIndex, action, nv, generatedByAssistant); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/storage/AppFuseBridge.java b/services/core/java/com/android/server/storage/AppFuseBridge.java index 6a0b6489f470..9d6a64701e85 100644 --- a/services/core/java/com/android/server/storage/AppFuseBridge.java +++ b/services/core/java/com/android/server/storage/AppFuseBridge.java @@ -16,6 +16,7 @@ package com.android.server.storage; +import android.os.FileUtils; import android.os.ParcelFileDescriptor; import android.system.ErrnoException; import android.system.Os; @@ -25,8 +26,6 @@ import com.android.internal.os.FuseUnavailableMountException; import com.android.internal.util.Preconditions; import com.android.server.NativeDaemonConnectorException; import libcore.io.IoUtils; -import java.io.File; -import java.io.FileNotFoundException; import java.util.concurrent.CountDownLatch; /** @@ -87,7 +86,7 @@ public class AppFuseBridge implements Runnable { } } - public ParcelFileDescriptor openFile(int pid, int mountId, int fileId, int mode) + public ParcelFileDescriptor openFile(int mountId, int fileId, int mode) throws FuseUnavailableMountException, InterruptedException { final MountScope scope; synchronized (this) { @@ -96,17 +95,14 @@ public class AppFuseBridge implements Runnable { throw new FuseUnavailableMountException(mountId); } } - if (scope.pid != pid) { - throw new SecurityException("PID does not match"); - } final boolean result = scope.waitForMount(); if (result == false) { throw new FuseUnavailableMountException(mountId); } try { - return ParcelFileDescriptor.open( - new File(scope.mountPoint, String.valueOf(fileId)), mode); - } catch (FileNotFoundException error) { + int flags = FileUtils.translateModePfdToPosix(mode); + return scope.openFile(mountId, fileId, flags); + } catch (NativeDaemonConnectorException error) { throw new FuseUnavailableMountException(mountId); } } @@ -131,17 +127,13 @@ public class AppFuseBridge implements Runnable { public static abstract class MountScope implements AutoCloseable { public final int uid; - public final int pid; public final int mountId; - public final File mountPoint; private final CountDownLatch mMounted = new CountDownLatch(1); private boolean mMountResult = false; - public MountScope(int uid, int pid, int mountId) { + public MountScope(int uid, int mountId) { this.uid = uid; - this.pid = pid; this.mountId = mountId; - this.mountPoint = new File(String.format(APPFUSE_MOUNT_NAME_TEMPLATE, uid, mountId)); } @GuardedBy("AppFuseBridge.this") @@ -159,6 +151,8 @@ public class AppFuseBridge implements Runnable { } public abstract ParcelFileDescriptor open() throws NativeDaemonConnectorException; + public abstract ParcelFileDescriptor openFile(int mountId, int fileId, int flags) + throws NativeDaemonConnectorException; } private native long native_new(); diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java index 8d27d1e043a7..c8a68b44c796 100644 --- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java +++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java @@ -402,7 +402,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi throws RemoteException { try { final int uid = context.getPackageManager() - .getPackageUid(packageName, 0); + .getPackageUidAsUser(packageName, UserHandle.getCallingUserId()); Preconditions.checkArgument(Binder.getCallingUid() == uid); } catch (IllegalArgumentException | NullPointerException | PackageManager.NameNotFoundException e) { diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 6ede423f63c8..cfec8effeede 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -1181,7 +1181,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // TODO(multi-display) TBD. if (mInfo != null && mInfo.supportsAmbientMode() && displayId == DEFAULT_DISPLAY) { try { - connector.mEngine.setInAmbientMode(mInAmbientMode, false /* animated */); + connector.mEngine.setInAmbientMode(mInAmbientMode, 0L /* duration */); } catch (RemoteException e) { Slog.w(TAG, "Failed to set ambient mode state", e); } @@ -2023,11 +2023,17 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - // TODO(b/115486823) Extends this method with specific display. - public void setInAmbientMode(boolean inAmbienMode, boolean animated) { + /** + * TODO(b/115486823) Extends this method with specific display. + * Propagate ambient state to wallpaper engine. + * + * @param inAmbientMode {@code true} when in ambient mode, {@code false} otherwise. + * @param animationDuration Duration of the animation, or 0 when immediate. + */ + public void setInAmbientMode(boolean inAmbientMode, long animationDuration) { final IWallpaperEngine engine; synchronized (mLock) { - mInAmbientMode = inAmbienMode; + mInAmbientMode = inAmbientMode; final WallpaperData data = mWallpaperMap.get(mCurrentUserId); if (data != null && data.connection != null && data.connection.mInfo != null && data.connection.mInfo.supportsAmbientMode()) { @@ -2040,7 +2046,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (engine != null) { try { - engine.setInAmbientMode(inAmbienMode, animated); + engine.setInAmbientMode(inAmbientMode, animationDuration); } catch (RemoteException e) { // Cannot talk to wallpaper engine. } @@ -2344,7 +2350,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return false; } if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) { - String msg = "Selected service does not require " + String msg = "Selected service does not have " + android.Manifest.permission.BIND_WALLPAPER + ": " + componentName; if (fromUser) { @@ -2396,6 +2402,22 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } + if (wi != null && wi.supportsAmbientMode()) { + final int hasPrivilege = mIPackageManager.checkPermission( + android.Manifest.permission.AMBIENT_WALLPAPER, wi.getPackageName(), + serviceUserId); + if (hasPrivilege != PackageManager.PERMISSION_GRANTED) { + String msg = "Selected service does not have " + + android.Manifest.permission.AMBIENT_WALLPAPER + + ": " + componentName; + if (fromUser) { + throw new SecurityException(msg); + } + Slog.w(TAG, msg); + return false; + } + } + // Bind the service! if (DEBUG) Slog.v(TAG, "Binding to:" + componentName); final int componentUid = mIPackageManager.getPackageUid(componentName.getPackageName(), diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index e4d1cfe943a8..fe0b5c250da8 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -16,9 +16,9 @@ package com.android.server.wm; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -706,7 +706,7 @@ final class AccessibilityController { mWindowManager.getDefaultDisplay().getRealSize(mTempPoint); surfaceControl = mService.getDefaultDisplayContentLocked().makeOverlay() .setName(SURFACE_TITLE) - .setSize(mTempPoint.x, mTempPoint.y) // not a typo + .setBufferSize(mTempPoint.x, mTempPoint.y) // not a typo .setFormat(PixelFormat.TRANSLUCENT) .build(); } catch (OutOfResourcesException oore) { @@ -784,7 +784,7 @@ final class AccessibilityController { public void updateSize() { synchronized (mService.mGlobalLock) { mWindowManager.getDefaultDisplay().getRealSize(mTempPoint); - mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y); + mSurfaceControl.setBufferSize(mTempPoint.x, mTempPoint.y); invalidate(mDirtyRect); } } diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java index ed366453028e..84750b385d97 100644 --- a/services/core/java/com/android/server/wm/ActivityDisplay.java +++ b/services/core/java/com/android/server/wm/ActivityDisplay.java @@ -39,8 +39,6 @@ import static com.android.server.am.ActivityDisplayProto.ID; import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY; import static com.android.server.am.ActivityDisplayProto.STACKS; import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; -import static com.android.server.wm.ActivityStackSupervisor.FindTaskResult; -import static com.android.server.wm.ActivityStackSupervisor.TAG_STATES; import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES; @@ -48,6 +46,8 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.RootActivityContainer.FindTaskResult; +import static com.android.server.wm.RootActivityContainer.TAG_STATES; import android.annotation.Nullable; import android.app.ActivityOptions; @@ -84,7 +84,8 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> */ private static int sNextFreeStackId = 0; - private ActivityStackSupervisor mSupervisor; + private ActivityTaskManagerService mService; + private RootActivityContainer mRootActivityContainer; /** Actual Display this object tracks. */ int mDisplayId; Display mDisplay; @@ -141,8 +142,9 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> private final FindTaskResult mTmpFindTaskResult = new FindTaskResult(); - ActivityDisplay(ActivityStackSupervisor supervisor, Display display) { - mSupervisor = supervisor; + ActivityDisplay(RootActivityContainer root, Display display) { + mRootActivityContainer = root; + mService = root.mService; mDisplayId = display.getDisplayId(); mDisplay = display; mWindowContainerController = createWindowContainerController(); @@ -168,7 +170,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> if (displayId != DEFAULT_DISPLAY) { final int displayState = mDisplay.getState(); if (displayState == Display.STATE_OFF && mOffToken == null) { - mOffToken = mSupervisor.mService.acquireSleepToken("Display-off", displayId); + mOffToken = mService.acquireSleepToken("Display-off", displayId); } else if (displayState == Display.STATE_ON && mOffToken != null) { mOffToken.release(); mOffToken = null; @@ -179,6 +181,11 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> mWindowContainerController.onDisplayChanged(); } + @Override + public void onInitializeOverrideConfiguration(Configuration config) { + getOverrideConfiguration().updateFrom(config); + } + void addChild(ActivityStack stack, int position) { if (position == POSITION_BOTTOM) { position = 0; @@ -189,7 +196,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> + " to displayId=" + mDisplayId + " position=" + position); addStackReferenceIfNeeded(stack); positionChildAt(stack, position); - mSupervisor.mService.updateSleepIfNeededLocked(); + mService.updateSleepIfNeededLocked(); } void removeChild(ActivityStack stack) { @@ -201,7 +208,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> } removeStackReferenceIfNeeded(stack); releaseSelfIfNeeded(); - mSupervisor.mService.updateSleepIfNeededLocked(); + mService.updateSleepIfNeededLocked(); onStackOrderChanged(); } @@ -252,7 +259,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> final ActivityStack currentFocusedStack = getFocusedStack(); if (currentFocusedStack != prevFocusedStack) { mLastFocusedStack = prevFocusedStack; - EventLogTags.writeAmFocusedStack(mSupervisor.mCurrentUser, mDisplayId, + EventLogTags.writeAmFocusedStack(mRootActivityContainer.mCurrentUser, mDisplayId, currentFocusedStack == null ? -1 : currentFocusedStack.getStackId(), mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(), updateLastFocusedStackReason); @@ -409,10 +416,10 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> } } - final ActivityTaskManagerService service = mSupervisor.mService; - if (!isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow, - service.mSupportsSplitScreenMultiWindow, service.mSupportsFreeformWindowManagement, - service.mSupportsPictureInPicture, activityType)) { + if (!isWindowingModeSupported(windowingMode, mService.mSupportsMultiWindow, + mService.mSupportsSplitScreenMultiWindow, + mService.mSupportsFreeformWindowManagement, mService.mSupportsPictureInPicture, + activityType)) { throw new IllegalArgumentException("Can't create stack for unsupported windowingMode=" + windowingMode); } @@ -425,10 +432,12 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType, int stackId, boolean onTop) { if (windowingMode == WINDOWING_MODE_PINNED) { - return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop); + return (T) new PinnedActivityStack(this, stackId, + mRootActivityContainer.mStackSupervisor, onTop); } - return (T) new ActivityStack( - this, stackId, mSupervisor, windowingMode, activityType, onTop); + return (T) new ActivityStack(this, stackId, + mRootActivityContainer.mStackSupervisor, windowingMode, activityType, + onTop); } /** @@ -543,7 +552,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = mStacks.get(stackNdx); // TODO(b/111541062): Check if resumed activity on this display instead - if (!mSupervisor.isTopDisplayFocusedStack(stack) + if (!mRootActivityContainer.isTopDisplayFocusedStack(stack) && stack.getResumedActivity() != null) { if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack + " mResumedActivity=" + stack.getResumedActivity()); @@ -608,7 +617,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> if (stack.getWindowingMode() != windowingMode) { continue; } - mSupervisor.removeStack(stack); + mRootActivityContainer.mStackSupervisor.removeStack(stack); } } } @@ -623,7 +632,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> for (int i = mStacks.size() - 1; i >= 0; --i) { final ActivityStack stack = mStacks.get(i); if (stack.getActivityType() == activityType) { - mSupervisor.removeStack(stack); + mRootActivityContainer.mStackSupervisor.removeStack(stack); } } } @@ -685,7 +694,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> } private void onSplitScreenModeDismissed() { - mSupervisor.mWindowManager.deferSurfaceLayout(); + mRootActivityContainer.mWindowManager.deferSurfaceLayout(); try { // Adjust the windowing mode of any stack in secondary split-screen to fullscreen. for (int i = mStacks.size() - 1; i >= 0; --i) { @@ -709,12 +718,12 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> mHomeStack.moveToFront("onSplitScreenModeDismissed"); topFullscreenStack.moveToFront("onSplitScreenModeDismissed"); } - mSupervisor.mWindowManager.continueSurfaceLayout(); + mRootActivityContainer.mWindowManager.continueSurfaceLayout(); } } private void onSplitScreenModeActivated() { - mSupervisor.mWindowManager.deferSurfaceLayout(); + mRootActivityContainer.mWindowManager.deferSurfaceLayout(); try { // Adjust the windowing mode of any affected by split-screen to split-screen secondary. for (int i = mStacks.size() - 1; i >= 0; --i) { @@ -729,7 +738,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> false /* creating */); } } finally { - mSupervisor.mWindowManager.continueSurfaceLayout(); + mRootActivityContainer.mWindowManager.continueSurfaceLayout(); } } @@ -824,11 +833,10 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> int validateWindowingMode(int windowingMode, @Nullable ActivityRecord r, @Nullable TaskRecord task, int activityType) { // Make sure the windowing mode we are trying to use makes sense for what is supported. - final ActivityTaskManagerService service = mSupervisor.mService; - boolean supportsMultiWindow = service.mSupportsMultiWindow; - boolean supportsSplitScreen = service.mSupportsSplitScreenMultiWindow; - boolean supportsFreeform = service.mSupportsFreeformWindowManagement; - boolean supportsPip = service.mSupportsPictureInPicture; + boolean supportsMultiWindow = mService.mSupportsMultiWindow; + boolean supportsSplitScreen = mService.mSupportsSplitScreenMultiWindow; + boolean supportsFreeform = mService.mSupportsFreeformWindowManagement; + boolean supportsPip = mService.mSupportsPictureInPicture; if (supportsMultiWindow) { if (task != null) { supportsMultiWindow = task.isResizeable(); @@ -932,7 +940,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> // This activity can be considered the top running activity if we are not considering // the locked state, the keyguard isn't locked, or we can show when locked. if (topRunning != null && considerKeyguardState - && mSupervisor.getKeyguardController().isKeyguardLocked() + && mRootActivityContainer.mStackSupervisor.getKeyguardController().isKeyguardLocked() && !topRunning.canShowWhenLocked()) { return null; } @@ -1010,7 +1018,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> @Override protected ConfigurationContainer getParent() { - return mSupervisor; + return mRootActivityContainer; } boolean isPrivate() { @@ -1043,8 +1051,8 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> // released (no more ActivityStack). But, we cannot release it at that moment or the // related WindowContainer and WindowContainerController will also be removed. So, we // set display as removed after reparenting stack finished. - final ActivityDisplay toDisplay = mSupervisor.getDefaultDisplay(); - mSupervisor.beginDeferResume(); + final ActivityDisplay toDisplay = mRootActivityContainer.getDefaultDisplay(); + mRootActivityContainer.mStackSupervisor.beginDeferResume(); try { int numStacks = mStacks.size(); // Keep the order from bottom to top. @@ -1070,7 +1078,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> numStacks = mStacks.size(); } } finally { - mSupervisor.endDeferResume(); + mRootActivityContainer.mStackSupervisor.endDeferResume(); } mRemoved = true; @@ -1082,9 +1090,9 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> releaseSelfIfNeeded(); if (!mAllSleepTokens.isEmpty()) { - mSupervisor.mSleepTokens.removeAll(mAllSleepTokens); + mRootActivityContainer.mSleepTokens.removeAll(mAllSleepTokens); mAllSleepTokens.clear(); - mSupervisor.mService.updateSleepIfNeededLocked(); + mService.updateSleepIfNeededLocked(); } } @@ -1092,8 +1100,9 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> if (mStacks.isEmpty() && mRemoved) { mWindowContainerController.removeContainer(); mWindowContainerController = null; - mSupervisor.removeChild(this); - mSupervisor.getKeyguardController().onDisplayRemoved(mDisplayId); + mRootActivityContainer.removeChild(this); + mRootActivityContainer.mStackSupervisor + .getKeyguardController().onDisplayRemoved(mDisplayId); } } @@ -1122,7 +1131,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> boolean shouldSleep() { return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty()) - && (mSupervisor.mService.mRunningVoice == null); + && (mService.mRunningVoice == null); } void setFocusedApp(ActivityRecord r, boolean moveFocusNow) { @@ -1213,7 +1222,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> @Nullable ActivityRecord getHomeActivity() { - return getHomeActivityForUser(mSupervisor.mCurrentUser); + return getHomeActivityForUser(mRootActivityContainer.mCurrentUser); } @Nullable diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java index e3133efb890c..eff0f75466d9 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java @@ -42,7 +42,7 @@ import java.lang.annotation.RetentionPolicy; * It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled} * or into {@code FINISHED} with {@link #onActivityLaunchFinished}. These are terminal states. * - * Note that the {@link ActivityRecord} provided as a parameter to some state transitions isn't + * Note that the {@code ActivityRecordProto} provided as a parameter to some state transitions isn't * necessarily the same within a single launch sequence: it is only the top-most activity at the * time (if any). Trampoline activities coalesce several activity starts into a single launch * sequence. @@ -94,6 +94,14 @@ public interface ActivityMetricsLaunchObserver { public static final int TEMPERATURE_HOT = 3; /** + * Typedef marker that a {@code byte[]} actually contains an + * <a href="proto/android/server/activitymanagerservice.proto">ActivityRecordProto</a> + * in the protobuf format. + */ + @Retention(RetentionPolicy.SOURCE) + @interface ActivityRecordProto {} + + /** * Notifies the observer that a new launch sequence has begun as a result of a new intent. * * Once a launch sequence begins, the resolved activity will either subsequently start with @@ -135,7 +143,7 @@ public interface ActivityMetricsLaunchObserver { * Multiple calls to this method cannot occur without first terminating the current * launch sequence. */ - public void onActivityLaunched(@NonNull ActivityRecord activity, + public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity, @Temperature int temperature); /** @@ -157,7 +165,7 @@ public interface ActivityMetricsLaunchObserver { * in the case of a trampoline, multiple activities could've been started * and only the latest activity is reported here. */ - public void onActivityLaunchCancelled(@Nullable ActivityRecord abortingActivity); + public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] abortingActivity); /** * Notifies the observer that the current launch sequence has been successfully finished. @@ -178,5 +186,5 @@ public interface ActivityMetricsLaunchObserver { * and only the latest activity that was top-most during first-frame drawn * is reported here. */ - public void onActivityLaunchFinished(@NonNull ActivityRecord finalActivity); + public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] finalActivity); } diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserverRegistry.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserverRegistry.java new file mode 100644 index 000000000000..fa90dc5b83f4 --- /dev/null +++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserverRegistry.java @@ -0,0 +1,60 @@ +/* + * 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 android.annotation.NonNull; + +/** + * Multi-cast delegate implementation for {@link ActivityMetricsLaunchObserver}. + * + * <br/><br/> + * This enables multiple launch observers to subscribe to {@link ActivityMetricsLogger} + * independently of each other. + * + * <br/><br/> + * Some callbacks in {@link ActivityMetricsLaunchObserver} have a {@code byte[]} + * parameter; this array is reused by all the registered observers, so it must not be written to + * (i.e. all observers must treat any array parameters as immutable). + * + * <br /><br /> + * Multi-cast invocations occurs sequentially in-order of registered observers. + */ +public interface ActivityMetricsLaunchObserverRegistry { + /** + * Register an extra launch observer to receive the multi-cast. + * + * <br /><br /> + * Multi-cast invocation happens in the same order the observers were registered. For example, + * <pre> + * registerLaunchObserver(A) + * registerLaunchObserver(B) + * + * obs.onIntentFailed() -> + * A.onIntentFailed() + * B.onIntentFailed() + * </pre> + */ + void registerLaunchObserver(@NonNull ActivityMetricsLaunchObserver launchObserver); + + /** + * Unregister an existing launch observer. It will not receive the multi-cast in the future. + * + * <br /><br /> + * This does nothing if this observer was not already registered. + */ + void unregisterLaunchObserver(@NonNull ActivityMetricsLaunchObserver launchObserver); +} diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 1c08d039207b..16df52d4ef65 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -99,10 +99,12 @@ import android.util.SparseArray; import android.util.SparseIntArray; import android.util.StatsLog; import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; import com.android.internal.logging.MetricsLogger; import com.android.internal.os.BackgroundThread; import com.android.internal.os.SomeArgs; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; /** @@ -168,7 +170,8 @@ class ActivityMetricsLogger { * Due to the global single concurrent launch sequence, all calls to this observer must be made * in-order on the same thread to fulfill the "happens-before" guarantee in LaunchObserver. */ - private final ActivityMetricsLaunchObserver mLaunchObserver = null; + private final LaunchObserverRegistryImpl mLaunchObserver; + @VisibleForTesting static final int LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE = 512; private final class H extends Handler { @@ -263,6 +266,7 @@ class ActivityMetricsLogger { mSupervisor = supervisor; mContext = context; mHandler = new H(looper); + mLaunchObserver = new LaunchObserverRegistryImpl(looper); } void logWindowState() { @@ -277,7 +281,8 @@ class ActivityMetricsLogger { mLastLogTimeSecs = now; mWindowState = WINDOW_STATE_INVALID; - ActivityStack stack = mSupervisor.getTopDisplayFocusedStack(); + ActivityStack stack = + mSupervisor.mRootActivityContainer.getTopDisplayFocusedStack(); if (stack == null) { return; } @@ -289,7 +294,7 @@ class ActivityMetricsLogger { @WindowingMode int windowingMode = stack.getWindowingMode(); if (windowingMode == WINDOWING_MODE_PINNED) { - stack = mSupervisor.findStackBehind(stack); + stack = mSupervisor.mRootActivityContainer.findStackBehind(stack); windowingMode = stack.getWindowingMode(); } switch (windowingMode) { @@ -850,9 +855,10 @@ class ActivityMetricsLogger { builder.addTaggedData(FIELD_TARGET_UID_HAS_ANY_VISIBLE_WINDOW, targetUidHasAnyVisibleWindow ? 1 : 0); builder.addTaggedData(FIELD_TARGET_WHITELIST_TAG, targetWhitelistTag); - builder.addTaggedData(FIELD_TARGET_SHORT_COMPONENT_NAME, r.shortComponentName); builder.addTaggedData(FIELD_COMING_FROM_PENDING_INTENT, comingFromPendingIntent ? 1 : 0); - builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction()); + if (intent != null) { + builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction()); + } if (callerApp != null) { builder.addTaggedData(FIELD_PROCESS_RECORD_PROCESS_NAME, callerApp.mName); builder.addTaggedData(FIELD_PROCESS_RECORD_CUR_PROC_STATE, @@ -881,29 +887,34 @@ class ActivityMetricsLogger { (nowUptime - callerApp.getWhenUnimportant())); } } - builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY, r.info.targetActivity); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_FLAGS, r.info.flags); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_REAL_ACTIVITY, r.realActivity.toShortString()); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME, r.shortComponentName); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_PROCESS_NAME, r.processName); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_FULLSCREEN, r.fullscreen ? 1 : 0); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY, r.noDisplay ? 1 : 0); - if (r.lastVisibleTime != 0) { - builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE, - (nowUptime - r.lastVisibleTime)); - } - if (r.resultTo != null) { - builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME, r.resultTo.packageName); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME, - r.resultTo.shortComponentName); - } - builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE, r.visible ? 1 : 0); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD, - r.visibleIgnoringKeyguard ? 1 : 0); - if (r.lastLaunchTime != 0) { - builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH, - (nowUptime - r.lastLaunchTime)); + if (r != null) { + builder.addTaggedData(FIELD_TARGET_SHORT_COMPONENT_NAME, r.shortComponentName); + builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode); + builder.addTaggedData(FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY, r.info.targetActivity); + builder.addTaggedData(FIELD_ACTIVITY_RECORD_FLAGS, r.info.flags); + builder.addTaggedData(FIELD_ACTIVITY_RECORD_REAL_ACTIVITY, + r.realActivity.toShortString()); + builder.addTaggedData(FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME, r.shortComponentName); + builder.addTaggedData(FIELD_ACTIVITY_RECORD_PROCESS_NAME, r.processName); + builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_FULLSCREEN, r.fullscreen ? 1 : 0); + builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY, r.noDisplay ? 1 : 0); + if (r.lastVisibleTime != 0) { + builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE, + (nowUptime - r.lastVisibleTime)); + } + if (r.resultTo != null) { + builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME, + r.resultTo.packageName); + builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME, + r.resultTo.shortComponentName); + } + builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE, r.visible ? 1 : 0); + builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD, + r.visibleIgnoringKeyguard ? 1 : 0); + if (r.lastLaunchTime != 0) { + builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH, + (nowUptime - r.lastLaunchTime)); + } } mMetricsLogger.write(builder); } @@ -993,12 +1004,19 @@ class ActivityMetricsLogger { } } + public ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry() { + return mLaunchObserver; + } + /** Notify the {@link ActivityMetricsLaunchObserver} that a new launch sequence has begun. */ private void launchObserverNotifyIntentStarted(Intent intent) { - if (mLaunchObserver != null) { - // Beginning a launch is timing sensitive and so should be observed as soon as possible. - mLaunchObserver.onIntentStarted(intent); - } + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, + "MetricsLogger:launchObserverNotifyIntentStarted"); + + // Beginning a launch is timing sensitive and so should be observed as soon as possible. + mLaunchObserver.onIntentStarted(intent); + + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } /** @@ -1007,9 +1025,12 @@ class ActivityMetricsLogger { * intent being delivered to the top running activity. */ private void launchObserverNotifyIntentFailed() { - if (mLaunchObserver != null) { - mLaunchObserver.onIntentFailed(); - } + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, + "MetricsLogger:launchObserverNotifyIntentFailed"); + + mLaunchObserver.onIntentFailed(); + + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } /** @@ -1017,14 +1038,17 @@ class ActivityMetricsLogger { * has started. */ private void launchObserverNotifyActivityLaunched(WindowingModeTransitionInfo info) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, + "MetricsLogger:launchObserverNotifyActivityLaunched"); + @ActivityMetricsLaunchObserver.Temperature int temperature = convertTransitionTypeToLaunchObserverTemperature(getTransitionType(info)); - if (mLaunchObserver != null) { - // Beginning a launch is timing sensitive and so should be observed as soon as possible. - mLaunchObserver.onActivityLaunched(info.launchedActivity, - temperature); - } + // Beginning a launch is timing sensitive and so should be observed as soon as possible. + mLaunchObserver.onActivityLaunched(convertActivityRecordToProto(info.launchedActivity), + temperature); + + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } /** @@ -1032,11 +1056,15 @@ class ActivityMetricsLogger { * cancelled. */ private void launchObserverNotifyActivityLaunchCancelled(WindowingModeTransitionInfo info) { - final ActivityRecord launchedActivity = info != null ? info.launchedActivity : null; + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, + "MetricsLogger:launchObserverNotifyActivityLaunchCancelled"); - if (mLaunchObserver != null) { - mLaunchObserver.onActivityLaunchCancelled(launchedActivity); - } + final @ActivityMetricsLaunchObserver.ActivityRecordProto byte[] activityRecordProto = + info != null ? convertActivityRecordToProto(info.launchedActivity) : null; + + mLaunchObserver.onActivityLaunchCancelled(activityRecordProto); + + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } /** @@ -1044,11 +1072,34 @@ class ActivityMetricsLogger { * has fully finished (successfully). */ private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info) { - final ActivityRecord launchedActivity = info.launchedActivity; + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, + "MetricsLogger:launchObserverNotifyActivityLaunchFinished"); - if (mLaunchObserver != null) { - mLaunchObserver.onActivityLaunchFinished(launchedActivity); - } + mLaunchObserver.onActivityLaunchFinished( + convertActivityRecordToProto(info.launchedActivity)); + + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + + @VisibleForTesting + static @ActivityMetricsLaunchObserver.ActivityRecordProto byte[] + convertActivityRecordToProto(ActivityRecord record) { + // May take non-negligible amount of time to convert ActivityRecord into a proto, + // so track the time. + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, + "MetricsLogger:convertActivityRecordToProto"); + + // There does not appear to be a way to 'reset' a ProtoOutputBuffer stream, + // so create a new one every time. + final ProtoOutputStream protoOutputStream = + new ProtoOutputStream(LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE); + // Write this data out as the top-most ActivityRecordProto (i.e. it is not a sub-object). + record.writeToProto(protoOutputStream); + final byte[] bytes = protoOutputStream.getBytes(); + + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + + return bytes; } private static @ActivityMetricsLaunchObserver.Temperature int diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 5e92b9e4d46a..6f2461bd8489 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -18,7 +18,17 @@ package com.android.server.wm; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX; +import static android.app.ActivityOptions.ANIM_CLIP_REVEAL; +import static android.app.ActivityOptions.ANIM_CUSTOM; +import static android.app.ActivityOptions.ANIM_NONE; +import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS; +import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION; +import static android.app.ActivityOptions.ANIM_SCALE_UP; import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION; +import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN; +import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP; +import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; +import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; import static android.app.ActivityTaskManager.INVALID_STACK_ID; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.AppOpsManager.MODE_ALLOWED; @@ -63,6 +73,7 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.pm.ActivityInfo.isFixedOrientationLandscape; import static android.content.pm.ActivityInfo.isFixedOrientationPortrait; import static android.content.res.Configuration.EMPTY; @@ -111,12 +122,18 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; -import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; +import static com.android.server.wm.ActivityTaskManagerService + .RELAUNCH_REASON_WINDOWING_MODE_RESIZE; import static com.android.server.wm.IdentifierProto.HASH_CODE; import static com.android.server.wm.IdentifierProto.TITLE; import static com.android.server.wm.IdentifierProto.USER_ID; import static com.android.server.wm.TaskPersister.DEBUG; import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.END_TAG; @@ -147,6 +164,7 @@ import android.content.pm.ApplicationInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.GraphicBuffer; import android.graphics.Rect; import android.os.Binder; import android.os.Build; @@ -167,11 +185,14 @@ import android.util.MergedConfiguration; import android.util.Slog; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; +import android.view.AppTransitionAnimationSpec; +import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IApplicationToken; import android.view.RemoteAnimationDefinition; import android.view.WindowManager.LayoutParams; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ResolverActivity; import com.android.internal.content.ReferrerIntent; import com.android.internal.util.XmlUtils; @@ -200,7 +221,7 @@ import java.util.Objects; /** * An entry in the history stack, representing an activity. */ -final class ActivityRecord extends ConfigurationContainer implements AppWindowContainerListener { +final class ActivityRecord extends ConfigurationContainer { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_ATM; private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION; private static final String TAG_SAVED_STATE = TAG + POSTFIX_SAVED_STATE; @@ -225,7 +246,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo final ActivityTaskManagerService service; // owner final IApplicationToken.Stub appToken; // window manager token - AppWindowContainerController mWindowContainerController; + // TODO: Remove after unification + AppWindowToken mAppWindowToken; + final ActivityInfo info; // all about me // TODO: This is duplicated state already contained in info.applicationInfo - remove ApplicationInfo appInfo; // information about activity's app @@ -322,6 +345,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo private boolean inHistory; // are we in the history stack? final ActivityStackSupervisor mStackSupervisor; + final RootActivityContainer mRootActivityContainer; static final int STARTING_WINDOW_NOT_SHOWN = 0; static final int STARTING_WINDOW_SHOWN = 1; @@ -769,10 +793,16 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } /** - * See {@link AppWindowContainerController#setWillCloseOrEnterPip(boolean)} + * Notifies AWT that this app is waiting to pause in order to determine if it will enter PIP. + * This information helps AWT know that the app is in the process of pausing before it gets the + * signal on the WM side. */ void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) { - getWindowContainerController().setWillCloseOrEnterPip(willCloseOrEnterPip); + if (mAppWindowToken == null) { + return; + } + + mAppWindowToken.setWillCloseOrEnterPip(willCloseOrEnterPip); } static class Token extends IApplicationToken.Stub { @@ -844,6 +874,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor, ActivityOptions options, ActivityRecord sourceRecord) { service = _service; + mRootActivityContainer = _service.mRootActivityContainer; appToken = new Token(this, _intent); info = aInfo; launchedFromPid = _launchedFromPid; @@ -991,13 +1022,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo return hasProcess() && app.hasThread(); } - AppWindowContainerController getWindowContainerController() { - return mWindowContainerController; - } - - void createWindowContainer() { - if (mWindowContainerController != null) { - throw new IllegalArgumentException("Window container=" + mWindowContainerController + void createAppWindowToken() { + if (mAppWindowToken != null) { + throw new IllegalArgumentException("App Window Token=" + mAppWindowToken + " already created for r=" + this); } @@ -1010,12 +1037,31 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // Make sure override configuration is up-to-date before using to create window controller. updateOverrideConfiguration(); - mWindowContainerController = new AppWindowContainerController(taskController, appToken, - realActivity, this, Integer.MAX_VALUE /* add on top */, info.screenOrientation, - fullscreen, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, info.configChanges, - task.voiceSession != null, mLaunchTaskBehind, isAlwaysFocusable(), - appInfo.targetSdkVersion, mRotationAnimationHint, - ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L); + // TODO: remove after unification + mAppWindowToken = service.mWindowManager.mRoot.getAppWindowToken(appToken.asBinder()); + if (mAppWindowToken != null) { + // TODO: Should this throw an exception instead? + Slog.w(TAG, "Attempted to add existing app token: " + appToken); + } else { + final Task container = taskController.mContainer; + if (container == null) { + throw new IllegalArgumentException("AppWindowContainerController: invalid " + + " controller=" + taskController); + } + mAppWindowToken = createAppWindow(service.mWindowManager, appToken, + task.voiceSession != null, container.getDisplayContent(), + ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this) + * 1000000L, fullscreen, + (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, appInfo.targetSdkVersion, + info.screenOrientation, mRotationAnimationHint, info.configChanges, + mLaunchTaskBehind, isAlwaysFocusable()); + if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) { + Slog.v(TAG, "addAppToken: " + + mAppWindowToken + " controller=" + taskController + " at " + + Integer.MAX_VALUE); + } + container.addChild(mAppWindowToken, Integer.MAX_VALUE /* add on top */); + } task.addActivityToTop(this); @@ -1026,17 +1072,51 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo mLastReportedPictureInPictureMode = inPinnedWindowingMode(); } + boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo, + CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, + IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning, + boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) { + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG, "setAppStartingWindow: token=" + appToken + + " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask + + " taskSwitch=" + taskSwitch + " processRunning=" + processRunning + + " allowTaskSnapshot=" + allowTaskSnapshot); + } + if (mAppWindowToken == null) { + Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + appToken); + return false; + } + return mAppWindowToken.addStartingWindow(pkg, theme, compatInfo, nonLocalizedLabel, + labelRes, icon, logo, windowFlags, transferFrom, newTask, taskSwitch, + processRunning, allowTaskSnapshot, activityCreated, fromRecents); + } + + // TODO: Remove after unification + @VisibleForTesting + AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token, + boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, + boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, + int rotationAnimationHint, int configChanges, boolean launchTaskBehind, + boolean alwaysFocusable) { + return new AppWindowToken(service, token, realActivity, voiceInteraction, dc, + inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation, + rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, + this); + } + void removeWindowContainer() { - // Do not try to remove a window container if we have already removed it. - if (mWindowContainerController == null) { + if (service.mWindowManager.mRoot == null) return; + + final DisplayContent dc = service.mWindowManager.mRoot.getDisplayContent( + getDisplayId()); + if (dc == null) { + Slog.w(TAG, "removeWindowContainer: Attempted to remove token: " + + appToken + " from non-existing displayId=" + getDisplayId()); return; } - // Resume key dispatching if it is currently paused before we remove the container. resumeKeyDispatchingLocked(); - - mWindowContainerController.removeContainer(getDisplayId()); - mWindowContainerController = null; + dc.removeAppToken(appToken.asBinder()); } /** @@ -1044,6 +1124,10 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo * should ensure that the {@param newTask} is not already the parent of this activity. */ void reparent(TaskRecord newTask, int position, String reason) { + if (mAppWindowToken == null) { + Slog.w(TAG, "reparent: Attempted to reparent non-existing app token: " + appToken); + return; + } final TaskRecord prevTask = task; if (prevTask == newTask) { throw new IllegalArgumentException(reason + ": task=" + newTask @@ -1059,8 +1143,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo + " r=" + this + " (" + prevTask.getStackId() + ")"); } - // Must reparent first in window manager - mWindowContainerController.reparent(newTask.getWindowContainerController(), position); + mAppWindowToken.reparent(newTask.getWindowContainerController(), position); // Reparenting prevents informing the parent stack of activity removal in the case that // the new stack has the same parent. we must manually signal here if this is not the case. @@ -1200,7 +1283,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } boolean isFocusable() { - return mStackSupervisor.isFocusable(this, isAlwaysFocusable()); + return mRootActivityContainer.isFocusable(this, isAlwaysFocusable()); } boolean isResizeable() { @@ -1353,7 +1436,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo return false; } - if (mStackSupervisor.getTopResumedActivity() == this) { + if (mRootActivityContainer.getTopResumedActivity() == this) { if (DEBUG_FOCUS) { Slog.d(TAG_FOCUS, "moveActivityStackToFront: already on top, activity=" + this); } @@ -1366,7 +1449,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo stack.moveToFront(reason, task); // Report top activity change to tracking services and WM - if (mStackSupervisor.getTopResumedActivity() == this) { + if (mRootActivityContainer.getTopResumedActivity() == this) { // TODO(b/111361570): Support multiple focused apps in WM service.setResumedActivityUncheckLocked(this, reason); } @@ -1490,7 +1573,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo void applyOptionsLocked() { if (pendingOptions != null && pendingOptions.getAnimationType() != ANIM_SCENE_TRANSITION) { - mWindowContainerController.applyOptionsLocked(pendingOptions, intent); + applyOptionsLocked(pendingOptions, intent); if (task == null) { clearOptionsLocked(false /* withAbort */); } else { @@ -1500,6 +1583,104 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } } + /** + * Apply override app transition base on options & animation type. + */ + void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) { + final int animationType = pendingOptions.getAnimationType(); + final DisplayContent displayContent = mAppWindowToken.getDisplayContent(); + switch (animationType) { + case ANIM_CUSTOM: + displayContent.mAppTransition.overridePendingAppTransition( + pendingOptions.getPackageName(), + pendingOptions.getCustomEnterResId(), + pendingOptions.getCustomExitResId(), + pendingOptions.getOnAnimationStartListener()); + break; + case ANIM_CLIP_REVEAL: + displayContent.mAppTransition.overridePendingAppTransitionClipReveal( + pendingOptions.getStartX(), pendingOptions.getStartY(), + pendingOptions.getWidth(), pendingOptions.getHeight()); + if (intent.getSourceBounds() == null) { + intent.setSourceBounds(new Rect(pendingOptions.getStartX(), + pendingOptions.getStartY(), + pendingOptions.getStartX() + pendingOptions.getWidth(), + pendingOptions.getStartY() + pendingOptions.getHeight())); + } + break; + case ANIM_SCALE_UP: + displayContent.mAppTransition.overridePendingAppTransitionScaleUp( + pendingOptions.getStartX(), pendingOptions.getStartY(), + pendingOptions.getWidth(), pendingOptions.getHeight()); + if (intent.getSourceBounds() == null) { + intent.setSourceBounds(new Rect(pendingOptions.getStartX(), + pendingOptions.getStartY(), + pendingOptions.getStartX() + pendingOptions.getWidth(), + pendingOptions.getStartY() + pendingOptions.getHeight())); + } + break; + case ANIM_THUMBNAIL_SCALE_UP: + case ANIM_THUMBNAIL_SCALE_DOWN: + final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP); + final GraphicBuffer buffer = pendingOptions.getThumbnail(); + displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer, + pendingOptions.getStartX(), pendingOptions.getStartY(), + pendingOptions.getOnAnimationStartListener(), + scaleUp); + if (intent.getSourceBounds() == null && buffer != null) { + intent.setSourceBounds(new Rect(pendingOptions.getStartX(), + pendingOptions.getStartY(), + pendingOptions.getStartX() + buffer.getWidth(), + pendingOptions.getStartY() + buffer.getHeight())); + } + break; + case ANIM_THUMBNAIL_ASPECT_SCALE_UP: + case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN: + final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs(); + final IAppTransitionAnimationSpecsFuture specsFuture = + pendingOptions.getSpecsFuture(); + if (specsFuture != null) { + // TODO(multidisplay): Shouldn't be really used anymore from next CL. + displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture( + specsFuture, pendingOptions.getOnAnimationStartListener(), + animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP); + } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN + && specs != null) { + displayContent.mAppTransition.overridePendingAppTransitionMultiThumb( + specs, pendingOptions.getOnAnimationStartListener(), + pendingOptions.getAnimationFinishedListener(), false); + } else { + displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb( + pendingOptions.getThumbnail(), + pendingOptions.getStartX(), pendingOptions.getStartY(), + pendingOptions.getWidth(), pendingOptions.getHeight(), + pendingOptions.getOnAnimationStartListener(), + (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP)); + if (intent.getSourceBounds() == null) { + intent.setSourceBounds(new Rect(pendingOptions.getStartX(), + pendingOptions.getStartY(), + pendingOptions.getStartX() + pendingOptions.getWidth(), + pendingOptions.getStartY() + pendingOptions.getHeight())); + } + } + break; + case ANIM_OPEN_CROSS_PROFILE_APPS: + displayContent.mAppTransition + .overridePendingAppTransitionStartCrossProfileApps(); + break; + case ANIM_REMOTE_ANIMATION: + // TODO(multidisplay): Will pass displayId and adjust dependencies from next CL. + displayContent.mAppTransition.overridePendingAppTransitionRemote( + pendingOptions.getRemoteAnimationAdapter()); + break; + case ANIM_NONE: + break; + default: + Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType); + break; + } + } + ActivityOptions getOptionsForTargetActivityLocked() { return pendingOptions != null ? pendingOptions.forTargetActivity() : null; } @@ -1532,8 +1713,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo if (!keysPaused) { keysPaused = true; - if (mWindowContainerController != null) { - mWindowContainerController.pauseKeyDispatching(); + // TODO: remove the check after unification with AppWindowToken. The DC check is not + // needed after no mock mAppWindowToken in tests. + if (mAppWindowToken != null && mAppWindowToken.getDisplayContent() != null) { + mAppWindowToken.getDisplayContent().getInputMonitor().pauseDispatchingLw( + mAppWindowToken); } } } @@ -1542,8 +1726,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo if (keysPaused) { keysPaused = false; - if (mWindowContainerController != null) { - mWindowContainerController.resumeKeyDispatching(); + // TODO: remove the check after unification with AppWindowToken. The DC check is not + // needed after no mock mAppWindowToken in tests. + if (mAppWindowToken != null && mAppWindowToken.getDisplayContent() != null) { + mAppWindowToken.getDisplayContent().getInputMonitor().resumeDispatchingLw( + mAppWindowToken); } } } @@ -1565,11 +1752,16 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } void setVisibility(boolean visible) { - mWindowContainerController.setVisibility(visible, mDeferHidingClient); + if (mAppWindowToken == null) { + Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + + appToken); + return; + } + mAppWindowToken.setVisibility(visible, mDeferHidingClient); mStackSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this); } - // TODO: Look into merging with #setVisibility() + // TODO: Look into merging with #commitVisibility() void setVisible(boolean newVisible) { visible = newVisible; mDeferHidingClient = !visible && mDeferHidingClient; @@ -1599,7 +1791,12 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // an indication that the Surface will eventually be destroyed. // This however isn't necessarily true if we are going to sleep. if (state == STOPPING && !isSleeping()) { - mWindowContainerController.notifyAppStopping(); + if (mAppWindowToken == null) { + Slog.w(TAG_WM, "Attempted to notify stopping on non-existing app token: " + + appToken); + return; + } + mAppWindowToken.detachChildren(); } } @@ -1637,7 +1834,12 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } void notifyAppResumed(boolean wasStopped) { - mWindowContainerController.notifyAppResumed(wasStopped); + if (mAppWindowToken == null) { + Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: " + + appToken); + return; + } + mAppWindowToken.notifyAppResumed(wasStopped); } void notifyUnknownVisibilityLaunched() { @@ -1645,7 +1847,10 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // No display activities never add a window, so there is no point in waiting them for // relayout. if (!noDisplay) { - mWindowContainerController.notifyUnknownVisibilityLaunched(); + if (mAppWindowToken != null) { + mAppWindowToken.getDisplayContent().mUnknownAppVisibilityController + .notifyLaunched(mAppWindowToken); + } } } @@ -1857,16 +2062,18 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo stopped = true; setState(STOPPED, "activityStoppedLocked"); - mWindowContainerController.notifyAppStopped(); + if (mAppWindowToken != null) { + mAppWindowToken.notifyAppStopped(); + } if (finishing) { clearOptionsLocked(); } else { if (deferRelaunchUntilPaused) { stack.destroyActivityLocked(this, true /* removeFromApp */, "stop-config"); - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } else { - mStackSupervisor.updatePreviousProcessLocked(this); + mRootActivityContainer.updatePreviousProcess(this); } } } @@ -1918,14 +2125,33 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo public void startFreezingScreenLocked(WindowProcessController app, int configChanges) { if (mayFreezeScreenLocked(app)) { - mWindowContainerController.startFreezingScreen(configChanges); + if (mAppWindowToken == null) { + Slog.w(TAG_WM, + "Attempted to freeze screen with non-existing app token: " + appToken); + return; + } + + if (configChanges == 0 && mAppWindowToken.okToDisplay()) { + if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + appToken); + return; + } + + mAppWindowToken.startFreezingScreen(); } } public void stopFreezingScreenLocked(boolean force) { if (force || frozenBeforeDestroy) { frozenBeforeDestroy = false; - mWindowContainerController.stopFreezingScreen(force); + if (mAppWindowToken == null) { + return; + } + if (DEBUG_ORIENTATION) { + Slog.v(TAG_WM, "Clear freezing of " + appToken + ": hidden=" + + mAppWindowToken.isHidden() + " freezing=" + + mAppWindowToken.isFreezingScreen()); + } + mAppWindowToken.stopFreezingScreen(true, force); } } @@ -1937,7 +2163,10 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo info.windowsFullyDrawnDelayMs); } } - @Override + + /** + * Called when the starting window for this container is drawn. + */ public void onStartingWindowDrawn(long timestamp) { synchronized (service.mGlobalLock) { mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn( @@ -1945,7 +2174,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } } - @Override + /** Called when the windows associated app window container are drawn. */ public void onWindowsDrawn(boolean drawn, long timestamp) { synchronized (service.mGlobalLock) { mDrawn = drawn; @@ -1965,7 +2194,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } } - @Override + /** Called when the windows associated app window container are visible. */ public void onWindowsVisible() { synchronized (service.mGlobalLock) { mStackSupervisor.reportActivityVisibleLocked(this); @@ -1999,7 +2228,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } } - @Override + /** Called when the windows associated app window container are no longer visible. */ public void onWindowsGone() { synchronized (service.mGlobalLock) { if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + this); @@ -2007,7 +2236,14 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } } - @Override + /** + * Called when the key dispatching to a window associated with the app window container + * timed-out. + * + * @param reason The reason for the key dispatching time out. + * @param windowPid The pid of the window key dispatching timed out on. + * @return True if input dispatching should be aborted. + */ public boolean keyDispatchingTimedOut(String reason, int windowPid) { ActivityRecord anrActivity; WindowProcessController anrApp; @@ -2036,7 +2272,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // another activity to start or has stopped, then the key dispatching // timeout should not be caused by this. if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(this) || stopped) { - final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack(); + final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack(); // Try to use the one which is closest to top. ActivityRecord r = stack.getResumedActivity(); if (r == null) { @@ -2183,7 +2419,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch, boolean fromRecents) { - if (mWindowContainerController == null) { + if (mAppWindowToken == null) { return; } if (mTaskOverlay) { @@ -2198,7 +2434,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo final CompatibilityInfo compatInfo = service.compatibilityInfoForPackageLocked(info.applicationInfo); - final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme, + final boolean shown = addStartingWindow(packageName, theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(), allowTaskSnapshot(), @@ -2213,34 +2449,62 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo if (mStartingWindowState == STARTING_WINDOW_SHOWN && behindFullscreenActivity) { if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this); mStartingWindowState = STARTING_WINDOW_REMOVED; - mWindowContainerController.removeStartingWindow(); + mAppWindowToken.removeStartingWindow(); } } int getRequestedOrientation() { - return mWindowContainerController.getOrientation(); + return getOrientation(); } void setRequestedOrientation(int requestedOrientation) { final int displayId = getDisplayId(); final Configuration displayConfig = - mStackSupervisor.getDisplayOverrideConfiguration(displayId); + mRootActivityContainer.getDisplayOverrideConfiguration(displayId); - final Configuration config = mWindowContainerController.setOrientation(requestedOrientation, + final Configuration config = setOrientation(requestedOrientation, displayId, displayConfig, mayFreezeScreenLocked(app)); if (config != null) { frozenBeforeDestroy = true; if (!service.updateDisplayOverrideConfigurationLocked(config, this, false /* deferResume */, displayId)) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } } service.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged( task.taskId, requestedOrientation); } + Configuration setOrientation(int requestedOrientation, int displayId, + Configuration displayConfig, boolean freezeScreenIfNeeded) { + if (mAppWindowToken == null) { + Slog.w(TAG_WM, + "Attempted to set orientation of non-existing app token: " + appToken); + return null; + } + + mAppWindowToken.setOrientation(requestedOrientation); + + final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null; + return service.mWindowManager.updateOrientationFromAppTokens(displayConfig, binder, + displayId); + } + + int getOrientation() { + if (mAppWindowToken == null) { + return SCREEN_ORIENTATION_UNSPECIFIED; + } + + return mAppWindowToken.getOrientationIgnoreVisibility(); + } + void setDisablePreviewScreenshots(boolean disable) { - mWindowContainerController.setDisablePreviewScreenshots(disable); + if (mAppWindowToken == null) { + Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app" + + " token: " + appToken); + return; + } + mAppWindowToken.setDisablePreviewScreenshots(disable); } /** @@ -2288,8 +2552,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo /** Returns true if the configuration is compatible with this activity. */ boolean isConfigurationCompatible(Configuration config) { - final int orientation = mWindowContainerController != null - ? mWindowContainerController.getOrientation() : info.screenOrientation; + final int orientation = mAppWindowToken != null + ? getOrientation() : info.screenOrientation; if (isFixedOrientationPortrait(orientation) && config.orientation != ORIENTATION_PORTRAIT) { return false; @@ -2867,7 +3131,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo void setShowWhenLocked(boolean showWhenLocked) { mShowWhenLocked = showWhenLocked; - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0 /* configChanges */, + mRootActivityContainer.ensureActivitiesVisible(null, 0 /* configChanges */, false /* preserveWindows */); } @@ -2905,7 +3169,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } boolean isTopRunningActivity() { - return mStackSupervisor.topRunningActivityLocked() == this; + return mRootActivityContainer.topRunningActivity() == this; } /** @@ -2918,7 +3182,12 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } void registerRemoteAnimations(RemoteAnimationDefinition definition) { - mWindowContainerController.registerRemoteAnimations(definition); + if (mAppWindowToken == null) { + Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app" + + " token: " + appToken); + return; + } + mAppWindowToken.registerRemoteAnimations(definition); } @Override @@ -2946,8 +3215,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo proto.end(token); } - public void writeToProto(ProtoOutputStream proto, long fieldId) { - final long token = proto.start(fieldId); + /** + * Write all fields to an {@code ActivityRecordProto}. This assumes the + * {@code ActivityRecordProto} is the outer-most proto data. + */ + void writeToProto(ProtoOutputStream proto) { super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */); writeIdentifierToProto(proto, IDENTIFIER); proto.write(STATE, mState.toString()); @@ -2957,6 +3229,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo proto.write(PROC_ID, app.getPid()); } proto.write(TRANSLUCENT, !fullscreen); + } + + public void writeToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + writeToProto(proto); proto.end(token); } } diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index bd3e43c380b6..9fbeaf8cd7bf 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -63,7 +63,6 @@ import static com.android.server.wm.ActivityStack.ActivityState.PAUSING; import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; import static com.android.server.wm.ActivityStack.ActivityState.STOPPED; import static com.android.server.wm.ActivityStack.ActivityState.STOPPING; -import static com.android.server.wm.ActivityStackSupervisor.FindTaskResult; import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS; @@ -103,6 +102,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; +import static com.android.server.wm.RootActivityContainer.FindTaskResult; import static java.lang.Integer.MAX_VALUE; @@ -267,7 +267,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mStackSupervisor.resizeDockedStackLocked( getOverrideBounds(), mTmpRect2, mTmpRect2, null, null, PRESERVE_WINDOWS); } - mStackSupervisor.updateUIDsPresentOnDisplay(); + mRootActivityContainer.updateUIDsPresentOnDisplay(); } enum ActivityState { @@ -390,6 +390,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai /** Run all ActivityStacks through this */ protected final ActivityStackSupervisor mStackSupervisor; + protected final RootActivityContainer mRootActivityContainer; private boolean mTopActivityOccludesKeyguard; private ActivityRecord mTopDismissingKeyguardActivity; @@ -489,6 +490,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai int windowingMode, int activityType, boolean onTop) { mStackSupervisor = supervisor; mService = supervisor.mService; + mRootActivityContainer = mService.mRootActivityContainer; mHandler = new ActivityStackHandler(supervisor.mLooper); mWindowManager = mService.mWindowManager; mStackId = stackId; @@ -508,7 +510,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai T createStackWindowController(int displayId, boolean onTop, Rect outBounds) { return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds, - mStackSupervisor.mWindowManager); + mRootActivityContainer.mWindowManager); } T getWindowContainerController() { @@ -532,7 +534,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_STACK) Slog.v(TAG_STACK, "set resumed activity to:" + record + " reason:" + reason); setResumedActivity(record, reason + " - onActivityStateChanged"); - if (record == mStackSupervisor.getTopResumedActivity()) { + if (record == mRootActivityContainer.getTopResumedActivity()) { // TODO(b/111361570): Support multiple focused apps in WM mService.setResumedActivityUncheckLocked(record, reason); } @@ -622,7 +624,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai display.onStackWindowingModeChanged(this); } if (hasNewOverrideBounds) { - mStackSupervisor.resizeStackLocked(this, mTmpRect2, null, null, PRESERVE_WINDOWS, + mRootActivityContainer.resizeStack(this, mTmpRect2, null, null, PRESERVE_WINDOWS, true /* allowResizeInDockedMode */, true /* deferResume */); } if (prevIsAlwaysOnTop != isAlwaysOnTop()) { @@ -819,8 +821,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } if (!deferEnsuringVisibility) { - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS); - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } } @@ -854,10 +856,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai /** Resume next focusable stack after reparenting to another display. */ void postReparent() { adjustFocusToNextFocusableStack("reparent", true /* allowFocusSelf */); - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); // Update visibility of activities before notifying WM. This way it won't try to resize // windows that are no longer visible. - mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */, + mRootActivityContainer.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, !PRESERVE_WINDOWS); } @@ -882,7 +884,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } ActivityDisplay getDisplay() { - return mStackSupervisor.getActivityDisplay(mDisplayId); + return mRootActivityContainer.getActivityDisplay(mDisplayId); } /** @@ -1034,7 +1036,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } /** - * This is a simplified version of topRunningActivityLocked that provides a number of + * This is a simplified version of topRunningActivity that provides a number of * optional skip-over modes. It is intended for use with the ActivityController hook only. * * @param token If non-null, any history records matching this token will be skipped. @@ -1236,7 +1238,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai boolean isFocusable() { final ActivityRecord r = topRunningActivityLocked(); - return mStackSupervisor.isFocusable(this, r != null && r.isFocusable()); + return mRootActivityContainer.isFocusable(this, r != null && r.isFocusable()); } boolean isFocusableAndVisible() { @@ -1398,7 +1400,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final TaskRecord task = mTaskHistory.get(i); if (task.okToShowLocked()) { - if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUserLocked: stack=" + getStackId() + + if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUser: stack=" + getStackId() + " moving " + task + " to top"); mTaskHistory.remove(i); mTaskHistory.add(task); @@ -1587,7 +1589,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (prev == null) { if (resuming == null) { Slog.wtf(TAG, "Trying to pause when nothing is resumed"); - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } return false; } @@ -1665,7 +1667,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // pause, so just treat it as being paused now. if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next."); if (resuming == null) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } return false; } @@ -1704,7 +1706,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } } } - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); } private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) { @@ -1757,9 +1759,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } if (resumeNext) { - final ActivityStack topStack = mStackSupervisor.getTopDisplayFocusedStack(); + final ActivityStack topStack = mRootActivityContainer.getTopDisplayFocusedStack(); if (!topStack.shouldSleepOrShutDownActivities()) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(topStack, prev, null); + mRootActivityContainer.resumeFocusedStacksTopActivities(topStack, prev, null); } else { checkReadyForSleep(); ActivityRecord top = topStack.topRunningActivityLocked(); @@ -1768,7 +1770,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // something. Also if the top activity on the stack is not the just paused // activity, we need to go ahead and resume it to ensure we complete an // in-flight app switch. - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } } } @@ -1799,7 +1801,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false; } - mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS); } private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) { @@ -2011,7 +2013,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai /** * Ensure visibility with an option to also update the configuration of visible activities. * @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean) - * @see ActivityStackSupervisor#ensureActivitiesVisibleLocked(ActivityRecord, int, boolean) + * @see RootActivityContainer#ensureActivitiesVisible(ActivityRecord, int, boolean) */ // TODO: Should be re-worked based on the fact that each task as a stack in most cases. final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges, @@ -2032,7 +2034,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai boolean aboveTop = top != null; final boolean stackShouldBeVisible = shouldBeVisible(starting); boolean behindFullscreenActivity = !stackShouldBeVisible; - boolean resumeNextActivity = mStackSupervisor.isTopDisplayFocusedStack(this) + boolean resumeNextActivity = mRootActivityContainer.isTopDisplayFocusedStack(this) && (isInStackLocked(starting) == null); final boolean isTopNotPinnedStack = isAttached() && getDisplay().isTopNotPinnedStack(this); @@ -2443,7 +2445,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai * * NOTE: It is not safe to call this method directly as it can cause an activity in a * non-focused stack to be resumed. - * Use {@link ActivityStackSupervisor#resumeFocusedStacksTopActivitiesLocked} to resume the + * Use {@link RootActivityContainer#resumeFocusedStacksTopActivities} to resume the * right activity for the current system state. */ @GuardedBy("mService") @@ -2513,7 +2515,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return false; } - mStackSupervisor.cancelInitializingActivities(); + mRootActivityContainer.cancelInitializingActivities(); // Remember how we'll process this pause/resume situation, and ensure // that the state is reset however we wind up proceeding. @@ -2536,7 +2538,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai executeAppTransition(options); if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Top activity resumed " + next); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return false; } @@ -2544,7 +2545,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // activity is paused, well that is the state we want. if (shouldSleepOrShutDownActivities() && mLastPausedActivity == next - && mStackSupervisor.allPausedActivitiesComplete()) { + && mRootActivityContainer.allPausedActivitiesComplete()) { // If the current top activity may be able to occlude keyguard but the occluded state // has not been set, update visibility and check again if we should continue to resume. boolean nothingToResume = true; @@ -2565,7 +2566,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai executeAppTransition(options); if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Going to sleep and all paused"); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return false; } } @@ -2576,7 +2576,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (!mService.mAmInternal.hasStartedUserState(next.userId)) { Slog.w(TAG, "Skipping resume of top activity " + next + ": user " + next.userId + " is stopped"); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return false; } @@ -2590,10 +2589,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next); // If we are currently pausing an activity, then don't do anything until that is done. - if (!mStackSupervisor.allPausedActivitiesComplete()) { + if (!mRootActivityContainer.allPausedActivitiesComplete()) { if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE, "resumeTopActivityLocked: Skip resume: some activity pausing."); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return false; } @@ -2640,7 +2638,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai next.app.updateProcessInfo(false /* updateServiceConnectionActivities */, true /* updateLru */, true /* activityChange */, false /* updateOomAdj */); } - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); if (lastResumed != null) { lastResumed.setWillCloseOrEnterPip(true); } @@ -2655,7 +2652,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai executeAppTransition(options); if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Top activity resumed (dontWaitForPause) " + next); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return true; } @@ -2673,7 +2669,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (prev != null && prev != next) { if (!mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(prev) - && next != null && !next.nowVisible) { + && !next.nowVisible) { mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(prev); if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming top, waiting visible to hide: " + prev); @@ -2814,7 +2810,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // result of invisible window resize. // TODO: Remove this once visibilities are set correctly immediately when // starting an activity. - notUpdated = !mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId, + notUpdated = !mRootActivityContainer.ensureVisibilityAndConfig(next, mDisplayId, true /* markFrozenIfConfigChanged */, false /* deferResume */); } @@ -2836,7 +2832,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai next.setVisibility(true); } next.completeResumeLocked(); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return true; } @@ -2899,7 +2894,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai false /* taskSwitch */); } mStackSupervisor.startSpecificActivityLocked(next, true, false); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return true; } @@ -2913,7 +2907,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai Slog.w(TAG, "Exception thrown during resume of " + next, e); requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null, "resume-exception", true); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return true; } } else { @@ -2931,7 +2924,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mStackSupervisor.startSpecificActivityLocked(next, true, true); } - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return true; } @@ -2942,7 +2934,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Try to move focus to the next visible stack with a running activity if this // stack is not covering the entire screen or is on a secondary display (with no home // stack). - return mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(nextFocusedStack, prev, + return mRootActivityContainer.resumeFocusedStacksTopActivities(nextFocusedStack, prev, null /* targetOptions */); } @@ -2950,8 +2942,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai ActivityOptions.abort(options); if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityInNextFocusableStack: " + reason + ", go home"); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); - return mStackSupervisor.resumeHomeActivity(prev, reason, mDisplayId); + return mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId); } /** Returns the position the input task should be placed in this stack. */ @@ -3043,7 +3034,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (!startIt) { if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task " + task, new RuntimeException("here").fillInStackTrace()); - r.createWindowContainer(); + r.createAppWindowToken(); ActivityOptions.abort(options); return; } @@ -3073,9 +3064,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // TODO: Need to investigate if it is okay for the controller to already be created by the // time we get to this point. I think it is, but need to double check. // Use test in b/34179495 to trace the call path. - if (r.getWindowContainerController() == null) { - r.createWindowContainer(); + if (r.mAppWindowToken == null) { + r.createAppWindowToken(); } + task.setFrontOfTask(); if (!isHomeOrRecentsStack() || numActivities() > 0) { @@ -3536,7 +3528,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } private void adjustFocusedActivityStack(ActivityRecord r, String reason) { - if (!mStackSupervisor.isTopDisplayFocusedStack(this) || + if (!mRootActivityContainer.isTopDisplayFocusedStack(this) || ((mResumedActivity != r) && (mResumedActivity != null))) { return; } @@ -3545,7 +3537,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final String myReason = reason + " adjustFocus"; if (next == r) { - final ActivityRecord top = mStackSupervisor.topRunningActivityLocked(); + final ActivityRecord top = mRootActivityContainer.topRunningActivity(); if (top != null) { top.moveFocusableActivityToTop(myReason); } @@ -3569,7 +3561,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final ActivityStack nextFocusableStack = adjustFocusToNextFocusableStack(myReason); if (nextFocusableStack != null) { final ActivityRecord top = nextFocusableStack.topRunningActivityLocked(); - if (top != null && top == mStackSupervisor.getTopResumedActivity()) { + if (top != null && top == mRootActivityContainer.getTopResumedActivity()) { // TODO(b/111361570): Remove this and update focused app per-display in // WindowManager every time an activity becomes resumed in // ActivityTaskManagerService#setResumedActivityUncheckLocked(). @@ -3597,7 +3589,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai */ private ActivityStack adjustFocusToNextFocusableStack(String reason, boolean allowFocusSelf) { final ActivityStack stack = - mStackSupervisor.getNextFocusableStackLocked(this, !allowFocusSelf); + mRootActivityContainer.getNextFocusableStack(this, !allowFocusSelf); final String myReason = reason + " adjustFocusToNextFocusableStack"; if (stack == null) { return null; @@ -4018,11 +4010,11 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // stack, need to make something visible in its place. Also if the display does not // have running activity, the configuration may need to be updated for restoring // original orientation of the display. - mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId, + mRootActivityContainer.ensureVisibilityAndConfig(next, mDisplayId, false /* markFrozenIfConfigChanged */, true /* deferResume */); } if (activityRemoved) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, "destroyActivityLocked: finishCurrentActivityLocked r=" + r + @@ -4035,7 +4027,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + r); mStackSupervisor.mFinishingActivities.add(r); r.resumeKeyDispatchingLocked(); - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); return r; } @@ -4377,7 +4369,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } } if (activityRemoved) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } } @@ -4568,7 +4560,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } } - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list, @@ -4712,7 +4704,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai task.mLastTimeMoved *= -1; } } - mStackSupervisor.invalidateTaskLayers(); + mRootActivityContainer.invalidateTaskLayers(); } final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, ActivityOptions options, @@ -4788,7 +4780,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai topActivity.supportsEnterPipOnTaskSwitch = true; } - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId); mService.getTaskChangeNotificationController().notifyTaskMovedToFront(tr.taskId); @@ -4860,7 +4852,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return true; } - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); return true; } @@ -4907,7 +4899,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (updatedConfig) { // Ensure the resumed state of the focus activity if we updated the configuration of // any activity. - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } } @@ -5099,7 +5091,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai */ void getRunningTasks(List<TaskRecord> tasksOut, @ActivityType int ignoreActivityType, @WindowingMode int ignoreWindowingMode, int callingUid, boolean allowed) { - boolean focusedStack = mStackSupervisor.getTopDisplayFocusedStack() == this; + boolean focusedStack = mRootActivityContainer.getTopDisplayFocusedStack() == this; boolean topTask = true; for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord task = mTaskHistory.get(taskNdx); @@ -5164,7 +5156,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return removeHistoryRecordsForAppLocked(app); } - void handleAppCrashLocked(WindowProcessController app) { + void handleAppCrash(WindowProcessController app) { for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { @@ -5311,7 +5303,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // We only need to adjust focused stack if this stack is in focus and we are not in the // process of moving the task to the top of the stack that will be focused. if (mode != REMOVE_TASK_MODE_MOVING_TO_TOP - && mStackSupervisor.isTopDisplayFocusedStack(this)) { + && mRootActivityContainer.isTopDisplayFocusedStack(this)) { String myReason = reason + " leftTaskHistoryEmpty"; if (!inMultiWindowMode() || adjustFocusToNextFocusableStack(myReason) == null) { getDisplay().moveHomeStackToFront(myReason); @@ -5417,7 +5409,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // The task might have already been running and its visibility needs to be synchronized with // the visibility of the stack / windows. ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } private ActivityStack preAddTask(TaskRecord task, String reason, boolean toTop) { @@ -5484,7 +5476,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai moveToFront(reason); // If the original state is resumed, there is no state change to update focused app. // So here makes sure the activity focus is set if it is the top. - if (origState == RESUMED && r == mStackSupervisor.getTopResumedActivity()) { + if (origState == RESUMED && r == mRootActivityContainer.getTopResumedActivity()) { // TODO(b/111361570): Support multiple focused apps in WM mService.setResumedActivityUncheckLocked(r, reason); } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index c517bd70cbf5..987c706b0c4e 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -25,28 +25,18 @@ import static android.app.ActivityManager.START_FLAG_DEBUG; import static android.app.ActivityManager.START_FLAG_NATIVE_DEBUGGING; import static android.app.ActivityManager.START_FLAG_TRACK_ALLOCATION; import static android.app.ActivityManager.START_TASK_TO_FRONT; -import static android.app.ActivityTaskManager.INVALID_STACK_ID; -import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN; import static android.app.WaitResult.INVALID_DELAY; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; -import static android.app.WindowConfiguration.activityTypeToString; -import static android.app.WindowConfiguration.windowingModeToString; -import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; -import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; -import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; import static android.content.pm.PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY; import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -59,26 +49,13 @@ import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.TYPE_VIRTUAL; import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS; -import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER; -import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS; -import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID; -import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT; -import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER; -import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES; -import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY; -import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED; -import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING; import static com.android.server.wm.ActivityStack.ActivityState.PAUSED; import static com.android.server.wm.ActivityStack.ActivityState.PAUSING; -import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; -import static com.android.server.wm.ActivityStack.ActivityState.STOPPED; -import static com.android.server.wm.ActivityStack.ActivityState.STOPPING; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IDLE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; @@ -86,9 +63,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_IDLE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; @@ -96,6 +71,10 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_STACK_MSG; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; +import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_ONLY; +import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; +import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE; +import static com.android.server.wm.RootActivityContainer.TAG_STATES; import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE; import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_WHITELISTED; @@ -103,20 +82,11 @@ import static com.android.server.wm.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE; import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT; -import static java.lang.Integer.MAX_VALUE; - import android.Manifest; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; -import android.app.ActivityManager.RunningTaskInfo; -import android.app.ActivityManager.StackInfo; import android.app.ActivityManagerInternal; import android.app.ActivityOptions; -import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.ProfilerInfo; import android.app.ResultInfo; @@ -139,17 +109,11 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.Rect; -import android.hardware.display.DisplayManager; -import android.hardware.display.DisplayManager.DisplayListener; -import android.hardware.display.DisplayManagerInternal; -import android.hardware.power.V1_0.PowerHint; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Debug; -import android.os.FactoryTest; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -163,20 +127,13 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.WorkSource; import android.provider.MediaStore; -import android.service.voice.IVoiceInteractionSession; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.DisplayMetrics; import android.util.EventLog; -import android.util.IntArray; import android.util.MergedConfiguration; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; -import android.util.TimeUtils; -import android.util.proto.ProtoOutputStream; -import android.view.Display; -import android.view.DisplayInfo; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -185,34 +142,28 @@ import com.android.internal.os.TransferPipe; import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.internal.util.ArrayUtils; import com.android.internal.util.function.pooled.PooledLambda; -import com.android.server.LocalServices; import com.android.server.am.ActivityManagerService; -import com.android.server.am.AppTimeTracker; import com.android.server.am.EventLogTags; import com.android.server.am.UserState; -import com.android.server.wm.ActivityStack.ActivityState; -import com.android.server.wm.ActivityTaskManagerInternal.SleepToken; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; -import java.util.Set; -public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener, - RecentTasks.Callbacks, RootWindowContainerListener { +// TODO: This class has become a dumping ground. Let's +// - Move things relating to the hierarchy to RootWindowContainer +// - Move things relating to activity life cycles to maybe a new class called ActivityLifeCycler +// - Move interface things to ActivityTaskManagerService. +// - All other little things to other files. +public class ActivityStackSupervisor implements RecentTasks.Callbacks { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_ATM; private static final String TAG_IDLE = TAG + POSTFIX_IDLE; private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE; private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS; - private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE; private static final String TAG_STACK = TAG + POSTFIX_STACK; private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH; - static final String TAG_STATES = TAG + POSTFIX_STATES; static final String TAG_TASKS = TAG + POSTFIX_TASKS; /** How long we wait until giving up on the last activity telling us it is idle. */ @@ -233,12 +184,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14; static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15; - private static final String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay"; - - // Used to indicate if an object (e.g. stack) that we are trying to get - // should be created if it doesn't exist already. - static final boolean CREATE_IF_NEEDED = true; - // Used to indicate that windows of activities should be preserved during the resize. static final boolean PRESERVE_WINDOWS = true; @@ -270,25 +215,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D private Rect mPendingTempOtherTaskBounds; private Rect mPendingTempOtherTaskInsetBounds; - /** - * The modes which affect which tasks are returned when calling - * {@link ActivityStackSupervisor#anyTaskForIdLocked(int)}. - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - MATCH_TASK_IN_STACKS_ONLY, - MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, - MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE - }) - public @interface AnyTaskForIdMatchTaskMode {} - // Match only tasks in the current stacks - static final int MATCH_TASK_IN_STACKS_ONLY = 0; - // Match either tasks in the current stacks, or in the recent tasks if not found in the stacks - static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS = 1; - // Match either tasks in the current stacks, or in the recent tasks, restoring it to the - // provided stack id - static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE = 2; - // Activity actions an app cannot start if it uses a permission which is not granted. private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION = new ArrayMap<>(); @@ -316,19 +242,19 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE; final ActivityTaskManagerService mService; + RootActivityContainer mRootActivityContainer; /** The historial list of recent tasks including inactive tasks */ RecentTasks mRecentTasks; /** Helper class to abstract out logic for fetching the set of currently running tasks */ - private RunningTasks mRunningTasks; + RunningTasks mRunningTasks; final ActivityStackSupervisorHandler mHandler; final Looper mLooper; /** Short cut */ WindowManagerService mWindowManager; - DisplayManager mDisplayManager; /** Common synchronization logic used to save things to disks. */ PersisterQueue mPersisterQueue; @@ -341,9 +267,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D */ private final SparseIntArray mCurTaskIdForUser = new SparseIntArray(20); - /** The current user */ - int mCurrentUser; - /** List of activities that are waiting for a new activity to become visible before completing * whatever operation they are supposed to do. */ // TODO: Remove mActivitiesWaitingForVisibleActivity list and just remove activity from @@ -392,9 +315,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D * is being brought in front of us. */ boolean mUserLeaving = false; - /** Set when a power hint has started, but not ended. */ - private boolean mPowerHintSent; - /** * We don't want to allow the device to go to sleep while in the process * of launching an activity. This is primarily to allow alarm intent @@ -410,29 +330,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D */ PowerManager.WakeLock mGoingToSleep; - /** - * A list of tokens that cause the top activity to be put to sleep. - * They are used by components that may hide and block interaction with underlying - * activities. - */ - final ArrayList<SleepToken> mSleepTokens = new ArrayList<>(); - - /** Stack id of the front stack when user switched, indexed by userId. */ - SparseIntArray mUserStackInFront = new SparseIntArray(2); - - /** Reference to default display so we can quickly look it up. */ - private ActivityDisplay mDefaultDisplay; - - /** - * List of displays which contain activities, sorted by z-order. - * The last entry in the list is the topmost. - */ - private final ArrayList<ActivityDisplay> mActivityDisplays = new ArrayList<>(); - - private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>(); - - private DisplayManagerInternal mDisplayManagerInternal; - /** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */ boolean inResumeTopActivity; @@ -443,50 +340,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D private final Rect tempRect = new Rect(); private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic(); - // The default minimal size that will be used if the activity doesn't specify its minimal size. - // It will be calculated when the default display gets added. - int mDefaultMinSizeOfResizeableTaskDp = -1; - - // Whether tasks have moved and we need to rank the tasks before next OOM scoring - private boolean mTaskLayersChanged = true; - private ActivityMetricsLogger mActivityMetricsLogger; - private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>(); - - @Override - protected int getChildCount() { - return mActivityDisplays.size(); - } - - @Override - protected ActivityDisplay getChildAt(int index) { - return mActivityDisplays.get(index); - } - - @Override - protected ConfigurationContainer getParent() { - return null; - } - - Configuration getDisplayOverrideConfiguration(int displayId) { - final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId); - if (activityDisplay == null) { - throw new IllegalArgumentException("No display found with id: " + displayId); - } - - return activityDisplay.getOverrideConfiguration(); - } - - void setDisplayOverrideConfiguration(Configuration overrideConfiguration, int displayId) { - final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId); - if (activityDisplay == null) { - throw new IllegalArgumentException("No display found with id: " + displayId); - } - - activityDisplay.onOverrideConfigurationChanged(overrideConfiguration); - } - /** Check if placing task or activity on specified display is allowed. */ boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid, ActivityInfo activityInfo) { @@ -508,44 +363,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } /** - * Check if configuration of specified display matches current global config. - * Used to check if we can put a non-resizeable activity on a secondary display and it will get - * the same config as on the default display. - * @param displayId Id of the display to check. - * @return {@code true} if configuration matches. - */ - private boolean displayConfigMatchesGlobal(int displayId) { - if (displayId == DEFAULT_DISPLAY) { - return true; - } - if (displayId == INVALID_DISPLAY) { - return false; - } - final ActivityDisplay targetDisplay = getActivityDisplayOrCreateLocked(displayId); - if (targetDisplay == null) { - throw new IllegalArgumentException("No display found with id: " + displayId); - } - return getConfiguration().equals(targetDisplay.getConfiguration()); - } - - static class FindTaskResult { - ActivityRecord mRecord; - boolean mIdealMatch; - - void clear() { - mRecord = null; - mIdealMatch = false; - } - - void setTo(FindTaskResult result) { - mRecord = result.mRecord; - mIdealMatch = result.mIdealMatch; - } - } - - private final FindTaskResult mTmpFindTaskResult = new FindTaskResult(); - - /** * Used to keep track whether app visibilities got changed since the last pause. Useful to * determine whether to invoke the task stack change listener after pausing. */ @@ -565,11 +382,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D */ private boolean mAllowDockedStackResize = true; - /** - * Is dock currently minimized. - */ - boolean mIsDockMinimized; - private KeyguardController mKeyguardController; private PowerManager mPowerManager; @@ -577,8 +389,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D private boolean mInitialized; - private RootWindowContainerController mWindowContainerController; - /** * Description of a request to start a new activity, which has been held * due to app switches being disabled. @@ -617,11 +427,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mHandler = new ActivityStackSupervisorHandler(looper); } - @VisibleForTesting - void setWindowContainerController(RootWindowContainerController controller) { - mWindowContainerController = controller; - } - public void initialize() { if (mInitialized) { return; @@ -629,7 +434,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mInitialized = true; mRunningTasks = createRunningTasks(); - mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext, mHandler.getLooper()); + + mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext, + mHandler.getLooper()); mKeyguardController = new KeyguardController(mService, this); mPersisterQueue = new PersisterQueue(); @@ -676,321 +483,16 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D void setWindowManager(WindowManagerService wm) { mWindowManager = wm; getKeyguardController().setWindowManager(wm); - setWindowContainerController(new RootWindowContainerController(this)); - - mDisplayManager = mService.mContext.getSystemService(DisplayManager.class); - mDisplayManager.registerDisplayListener(this, mHandler); - mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); - - final Display[] displays = mDisplayManager.getDisplays(); - for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) { - final Display display = displays[displayNdx]; - final ActivityDisplay activityDisplay = new ActivityDisplay(this, display); - if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) { - mDefaultDisplay = activityDisplay; - } - addChild(activityDisplay, ActivityDisplay.POSITION_TOP); - } - calculateDefaultMinimalSizeOfResizeableTasks(); - - final ActivityDisplay defaultDisplay = getDefaultDisplay(); - - defaultDisplay.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); - positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP); - } - - /** Change the z-order of the given display. */ - private void positionChildAt(ActivityDisplay display, int position) { - if (position >= mActivityDisplays.size()) { - position = mActivityDisplays.size() - 1; - } else if (position < 0) { - position = 0; - } - - if (mActivityDisplays.isEmpty()) { - mActivityDisplays.add(display); - } else if (mActivityDisplays.get(position) != display) { - mActivityDisplays.remove(display); - mActivityDisplays.add(position, display); - } - } - - @Override - public void onChildPositionChanged(DisplayWindowController childController, int position) { - // Assume AM lock is held from positionChildAt of controller in each hierarchy. - final ActivityDisplay display = getActivityDisplay(childController.getDisplayId()); - if (display != null) { - positionChildAt(display, position); - } - } - - ActivityStack getTopDisplayFocusedStack() { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityStack focusedStack = mActivityDisplays.get(i).getFocusedStack(); - if (focusedStack != null) { - return focusedStack; - } - } - return null; - } - - ActivityRecord getTopResumedActivity() { - final ActivityStack focusedStack = getTopDisplayFocusedStack(); - if (focusedStack == null) { - return null; - } - final ActivityRecord resumedActivity = focusedStack.getResumedActivity(); - if (resumedActivity != null && resumedActivity.app != null) { - return resumedActivity; - } - // The top focused stack might not have a resumed activity yet - look on all displays in - // focus order. - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityDisplay display = mActivityDisplays.get(i); - final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity(); - if (resumedActivityOnDisplay != null) { - return resumedActivityOnDisplay; - } - } - return null; - } - - boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) { - if (container.inSplitScreenPrimaryWindowingMode() && mIsDockMinimized) { - return false; - } - - return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable; - } - - boolean isTopDisplayFocusedStack(ActivityStack stack) { - return stack != null && stack == getTopDisplayFocusedStack(); } void moveRecentsStackToFront(String reason) { - final ActivityStack recentsStack = getDefaultDisplay().getStack( + final ActivityStack recentsStack = mRootActivityContainer.getDefaultDisplay().getStack( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS); if (recentsStack != null) { recentsStack.moveToFront(reason); } } - boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) { - if (!mService.isBooting() && !mService.isBooted()) { - // Not ready yet! - return false; - } - - if (displayId == INVALID_DISPLAY) { - displayId = DEFAULT_DISPLAY; - } - - final ActivityRecord r = getActivityDisplay(displayId).getHomeActivity(); - final String myReason = reason + " resumeHomeActivity"; - - // Only resume home activity if isn't finishing. - if (r != null && !r.finishing) { - r.moveFocusableActivityToTop(myReason); - return resumeFocusedStacksTopActivitiesLocked(r.getStack(), prev, null); - } - return startHomeOnDisplay(mCurrentUser, myReason, displayId); - } - - /** - * Check if home activity start should be allowed on a display. - * @param homeInfo {@code ActivityInfo} of the home activity that is going to be launched. - * @param displayId The id of the target display. - * @param allowInstrumenting Whether launching home should be allowed if being instrumented. - * @return {@code true} if allow to launch, {@code false} otherwise. - */ - boolean canStartHomeOnDisplay(ActivityInfo homeInfo, int displayId, - boolean allowInstrumenting) { - if (mService.mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL - && mService.mTopAction == null) { - // We are running in factory test mode, but unable to find the factory test app, so - // just sit around displaying the error message and don't try to start anything. - return false; - } - - final WindowProcessController app = - mService.getProcessController(homeInfo.processName, homeInfo.applicationInfo.uid); - if (!allowInstrumenting && app != null && app.isInstrumenting()) { - // Don't do this if the home app is currently being instrumented. - return false; - } - - if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY - && displayId == mService.mVr2dDisplayId)) { - // No restrictions to default display or vr 2d display. - return true; - } - - final ActivityDisplay display = getActivityDisplay(displayId); - if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) { - // Can't launch home on display that doesn't support system decorations. - return false; - } - - final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK - && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE - && homeInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q; - if (!supportMultipleInstance) { - // Can't launch home on other displays if it requested to be single instance. Also we - // don't allow home applications that target before Q to have multiple home activity - // instances because they may not be expected to have multiple home scenario and - // haven't explicitly request for single instance. - return false; - } - - return true; - } - - TaskRecord anyTaskForIdLocked(int id) { - return anyTaskForIdLocked(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE); - } - - TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode) { - return anyTaskForIdLocked(id, matchMode, null, !ON_TOP); - } - - /** - * Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise. - * @param id Id of the task we would like returned. - * @param matchMode The mode to match the given task id in. - * @param aOptions The activity options to use for restoration. Can be null. - * @param onTop If the stack for the task should be the topmost on the display. - */ - TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode, - @Nullable ActivityOptions aOptions, boolean onTop) { - // If options are set, ensure that we are attempting to actually restore a task - if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) { - throw new IllegalArgumentException("Should not specify activity options for non-restore" - + " lookup"); - } - - int numDisplays = mActivityDisplays.size(); - for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - final TaskRecord task = stack.taskForIdLocked(id); - if (task == null) { - continue; - } - if (aOptions != null) { - // Resolve the stack the task should be placed in now based on options - // and reparent if needed. - final ActivityStack launchStack = getLaunchStack(null, aOptions, task, onTop); - if (launchStack != null && stack != launchStack) { - final int reparentMode = onTop - ? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE; - task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME, - "anyTaskForIdLocked"); - } - } - return task; - } - } - - // If we are matching stack tasks only, return now - if (matchMode == MATCH_TASK_IN_STACKS_ONLY) { - return null; - } - - // Otherwise, check the recent tasks and return if we find it there and we are not restoring - // the task from recents - if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents"); - final TaskRecord task = mRecentTasks.getTask(id); - - if (task == null) { - if (DEBUG_RECENTS) { - Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents"); - } - - return null; - } - - if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) { - return task; - } - - // Implicitly, this case is MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE - if (!restoreRecentTaskLocked(task, aOptions, onTop)) { - if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, - "Couldn't restore task id=" + id + " found in recents"); - return null; - } - if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Restored task id=" + id + " from in recents"); - return task; - } - - ActivityRecord isInAnyStackLocked(IBinder token) { - int numDisplays = mActivityDisplays.size(); - for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - final ActivityRecord r = stack.isInStackLocked(token); - if (r != null) { - return r; - } - } - } - return null; - } - - /** - * Detects whether we should show a lock screen in front of this task for a locked user. - * <p> - * We'll do this if either of the following holds: - * <ul> - * <li>The top activity explicitly belongs to {@param userId}.</li> - * <li>The top activity returns a result to an activity belonging to {@param userId}.</li> - * </ul> - * - * @return {@code true} if the top activity looks like it belongs to {@param userId}. - */ - private boolean taskTopActivityIsUser(TaskRecord task, @UserIdInt int userId) { - // To handle the case that work app is in the task but just is not the top one. - final ActivityRecord activityRecord = task.getTopActivity(); - final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null); - - return (activityRecord != null && activityRecord.userId == userId) - || (resultTo != null && resultTo.userId == userId); - } - - /** - * Find all visible task stacks containing {@param userId} and intercept them with an activity - * to block out the contents and possibly start a credential-confirming intent. - * - * @param userId user handle for the locked managed profile. - */ - void lockAllProfileTasks(@UserIdInt int userId) { - mWindowManager.deferSurfaceLayout(); - try { - 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); - final List<TaskRecord> tasks = stack.getAllTasks(); - for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) { - final TaskRecord task = tasks.get(taskNdx); - - // Check the task for a top activity belonging to userId, or returning a - // result to an activity belonging to userId. Example case: a document - // picker for personal files, opened by a work app, should still get locked. - if (taskTopActivityIsUser(task, userId)) { - mService.getTaskChangeNotificationController().notifyTaskProfileLocked( - task.taskId, userId); - } - } - } - } - } finally { - mWindowManager.continueSurfaceLayout(); - } - } - void setNextTaskIdForUserLocked(int taskId, int userId) { final int currentTaskId = mCurTaskIdForUser.get(userId, -1); if (taskId > currentTaskId) { @@ -1014,7 +516,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on. int candidateTaskId = nextTaskIdForUser(currentTaskId, userId); while (mRecentTasks.containsTaskId(candidateTaskId, userId) - || anyTaskForIdLocked( + || mRootActivityContainer.anyTaskForId( candidateTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) { candidateTaskId = nextTaskIdForUser(candidateTaskId, userId); if (candidateTaskId == currentTaskId) { @@ -1029,142 +531,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return candidateTaskId; } - boolean attachApplicationLocked(WindowProcessController app) throws RemoteException { - final String processName = app.mName; - boolean didSomething = false; - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - final ActivityStack stack = display.getFocusedStack(); - if (stack != null) { - stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList); - final ActivityRecord top = stack.topRunningActivityLocked(); - final int size = mTmpActivityList.size(); - for (int i = 0; i < size; i++) { - final ActivityRecord activity = mTmpActivityList.get(i); - if (activity.app == null && app.mUid == activity.info.applicationInfo.uid - && processName.equals(activity.processName)) { - try { - if (realStartActivityLocked(activity, app, - top == activity /* andResume */, true /* checkConfig */)) { - didSomething = true; - } - } catch (RemoteException e) { - Slog.w(TAG, "Exception in new application when starting activity " - + top.intent.getComponent().flattenToShortString(), e); - throw e; - } - } - } - } - } - if (!didSomething) { - ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); - } - return didSomething; - } - - boolean allResumedActivitiesIdle() { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - // TODO(b/117135575): Check resumed activities on all visible stacks. - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - if (display.isSleeping()) { - // No resumed activities while display is sleeping. - continue; - } - - // If the focused stack is not null or not empty, there should have some activities - // resuming or resumed. Make sure these activities are idle. - final ActivityStack stack = display.getFocusedStack(); - if (stack == null || stack.numActivities() == 0) { - continue; - } - final ActivityRecord resumedActivity = stack.getResumedActivity(); - if (resumedActivity == null || !resumedActivity.idle) { - if (DEBUG_STATES) { - Slog.d(TAG_STATES, "allResumedActivitiesIdle: stack=" - + stack.mStackId + " " + resumedActivity + " not idle"); - } - return false; - } - } - // Send launch end powerhint when idle - sendPowerHintForLaunchEndIfNeeded(); - return true; - } - - private boolean allResumedActivitiesVisible() { - boolean foundResumed = 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); - final ActivityRecord r = stack.getResumedActivity(); - if (r != null) { - if (!r.nowVisible || mActivitiesWaitingForVisibleActivity.contains(r)) { - return false; - } - foundResumed = true; - } - } - } - return foundResumed; - } - - private void executeAppTransitionForAllDisplay() { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - display.getWindowContainerController().executeAppTransition(); - } - } - - /** - * Pause all activities in either all of the stacks or just the back stacks. - * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving(). - * @param resuming The resuming activity. - * @param dontWait The resuming activity isn't going to wait for all activities to be paused - * before resuming. - * @return true if any activity was paused as a result of this call. - */ - boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) { - boolean someActivityPaused = false; - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - someActivityPaused |= mActivityDisplays.get(displayNdx) - .pauseBackStacks(userLeaving, resuming, dontWait); - } - return someActivityPaused; - } - - boolean allPausedActivitiesComplete() { - boolean pausing = true; - 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); - final ActivityRecord r = stack.mPausingActivity; - if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) { - if (DEBUG_STATES) { - Slog.d(TAG_STATES, - "allPausedActivitiesComplete: r=" + r + " state=" + r.getState()); - pausing = false; - } else { - return false; - } - } - } - } - return pausing; - } - - void cancelInitializingActivities() { - 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); - stack.cancelInitializingActivities(); - } - } - } - void waitActivityVisible(ComponentName name, WaitResult result, long startTimeMs) { final WaitInfo waitInfo = new WaitInfo(name, result, startTimeMs); mWaitingForActivityVisible.add(waitInfo); @@ -1255,24 +621,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - ActivityRecord topRunningActivityLocked() { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity(); - if (topActivity != null) { - return topActivity; - } - } - return null; - } - - @VisibleForTesting - void getRunningTasks(int maxNum, List<RunningTaskInfo> list, - @ActivityType int ignoreActivityType, @WindowingMode int ignoreWindowingMode, - int callingUid, boolean allowed) { - mRunningTasks.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, - mActivityDisplays, callingUid, allowed); - } - ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags, ProfilerInfo profilerInfo) { final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null; @@ -1352,10 +700,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return resolveActivity(intent, rInfo, startFlags, profilerInfo); } - private boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc, + boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc, boolean andResume, boolean checkConfig) throws RemoteException { - if (!allPausedActivitiesComplete()) { + if (!mRootActivityContainer.allPausedActivitiesComplete()) { // While there are activities pausing we skipping starting any new activities until // pauses are complete. NOTE: that we also do this for activities that are starting in // the paused state because they will first be resumed then paused on the client side. @@ -1390,7 +738,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Deferring resume here because we're going to launch new activity shortly. // We don't want to perform a redundant launch of the same record while ensuring // configurations and trying to resume top activity of focused stack. - ensureVisibilityAndConfig(r, r.getDisplayId(), + mRootActivityContainer.ensureVisibilityAndConfig(r, r.getDisplayId(), false /* markFrozenIfConfigChanged */, true /* deferResume */); } @@ -1560,7 +908,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // launching the initial activity (that is, home), so that it can have // a chance to initialize itself while in the background, making the // switch back to it faster and look better. - if (isTopDisplayFocusedStack(stack)) { + if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) { mService.getActivityStartController().startSetupActivity(); } @@ -1573,47 +921,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return true; } - /** - * Ensure all activities visibility, update orientation and configuration. - * - * @param starting The currently starting activity or {@code null} if there is none. - * @param displayId The id of the display where operation is executed. - * @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to - * {@code true} if config changed. - * @param deferResume Whether to defer resume while updating config. - * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched - * because of configuration update. - */ - boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId, - boolean markFrozenIfConfigChanged, boolean deferResume) { - // First ensure visibility without updating the config just yet. We need this to know what - // activities are affecting configuration now. - // Passing null here for 'starting' param value, so that visibility of actual starting - // activity will be properly updated. - ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */, - false /* preserveWindows */, false /* notifyClients */); - - if (displayId == INVALID_DISPLAY) { - // The caller didn't provide a valid display id, skip updating config. - return true; - } - - // Force-update the orientation from the WindowManager, since we need the true configuration - // to send to the client now. - final Configuration config = mWindowManager.updateOrientationFromAppTokens( - getDisplayOverrideConfiguration(displayId), - starting != null && starting.mayFreezeScreenLocked(starting.app) - ? starting.appToken : null, - displayId, true /* forceUpdate */); - if (starting != null && markFrozenIfConfigChanged && config != null) { - starting.frozenBeforeDestroy = true; - } - - // Update the configuration of the activities on the display. - return mService.updateDisplayOverrideConfigurationLocked(config, starting, deferResume, - displayId); - } - private void logIfTransactionTooLarge(Intent intent, Bundle icicle) { int extrasSize = 0; if (intent != null) { @@ -1669,47 +976,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mService.mH.sendMessage(msg); } - void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) { - boolean sendHint = forceSend; - - if (!sendHint) { - // Send power hint if we don't know what we're launching yet - sendHint = targetActivity == null || targetActivity.app == null; - } - - if (!sendHint) { // targetActivity != null - // Send power hint when the activity's process is different than the current resumed - // activity on all displays, or if there are no resumed activities in the system. - boolean noResumedActivities = true; - boolean allFocusedProcessesDiffer = true; - for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) { - final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx); - final ActivityRecord resumedActivity = activityDisplay.getResumedActivity(); - final WindowProcessController resumedActivityProcess = - resumedActivity == null ? null : resumedActivity.app; - - noResumedActivities &= resumedActivityProcess == null; - if (resumedActivityProcess != null) { - allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app); - } - } - sendHint = noResumedActivities || allFocusedProcessesDiffer; - } - - if (sendHint && mService.mPowerManagerInternal != null) { - mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 1); - mPowerHintSent = true; - } - } - - void sendPowerHintForLaunchEndIfNeeded() { - // Trigger launch power hint if activity is launched - if (mPowerHintSent && mService.mPowerManagerInternal != null) { - mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 0); - mPowerHintSent = false; - } - } - boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho, int requestCode, int callingPid, int callingUid, String callingPackage, boolean ignoreTargetSecurity, boolean launchingInTask, @@ -1788,7 +1054,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return true; } - final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(launchDisplayId); + final ActivityDisplay activityDisplay = + mRootActivityContainer.getActivityDisplayOrCreate(launchDisplayId); if (activityDisplay == null || activityDisplay.isRemoved()) { Slog.w(TAG, "Launch on display check: display not found"); return false; @@ -1850,21 +1117,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return false; } - /** Update lists of UIDs that are present on displays and have access to them. */ - void updateUIDsPresentOnDisplay() { - mDisplayAccessUIDs.clear(); - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx); - // Only bother calculating the whitelist for private displays - if (activityDisplay.isPrivate()) { - mDisplayAccessUIDs.append( - activityDisplay.mDisplayId, activityDisplay.getPresentUIDs()); - } - } - // Store updated lists in DisplayManager. Callers from outside of AM should get them there. - mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs); - } - UserInfo getUserInfo(int userId) { final long identity = Binder.clearCallingIdentity(); try { @@ -2016,7 +1268,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Check if able to finish booting when device is booting and all resumed activities // are idle. - if ((mService.isBooting() && allResumedActivitiesIdle()) || fromTimeout) { + if ((mService.isBooting() && mRootActivityContainer.allResumedActivitiesIdle()) + || fromTimeout) { booting = checkFinishBootingLocked(); } @@ -2025,7 +1278,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D r.mRelaunchReason = RELAUNCH_REASON_NONE; } - if (allResumedActivitiesIdle()) { + if (mRootActivityContainer.allResumedActivitiesIdle()) { if (r != null) { mService.scheduleAppGcsLocked(); } @@ -2038,7 +1291,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } mLaunchingActivity.release(); } - ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); } // Atomically retrieve all of the other things to do. @@ -2094,186 +1347,13 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D //mWindowManager.dump(); if (activityRemoved) { - resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } return r; } - boolean handleAppDiedLocked(WindowProcessController app) { - boolean hasVisibleActivities = 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); - hasVisibleActivities |= stack.handleAppDiedLocked(app); - } - } - return hasVisibleActivities; - } - - void closeSystemDialogsLocked() { - 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); - stack.closeSystemDialogsLocked(); - } - } - } - - void removeUserLocked(int userId) { - mUserStackInFront.delete(userId); - } - - /** - * Update the last used stack id for non-current user (current user's last - * used stack is the focused stack) - */ - void updateUserStackLocked(int userId, ActivityStack stack) { - if (userId != mCurrentUser) { - mUserStackInFront.put(userId, stack != null ? stack.getStackId() - : getDefaultDisplay().getHomeStack().mStackId); - } - } - - /** - * @return true if some activity was finished (or would have finished if doit were true). - */ - boolean finishDisabledPackageActivitiesLocked(String packageName, Set<String> filterByClasses, - boolean doit, boolean evenPersistent, int userId) { - 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 (stack.finishDisabledPackageActivitiesLocked( - packageName, filterByClasses, doit, evenPersistent, userId)) { - didSomething = true; - } - } - } - return didSomething; - } - - void updatePreviousProcessLocked(ActivityRecord r) { - // Now that this process has stopped, we may want to consider - // it to be the previous app to try to keep around in case - // the user wants to return to it. - - // First, found out what is currently the foreground app, so that - // we don't blow away the previous app if this activity is being - // hosted by the process that is actually still the foreground. - WindowProcessController fgApp = null; - 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)) { - final ActivityRecord resumedActivity = stack.getResumedActivity(); - if (resumedActivity != null) { - fgApp = resumedActivity.app; - } else if (stack.mPausingActivity != null) { - fgApp = stack.mPausingActivity.app; - } - break; - } - } - } - - // Now set this one as the previous process, only if that really - // makes sense to. - if (r.hasProcess() && fgApp != null && r.app != fgApp - && r.lastVisibleTime > mService.mPreviousProcessVisibleTime - && r.app != mService.mHomeProcess) { - mService.mPreviousProcess = r.app; - mService.mPreviousProcessVisibleTime = r.lastVisibleTime; - } - } - - boolean resumeFocusedStacksTopActivitiesLocked() { - return resumeFocusedStacksTopActivitiesLocked(null, null, null); - } - - boolean resumeFocusedStacksTopActivitiesLocked( - ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) { - - if (!readyToResume()) { - return false; - } - - if (targetStack != null && (targetStack.isTopStackOnDisplay() - || getTopDisplayFocusedStack() == targetStack)) { - return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions); - } - - // Resume all top activities in focused stacks on all displays. - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - final ActivityStack focusedStack = display.getFocusedStack(); - if (focusedStack == null) { - continue; - } - final ActivityRecord r = focusedStack.topRunningActivityLocked(); - if (r == null || !r.isState(RESUMED)) { - focusedStack.resumeTopActivityUncheckedLocked(null, null); - } else if (r.isState(RESUMED)) { - // Kick off any lingering app transitions form the MoveTaskToFront operation. - focusedStack.executeAppTransition(targetOptions); - } - } - - return false; - } - - void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) { - 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); - stack.updateActivityApplicationInfoLocked(aInfo); - } - } - } - - /** - * Finish the topmost activities in all stacks that belong to the crashed app. - * @param app The app that crashed. - * @param reason Reason to perform this action. - * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished. - */ - int finishTopCrashedActivitiesLocked(WindowProcessController app, String reason) { - TaskRecord finishedTask = null; - ActivityStack focusedStack = getTopDisplayFocusedStack(); - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - // It is possible that request to finish activity might also remove its task and stack, - // so we need to be careful with indexes in the loop and check child count every time. - for (int stackNdx = 0; stackNdx < display.getChildCount(); ++stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - final TaskRecord t = stack.finishTopCrashedActivityLocked(app, reason); - if (stack == focusedStack || finishedTask == null) { - finishedTask = t; - } - } - } - return finishedTask != null ? finishedTask.taskId : INVALID_TASK_ID; - } - - void finishVoiceTask(IVoiceInteractionSession session) { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - final int numStacks = display.getChildCount(); - for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - stack.finishVoiceTask(session); - } - } - } - - /** - * This doesn't just find a task, it also moves the task to front. - */ + /** This doesn't just find a task, it also moves the task to front. */ void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, String reason, boolean forceNonResizeable) { ActivityStack currentStack = task.getStack(); @@ -2293,7 +1373,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D final Rect bounds = options.getLaunchBounds(); task.updateOverrideConfiguration(bounds); - ActivityStack stack = getLaunchStack(null, options, task, ON_TOP); + ActivityStack stack = + mRootActivityContainer.getLaunchStack(null, options, task, ON_TOP); if (stack != currentStack) { moveHomeStackToFrontIfNeeded(flags, stack.getDisplay(), reason); @@ -2305,7 +1386,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // still need moveTaskToFrontLocked() below for any transition settings. } if (stack.resizeStackWithLaunchBounds()) { - resizeStackLocked(stack, bounds, null /* tempTaskBounds */, + mRootActivityContainer.resizeStack(stack, bounds, null /* tempTaskBounds */, null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS, true /* allowResizeInDockedMode */, !DEFER_RESUME); } else { @@ -2358,388 +1439,22 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return mLaunchParamsController; } - protected <T extends ActivityStack> T getStack(int stackId) { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final T stack = mActivityDisplays.get(i).getStack(stackId); - if (stack != null) { - return stack; - } - } - return null; - } - - /** @see ActivityDisplay#getStack(int, int) */ - private <T extends ActivityStack> T getStack(int windowingMode, int activityType) { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final T stack = mActivityDisplays.get(i).getStack(windowingMode, activityType); - if (stack != null) { - return stack; - } - } - return null; - } - - int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options, - @Nullable TaskRecord task) { - // Preference is given to the activity type for the activity then the task since the type - // once set shouldn't change. - int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED; - if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) { - activityType = task.getActivityType(); - } - if (activityType != ACTIVITY_TYPE_UNDEFINED) { - return activityType; - } - if (options != null) { - activityType = options.getLaunchActivityType(); - } - return activityType != ACTIVITY_TYPE_UNDEFINED ? activityType : ACTIVITY_TYPE_STANDARD; - } - - <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r, - @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) { - return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */); - } - - /** - * Returns the right stack to use for launching factoring in all the input parameters. - * - * @param r The activity we are trying to launch. Can be null. - * @param options The activity options used to the launch. Can be null. - * @param candidateTask The possible task the activity might be launched in. Can be null. - * @params launchParams The resolved launch params to use. - * - * @return The stack to use for the launch or INVALID_STACK_ID. - */ - <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r, - @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop, - @Nullable LaunchParamsController.LaunchParams launchParams) { - int taskId = INVALID_TASK_ID; - int displayId = INVALID_DISPLAY; - //Rect bounds = null; - - // 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 - // associated with that if possible. - if (taskId != INVALID_TASK_ID) { - // Temporarily set the task id to invalid in case in re-entry. - options.setLaunchTaskId(INVALID_TASK_ID); - final TaskRecord task = anyTaskForIdLocked(taskId, - MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options, onTop); - options.setLaunchTaskId(taskId); - if (task != null) { - return task.getStack(); - } - } - - final int activityType = resolveActivityType(r, options, candidateTask); - T stack; - - // Next preference for stack goes to the display Id set the candidate display. - if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) { - displayId = launchParams.mPreferredDisplayId; - } - if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) { - if (r != null) { - stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options, - launchParams); - if (stack != null) { - return stack; - } - } - final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId); - if (display != null) { - stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop); - if (stack != null) { - return stack; - } - } - } - - // Give preference to the stack and display of the input task and activity if they match the - // mode we want to launch into. - stack = null; - ActivityDisplay display = null; - if (candidateTask != null) { - stack = candidateTask.getStack(); - } - if (stack == null && r != null) { - stack = r.getStack(); - } - if (stack != null) { - display = stack.getDisplay(); - if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) { - int windowingMode = launchParams != null ? launchParams.mWindowingMode - : WindowConfiguration.WINDOWING_MODE_UNDEFINED; - if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) { - windowingMode = display.resolveWindowingMode(r, options, candidateTask, - activityType); - } - if (stack.isCompatible(windowingMode, activityType)) { - return stack; - } - if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY - && display.getSplitScreenPrimaryStack() == stack - && candidateTask == stack.topTask()) { - // This is a special case when we try to launch an activity that is currently on - // top of split-screen primary stack, but is targeting split-screen secondary. - // In this case we don't want to move it to another stack. - // TODO(b/78788972): Remove after differentiating between preferred and required - // launch options. - return stack; - } - } - } - - if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) { - display = getDefaultDisplay(); - } - - return display.getOrCreateStack(r, options, candidateTask, activityType, onTop); - } - - /** @return true if activity record is null or can be launched on provided display. */ - private boolean canLaunchOnDisplay(ActivityRecord r, int displayId) { - if (r == null) { - return true; - } - return r.canBeLaunchedOnDisplay(displayId); - } - - /** - * Get a topmost stack on the display, that is a valid launch stack for specified activity. - * If there is no such stack, new dynamic stack can be created. - * @param displayId Target display. - * @param r Activity that should be launched there. - * @param candidateTask The possible task the activity might be put in. - * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null. - */ - private ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r, - @Nullable TaskRecord candidateTask, @Nullable ActivityOptions options, - @Nullable LaunchParamsController.LaunchParams launchParams) { - final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId); - if (activityDisplay == null) { - throw new IllegalArgumentException( - "Display with displayId=" + displayId + " not found."); - } - - if (!r.canBeLaunchedOnDisplay(displayId)) { - return null; - } - - // If {@code r} is already in target display and its task is the same as the candidate task, - // the intention should be getting a launch stack for the reusable activity, so we can use - // the existing stack. - if (r.getDisplayId() == displayId && r.getTask() == candidateTask) { - return candidateTask.getStack(); - } - - // Return the topmost valid stack on the display. - for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) { - final ActivityStack stack = activityDisplay.getChildAt(i); - if (isValidLaunchStack(stack, displayId, r)) { - return stack; - } - } - - // If there is no valid stack on the external display - check if new dynamic stack will do. - if (displayId != DEFAULT_DISPLAY) { - final int windowingMode; - if (launchParams != null) { - // When launch params is not null, we always defer to its windowing mode. Sometimes - // it could be unspecified, which indicates it should inherit windowing mode from - // display. - windowingMode = launchParams.mWindowingMode; - } else { - windowingMode = options != null ? options.getLaunchWindowingMode() - : r.getWindowingMode(); - } - 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); - return null; - } - - ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r, - @Nullable ActivityOptions options, - @Nullable LaunchParamsController.LaunchParams launchParams) { - return getValidLaunchStackOnDisplay(displayId, r, null /* candidateTask */, options, - launchParams); - } - - // TODO: Can probably be consolidated into getLaunchStack()... - private boolean isValidLaunchStack(ActivityStack stack, int displayId, ActivityRecord r) { - switch (stack.getActivityType()) { - case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome(); - case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents(); - case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant(); - } - // There is a 1-to-1 relationship between stack and task when not in - // primary split-windowing mode. - if (stack.getWindowingMode() != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - return false; - } else { - return r.supportsSplitScreenWindowingMode(); - } - } - - /** - * Get next focusable stack in the system. This will search through the stack on the same - * display as the current focused stack, looking for a focusable and visible stack, different - * from the target stack. If no valid candidates will be found, it will then go through all - * displays and stacks in last-focused order. - * - * @param currentFocus The stack that previously had focus. - * @param ignoreCurrent If we should ignore {@param currentFocus} when searching for next - * candidate. - * @return Next focusable {@link ActivityStack}, {@code null} if not found. - */ - ActivityStack getNextFocusableStackLocked(@NonNull ActivityStack currentFocus, - boolean ignoreCurrent) { - // First look for next focusable stack on the same display - final ActivityDisplay preferredDisplay = currentFocus.getDisplay(); - final ActivityStack preferredFocusableStack = preferredDisplay.getNextFocusableStack( - currentFocus, ignoreCurrent); - if (preferredFocusableStack != null) { - return preferredFocusableStack; - } - if (preferredDisplay.supportsSystemDecorations()) { - // Stop looking for focusable stack on other displays because the preferred display - // supports system decorations. Home activity would be launched on the same display if - // no focusable stack found. - return null; - } - - // Now look through all displays - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityDisplay display = mActivityDisplays.get(i); - if (display == preferredDisplay) { - // We've already checked this one - continue; - } - final ActivityStack nextFocusableStack = display.getNextFocusableStack(currentFocus, - ignoreCurrent); - if (nextFocusableStack != null) { - return nextFocusableStack; - } - } - - return null; - } - - /** - * Get next valid stack for launching provided activity in the system. This will search across - * displays and stacks in last-focused order for a focusable and visible stack, except those - * that are on a currently focused display. - * - * @param r The activity that is being launched. - * @param currentFocus The display that previously had focus and thus needs to be ignored when - * searching for the next candidate. - * @return Next valid {@link ActivityStack}, null if not found. - */ - ActivityStack getNextValidLaunchStackLocked(@NonNull ActivityRecord r, int currentFocus) { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityDisplay display = mActivityDisplays.get(i); - if (display.mDisplayId == currentFocus) { - continue; - } - final ActivityStack stack = getValidLaunchStackOnDisplay(display.mDisplayId, r, - null /* options */, null /* launchParams */); - if (stack != null) { - return stack; - } - } - return null; - } - - ActivityRecord getDefaultDisplayHomeActivity() { - return getDefaultDisplayHomeActivityForUser(mCurrentUser); + private void deferUpdateRecentsHomeStackBounds() { + mRootActivityContainer.deferUpdateBounds(ACTIVITY_TYPE_RECENTS); + mRootActivityContainer.deferUpdateBounds(ACTIVITY_TYPE_HOME); } - ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) { - return getActivityDisplay(DEFAULT_DISPLAY).getHomeActivityForUser(userId); - } - - void resizeStackLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds, - Rect tempTaskInsetBounds, boolean preserveWindows, boolean allowResizeInDockedMode, - boolean deferResume) { - - if (stack.inSplitScreenPrimaryWindowingMode()) { - resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null, - preserveWindows, deferResume); - return; - } - - final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenPrimaryStack(); - if (!allowResizeInDockedMode - && !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) { - // If the docked stack exists, don't resize non-floating stacks independently of the - // size computed from the docked stack size (otherwise they will be out of sync) - return; - } - - Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId); - mWindowManager.deferSurfaceLayout(); - try { - if (stack.affectedBySplitScreenResize()) { - if (bounds == null && stack.inSplitScreenWindowingMode()) { - // null bounds = fullscreen windowing mode...at least for now. - stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - } else if (splitScreenActive) { - // If we are in split-screen mode and this stack support split-screen, then - // it should be split-screen secondary mode. i.e. adjacent to the docked stack. - stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); - } - } - stack.resize(bounds, tempTaskBounds, tempTaskInsetBounds); - if (!deferResume) { - stack.ensureVisibleActivitiesConfigurationLocked( - stack.topRunningActivityLocked(), preserveWindows); - } - } finally { - mWindowManager.continueSurfaceLayout(); - Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); - } - } - - void deferUpdateRecentsHomeStackBounds() { - deferUpdateBounds(ACTIVITY_TYPE_RECENTS); - deferUpdateBounds(ACTIVITY_TYPE_HOME); - } - - void deferUpdateBounds(int activityType) { - final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType); - if (stack != null) { - stack.deferUpdateBounds(); - } - } - - void continueUpdateRecentsHomeStackBounds() { - continueUpdateBounds(ACTIVITY_TYPE_RECENTS); - continueUpdateBounds(ACTIVITY_TYPE_HOME); - } - - void continueUpdateBounds(int activityType) { - final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType); - if (stack != null) { - stack.continueUpdateBounds(); - } + private void continueUpdateRecentsHomeStackBounds() { + mRootActivityContainer.continueUpdateBounds(ACTIVITY_TYPE_RECENTS); + mRootActivityContainer.continueUpdateBounds(ACTIVITY_TYPE_HOME); } void notifyAppTransitionDone() { continueUpdateRecentsHomeStackBounds(); for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) { final int taskId = mResizingTasksDuringAnimation.valueAt(i); - final TaskRecord task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_ONLY); + final TaskRecord task = + mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY); if (task != null) { task.setTaskDockedResizing(false); } @@ -2758,7 +1473,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D try { final int windowingMode = fromStack.getWindowingMode(); final boolean inPinnedWindowingMode = windowingMode == WINDOWING_MODE_PINNED; - final ActivityDisplay toDisplay = getActivityDisplay(toDisplayId); + final ActivityDisplay toDisplay = + mRootActivityContainer.getActivityDisplay(toDisplayId); if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { // Tell the display we are exiting split-screen mode. @@ -2815,8 +1531,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS); - resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } finally { mAllowDockedStackResize = true; mWindowManager.continueSurfaceLayout(); @@ -2862,7 +1578,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D false /* deferResume */); } - private void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds, + void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds, Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds, boolean preserveWindows, boolean deferResume) { @@ -2871,7 +1587,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return; } - final ActivityStack stack = getDefaultDisplay().getSplitScreenPrimaryStack(); + final ActivityStack stack = + mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack(); if (stack == null) { Slog.w(TAG, "resizeDockedStackLocked: docked stack not found"); return; @@ -2910,7 +1627,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // static stacks need to be adjusted so they don't overlap with the docked stack. // We get the bounds to use from window manager which has been adjusted for any // screen controls and is also the same for all stacks. - final ActivityDisplay display = getDefaultDisplay(); + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); final Rect otherTaskRect = new Rect(); for (int i = display.getChildCount() - 1; i >= 0; --i) { final ActivityStack current = display.getChildAt(i); @@ -2930,7 +1647,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D tempRect /* outStackBounds */, otherTaskRect /* outTempTaskBounds */); - resizeStackLocked(current, !tempRect.isEmpty() ? tempRect : null, + mRootActivityContainer.resizeStack(current, + !tempRect.isEmpty() ? tempRect : null, !otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds, tempOtherTaskInsetBounds, preserveWindows, true /* allowResizeInDockedMode */, deferResume); @@ -2948,7 +1666,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) { // TODO(multi-display): Pinned stack display should be passed in. - final PinnedActivityStack stack = getDefaultDisplay().getPinnedStack(); + final PinnedActivityStack stack = + mRootActivityContainer.getDefaultDisplay().getPinnedStack(); if (stack == null) { Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found"); return; @@ -3029,22 +1748,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } /** - * Removes stacks in the input windowing modes from the system if they are of activity type - * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED - */ - void removeStacksInWindowingModes(int... windowingModes) { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - mActivityDisplays.get(i).removeStacksInWindowingModes(windowingModes); - } - } - - void removeStacksWithActivityTypes(int... activityTypes) { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - mActivityDisplays.get(i).removeStacksWithActivityTypes(activityTypes); - } - } - - /** * See {@link #removeTaskByIdLocked(int, boolean, boolean, boolean)} */ boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents, @@ -3065,7 +1768,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D */ boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents, boolean pauseImmediately, String reason) { - final TaskRecord tr = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); + final TaskRecord tr = + mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (tr != null) { tr.removeTaskActivitiesLocked(pauseImmediately, reason); cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents); @@ -3154,7 +1858,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D * @return true if the task has been restored successfully. */ boolean restoreRecentTaskLocked(TaskRecord task, ActivityOptions aOptions, boolean onTop) { - final ActivityStack stack = getLaunchStack(null, aOptions, task, onTop); + final ActivityStack stack = + mRootActivityContainer.getLaunchStack(null, aOptions, task, onTop); final ActivityStack currentStack = task.getStack(); if (currentStack != null) { // Task has already been restored once. See if we need to do anything more @@ -3174,7 +1879,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D "Added restored task=" + task + " to stack=" + stack); final ArrayList<ActivityRecord> activities = task.mActivities; for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { - activities.get(activityNdx).createWindowContainer(); + activities.get(activityNdx).createAppWindowToken(); } return true; } @@ -3196,39 +1901,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } /** - * Move stack with all its existing content to specified display. - * @param stackId Id of stack to move. - * @param displayId Id of display to move stack to. - * @param onTop Indicates whether container should be place on top or on bottom. - */ - void moveStackToDisplayLocked(int stackId, int displayId, boolean onTop) { - final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId); - if (activityDisplay == null) { - throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown displayId=" - + displayId); - } - final ActivityStack stack = getStack(stackId); - if (stack == null) { - throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown stackId=" - + stackId); - } - - final ActivityDisplay currentDisplay = stack.getDisplay(); - if (currentDisplay == null) { - throw new IllegalStateException("moveStackToDisplayLocked: Stack with stack=" + stack - + " is not attached to any display."); - } - - if (currentDisplay.mDisplayId == displayId) { - throw new IllegalArgumentException("Trying to move stack=" + stack - + " to its current displayId=" + displayId); - } - - stack.reparent(activityDisplay, onTop, false /* displayRemoved */); - // TODO(multi-display): resize stacks properly if moved from split-screen. - } - - /** * Returns the reparent target stack, creating the stack if necessary. This call also enforces * the various checks on tasks that are going to be reparented from one stack to another. */ @@ -3280,159 +1952,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return stack; } - boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect destBounds) { - final ActivityStack stack = getStack(stackId); - if (stack == null) { - throw new IllegalArgumentException( - "moveTopStackActivityToPinnedStackLocked: Unknown stackId=" + stackId); - } - - final ActivityRecord r = stack.topRunningActivityLocked(); - if (r == null) { - Slog.w(TAG, "moveTopStackActivityToPinnedStackLocked: No top running activity" - + " in stack=" + stack); - return false; - } - - if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) { - Slog.w(TAG, - "moveTopStackActivityToPinnedStackLocked: Picture-In-Picture not supported for " - + " r=" + r); - return false; - } - - moveActivityToPinnedStackLocked(r, null /* sourceBounds */, 0f /* aspectRatio */, - "moveTopActivityToPinnedStack"); - return true; - } - - void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceHintBounds, float aspectRatio, - String reason) { - - mWindowManager.deferSurfaceLayout(); - - final ActivityDisplay display = r.getStack().getDisplay(); - PinnedActivityStack stack = display.getPinnedStack(); - - // This will clear the pinned stack by moving an existing task to the full screen stack, - // ensuring only one task is present. - if (stack != null) { - moveTasksToFullscreenStackLocked(stack, !ON_TOP); - } - - // Need to make sure the pinned stack exist so we can resize it below... - stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP); - - // Calculate the target bounds here before the task is reparented back into pinned windowing - // mode (which will reset the saved bounds) - final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio); - - try { - final TaskRecord task = r.getTask(); - // Resize the pinned stack to match the current size of the task the activity we are - // going to be moving is currently contained in. We do this to have the right starting - // animation bounds for the pinned stack to the desired bounds the caller wants. - resizeStackLocked(stack, task.getOverrideBounds(), null /* tempTaskBounds */, - null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS, - true /* allowResizeInDockedMode */, !DEFER_RESUME); - - if (task.mActivities.size() == 1) { - // Defer resume until below, and do not schedule PiP changes until we animate below - task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME, - false /* schedulePictureInPictureModeChange */, reason); - } else { - // There are multiple activities in the task and moving the top activity should - // reveal/leave the other activities in their original task. - - // Currently, we don't support reparenting activities across tasks in two different - // stacks, so instead, just create a new task in the same stack, reparent the - // activity into that task, and then reparent the whole task to the new stack. This - // ensures that all the necessary work to migrate states in the old and new stacks - // is also done. - final TaskRecord newTask = task.getStack().createTaskRecord( - getNextTaskIdForUserLocked(r.userId), r.info, r.intent, null, null, true); - r.reparent(newTask, MAX_VALUE, "moveActivityToStack"); - - // Defer resume until below, and do not schedule PiP changes until we animate below - newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, - DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason); - } - - // Reset the state that indicates it can enter PiP while pausing after we've moved it - // to the pinned stack - r.supportsEnterPipOnTaskSwitch = false; - } finally { - mWindowManager.continueSurfaceLayout(); - } - - stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */, - true /* fromFullscreen */); - - // Update the visibility of all activities after the they have been reparented to the new - // stack. This MUST run after the animation above is scheduled to ensure that the windows - // drawn signal is scheduled after the bounds animation start call on the bounds animator - // thread. - ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); - resumeFocusedStacksTopActivitiesLocked(); - - mService.getTaskChangeNotificationController().notifyActivityPinned(r); - } - - ActivityRecord findTaskLocked(ActivityRecord r, int preferredDisplayId) { - if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r); - mTmpFindTaskResult.clear(); - - // Looking up task on preferred display first - final ActivityDisplay preferredDisplay = getActivityDisplay(preferredDisplayId); - if (preferredDisplay != null) { - preferredDisplay.findTaskLocked(r, true /* isPreferredDisplay */, mTmpFindTaskResult); - if (mTmpFindTaskResult.mIdealMatch) { - return mTmpFindTaskResult.mRecord; - } - } - - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - if (display.mDisplayId == preferredDisplayId) { - continue; - } - - display.findTaskLocked(r, false /* isPreferredDisplay */, mTmpFindTaskResult); - if (mTmpFindTaskResult.mIdealMatch) { - return mTmpFindTaskResult.mRecord; - } - } - - if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found"); - return mTmpFindTaskResult.mRecord; - } - - ActivityRecord findActivityLocked(Intent intent, ActivityInfo info, - boolean compareIntentFilters) { - 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); - final ActivityRecord ar = stack.findActivityLocked( - intent, info, compareIntentFilters); - if (ar != null) { - return ar; - } - } - } - return null; - } - - boolean hasAwakeDisplay() { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - if (!display.shouldSleep()) { - return true; - } - } - return false; - } - void goingToSleepLocked() { scheduleSleepTimeout(); if (!mGoingToSleep.isHeld()) { @@ -3446,24 +1965,19 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - applySleepTokensLocked(false /* applyToStacks */); + mRootActivityContainer.applySleepTokens(false /* applyToStacks */); checkReadyForSleepLocked(true /* allowDelay */); } - void prepareForShutdownLocked() { - for (int i = 0; i < mActivityDisplays.size(); i++) { - createSleepTokenLocked("shutdown", mActivityDisplays.get(i).mDisplayId); - } - } - boolean shutdownLocked(int timeout) { goingToSleepLocked(); boolean timedout = false; final long endTime = System.currentTimeMillis() + timeout; while (true) { - if (!putStacksToSleepLocked(true /* allowDelay */, true /* shuttingDown */)) { + if (!mRootActivityContainer.putStacksToSleep( + true /* allowDelay */, true /* shuttingDown */)) { long timeRemaining = endTime - System.currentTimeMillis(); if (timeRemaining > 0) { try { @@ -3493,51 +2007,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - void applySleepTokensLocked(boolean applyToStacks) { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - // Set the sleeping state of the display. - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - final boolean displayShouldSleep = display.shouldSleep(); - if (displayShouldSleep == display.isSleeping()) { - continue; - } - display.setIsSleeping(displayShouldSleep); - - if (!applyToStacks) { - continue; - } - - // Set the sleeping state of the stacks on the display. - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - if (displayShouldSleep) { - stack.goToSleepIfPossible(false /* shuttingDown */); - } else { - stack.awakeFromSleepingLocked(); - if (stack.isFocusedStackOnDisplay() && !getKeyguardController() - .isKeyguardOrAodShowing(display.mDisplayId)) { - // If the keyguard is unlocked - resume immediately. - // It is possible that the display will not be awake at the time we - // process the keyguard going away, which can happen before the sleep token - // is released. As a result, it is important we resume the activity here. - resumeFocusedStacksTopActivitiesLocked(); - } - } - } - - if (displayShouldSleep || mGoingToSleepActivities.isEmpty()) { - continue; - } - // The display is awake now, so clean up the going to sleep list. - for (Iterator<ActivityRecord> it = mGoingToSleepActivities.iterator(); it.hasNext(); ) { - final ActivityRecord r = it.next(); - if (r.getDisplayId() == display.mDisplayId) { - it.remove(); - } - } - } - } - void activitySleptLocked(ActivityRecord r) { mGoingToSleepActivities.remove(r); final ActivityStack s = r.getStack(); @@ -3554,12 +2023,13 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return; } - if (!putStacksToSleepLocked(allowDelay, false /* shuttingDown */)) { + if (!mRootActivityContainer.putStacksToSleep( + allowDelay, false /* shuttingDown */)) { return; } // Send launch end powerhint before going sleep - sendPowerHintForLaunchEndIfNeeded(); + mRootActivityContainer.sendPowerHintForLaunchEndIfNeeded(); removeSleepTimeouts(); @@ -3571,52 +2041,24 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - // Tries to put all activity stacks to sleep. Returns true if all stacks were - // successfully put to sleep. - private boolean putStacksToSleepLocked(boolean allowDelay, boolean shuttingDown) { - boolean allSleep = true; - 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 (allowDelay) { - allSleep &= stack.goToSleepIfPossible(shuttingDown); - } else { - stack.goToSleep(); - } - } - } - return allSleep; - } - boolean reportResumedActivityLocked(ActivityRecord r) { // A resumed activity cannot be stopping. remove from list mStoppingActivities.remove(r); final ActivityStack stack = r.getStack(); - if (isTopDisplayFocusedStack(stack)) { + if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) { mService.updateUsageStats(r, true); } if (stack.getDisplay().allResumedActivitiesComplete()) { - ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); // Make sure activity & window visibility should be identical // for all displays in this stage. - executeAppTransitionForAllDisplay(); + mRootActivityContainer.executeAppTransitionForAllDisplay(); return true; } return false; } - void handleAppCrashLocked(WindowProcessController app) { - 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); - stack.handleAppCrashLocked(app); - } - } - } - // Called when WindowManager has finished animating the launchingBehind activity to the back. private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) { final TaskRecord task = r.getTask(); @@ -3639,157 +2081,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mHandler.obtainMessage(LAUNCH_TASK_BEHIND_COMPLETE, token).sendToTarget(); } - /** - * Make sure that all activities that need to be visible in the system actually are and update - * their configuration. - */ - void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges, - boolean preserveWindows) { - ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows, - true /* notifyClients */); - } - - /** - * @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean) - */ - void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges, - boolean preserveWindows, boolean notifyClients) { - getKeyguardController().beginActivityVisibilityUpdate(); - try { - // First the front stacks. In case any are not fullscreen and are in front of home. - 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); - stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows, - notifyClients); - } - } - } finally { - getKeyguardController().endActivityVisibilityUpdate(); - } - } - - void addStartingWindowsForVisibleActivities(boolean taskSwitch) { - 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); - stack.addStartingWindowsForVisibleActivities(taskSwitch); - } - } - } - - void invalidateTaskLayers() { - mTaskLayersChanged = true; - } - - void rankTaskLayersIfNeeded() { - if (!mTaskLayersChanged) { - return; - } - mTaskLayersChanged = false; - for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - int baseLayer = 0; - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - baseLayer += stack.rankTaskLayers(baseLayer); - } - } - } - - void clearOtherAppTimeTrackers(AppTimeTracker except) { - 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); - stack.clearOtherAppTimeTrackers(except); - } - } - } - - void scheduleDestroyAllActivities(WindowProcessController app, String reason) { - 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); - stack.scheduleDestroyActivities(app, reason); - } - } - } - - void releaseSomeActivitiesLocked(WindowProcessController app, String reason) { - // Tasks is non-null only if two or more tasks are found. - ArraySet<TaskRecord> tasks = app.getReleaseSomeActivitiesTasks(); - if (tasks == null) { - if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release"); - return; - } - // If we have activities in multiple tasks that are in a position to be destroyed, - // let's iterate through the tasks and release the oldest one. - final int numDisplays = mActivityDisplays.size(); - for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - final int stackCount = display.getChildCount(); - // Step through all stacks starting from behind, to hit the oldest things first. - for (int stackNdx = 0; stackNdx < stackCount; stackNdx++) { - final ActivityStack stack = display.getChildAt(stackNdx); - // Try to release activities in this stack; if we manage to, we are done. - if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) { - return; - } - } - } - } - - boolean switchUserLocked(int userId, UserState uss) { - final int focusStackId = getTopDisplayFocusedStack().getStackId(); - // We dismiss the docked stack whenever we switch users. - final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack(); - if (dockedStack != null) { - moveTasksToFullscreenStackLocked(dockedStack, dockedStack.isFocusedStackOnDisplay()); - } - // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will - // also cause all tasks to be moved to the fullscreen stack at a position that is - // appropriate. - removeStacksInWindowingModes(WINDOWING_MODE_PINNED); - - mUserStackInFront.put(mCurrentUser, focusStackId); - final int restoreStackId = - mUserStackInFront.get(userId, getDefaultDisplay().getHomeStack().mStackId); - mCurrentUser = userId; - - mStartingUsers.add(uss); - 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); - stack.switchUserLocked(userId); - TaskRecord task = stack.topTask(); - if (task != null) { - stack.positionChildWindowContainerAtTop(task); - } - } - } - - ActivityStack stack = getStack(restoreStackId); - if (stack == null) { - stack = getDefaultDisplay().getHomeStack(); - } - final boolean homeInFront = stack.isActivityTypeHome(); - if (stack.isOnHomeDisplay()) { - stack.moveToFront("switchUserOnHomeDisplay"); - } else { - // Stack was moved to another display while user was swapped out. - resumeHomeActivity(null, "switchUserOnOtherDisplay", DEFAULT_DISPLAY); - } - return homeInFront; - } - /** Checks whether the userid is a profile of the current user. */ boolean isCurrentProfileLocked(int userId) { - if (userId == mCurrentUser) return true; + if (userId == mRootActivityContainer.mCurrentUser) return true; return mService.mAmInternal.isCurrentProfile(userId); } @@ -3814,7 +2108,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D boolean remove, boolean processPausingActivities) { ArrayList<ActivityRecord> stops = null; - final boolean nowVisible = allResumedActivitiesVisible(); + final boolean nowVisible = mRootActivityContainer.allResumedActivitiesVisible(); for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) { ActivityRecord s = mStoppingActivities.get(activityNdx); boolean waitingVisible = mActivitiesWaitingForVisibleActivity.contains(s); @@ -3864,134 +2158,26 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return stops; } - void validateTopActivitiesLocked() { - 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); - final ActivityRecord r = stack.topRunningActivityLocked(); - final ActivityState state = r == null ? DESTROYED : r.getState(); - if (isTopDisplayFocusedStack(stack)) { - if (r == null) Slog.e(TAG, - "validateTop...: null top activity, stack=" + stack); - else { - final ActivityRecord pausing = stack.mPausingActivity; - if (pausing != null && pausing == r) Slog.e(TAG, - "validateTop...: top stack has pausing activity r=" + r - + " state=" + state); - if (state != INITIALIZING && state != RESUMED) Slog.e(TAG, - "validateTop...: activity in front not resumed r=" + r - + " state=" + state); - } - } else { - final ActivityRecord resumed = stack.getResumedActivity(); - if (resumed != null && resumed == r) Slog.e(TAG, - "validateTop...: back stack has resumed activity r=" + r - + " state=" + state); - if (r != null && (state == INITIALIZING || state == RESUMED)) Slog.e(TAG, - "validateTop...: activity in back resumed r=" + r + " state=" + state); - } - } - } - } - - public void dumpDisplays(PrintWriter pw) { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityDisplay display = mActivityDisplays.get(i); - pw.print("[id:" + display.mDisplayId + " stacks:"); - display.dumpStacks(pw); - pw.print("]"); - } - } - public void dump(PrintWriter pw, String prefix) { pw.println(); pw.println("ActivityStackSupervisor state:"); - pw.print(prefix); - pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack()); + mRootActivityContainer.dump(pw, prefix); pw.print(prefix); pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser); - pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront); - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityDisplay display = mActivityDisplays.get(i); - display.dump(pw, prefix); - } + pw.println(prefix + "mUserStackInFront=" + mRootActivityContainer.mUserStackInFront); if (!mWaitingForActivityVisible.isEmpty()) { - pw.print(prefix); pw.println("mWaitingForActivityVisible="); + pw.println(prefix + "mWaitingForActivityVisible="); for (int i = 0; i < mWaitingForActivityVisible.size(); ++i) { - pw.print(prefix); pw.print(prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix); + pw.print(prefix + prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix); } } pw.print(prefix); pw.print("isHomeRecentsComponent="); - pw.print(mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser)); + pw.print(mRecentTasks.isRecentsComponentHomeActivity(mRootActivityContainer.mCurrentUser)); getKeyguardController().dump(pw, prefix); mService.getLockTaskController().dump(pw, prefix); } - public void writeToProto(ProtoOutputStream proto, long fieldId) { - final long token = proto.start(fieldId); - super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */); - for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) { - final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx); - activityDisplay.writeToProto(proto, DISPLAYS); - } - getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER); - // TODO(b/111541062): Update tests to look for resumed activities on all displays - final ActivityStack focusedStack = getTopDisplayFocusedStack(); - if (focusedStack != null) { - proto.write(FOCUSED_STACK_ID, focusedStack.mStackId); - final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity(); - if (focusedActivity != null) { - focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY); - } - } else { - proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID); - } - proto.write(IS_HOME_RECENTS_COMPONENT, - mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser)); - mService.getActivityStartController().writeToProto(proto, PENDING_ACTIVITIES); - proto.end(token); - } - - /** - * Dump all connected displays' configurations. - * @param prefix Prefix to apply to each line of the dump. - */ - void dumpDisplayConfigs(PrintWriter pw, String prefix) { - pw.print(prefix); pw.println("Display override configurations:"); - final int displayCount = mActivityDisplays.size(); - for (int i = 0; i < displayCount; i++) { - final ActivityDisplay activityDisplay = mActivityDisplays.get(i); - pw.print(prefix); pw.print(" "); pw.print(activityDisplay.mDisplayId); pw.print(": "); - pw.println(activityDisplay.getOverrideConfiguration()); - } - } - - /** - * Dumps the activities matching the given {@param name} in the either the focused stack - * or all visible stacks if {@param dumpVisibleStacks} is true. - */ - ArrayList<ActivityRecord> getDumpActivitiesLocked(String name, boolean dumpVisibleStacksOnly, - boolean dumpFocusedStackOnly) { - if (dumpFocusedStackOnly) { - return getTopDisplayFocusedStack().getDumpActivitiesLocked(name); - } else { - ArrayList<ActivityRecord> activities = new ArrayList<>(); - int numDisplays = mActivityDisplays.size(); - for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) { - activities.addAll(stack.getDumpActivitiesLocked(name)); - } - } - } - return activities; - } - } - static boolean printThisActivity(PrintWriter pw, ActivityRecord activity, String dumpPackage, boolean needSep, String prefix) { if (activity != null) { @@ -4007,73 +2193,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return false; } - boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll, - boolean dumpClient, String dumpPackage) { - boolean printed = false; - boolean needSep = false; - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx); - pw.print("Display #"); pw.print(activityDisplay.mDisplayId); - pw.println(" (activities from top to bottom):"); - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - pw.println(); - pw.println(" Stack #" + stack.mStackId - + ": type=" + activityTypeToString(stack.getActivityType()) - + " mode=" + windowingModeToString(stack.getWindowingMode())); - pw.println(" isSleeping=" + stack.shouldSleepActivities()); - pw.println(" mBounds=" + stack.getOverrideBounds()); - - printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage, - needSep); - - printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, " ", "Run", false, - !dumpAll, false, dumpPackage, true, - " Running activities (most recent first):", null); - - needSep = printed; - boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep, - " mPausingActivity: "); - if (pr) { - printed = true; - needSep = false; - } - pr = printThisActivity(pw, stack.getResumedActivity(), dumpPackage, needSep, - " mResumedActivity: "); - if (pr) { - printed = true; - needSep = false; - } - if (dumpAll) { - pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep, - " mLastPausedActivity: "); - if (pr) { - printed = true; - needSep = true; - } - printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage, - needSep, " mLastNoHistoryActivity: "); - } - needSep = printed; - } - printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep, - " ResumedActivity:"); - } - - printed |= dumpHistoryList(fd, pw, mFinishingActivities, " ", "Fin", false, !dumpAll, - false, dumpPackage, true, " Activities waiting to finish:", null); - printed |= dumpHistoryList(fd, pw, mStoppingActivities, " ", "Stop", false, !dumpAll, - false, dumpPackage, true, " Activities waiting to stop:", null); - printed |= dumpHistoryList(fd, pw, mActivitiesWaitingForVisibleActivity, " ", "Wait", - false, !dumpAll, false, dumpPackage, true, - " Activities waiting for another to become visible:", null); - printed |= dumpHistoryList(fd, pw, mGoingToSleepActivities, " ", "Sleep", false, !dumpAll, - false, dumpPackage, true, " Activities waiting to sleep:", null); - - return printed; - } - static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list, String prefix, String label, boolean complete, boolean brief, boolean client, String dumpPackage, boolean needNL, String header, TaskRecord lastTask) { @@ -4183,294 +2302,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mHandler.sendEmptyMessageDelayed(SLEEP_TIMEOUT_MSG, SLEEP_TIMEOUT); } - @Override - public void onDisplayAdded(int displayId) { - if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId); - synchronized (mService.mGlobalLock) { - getActivityDisplayOrCreateLocked(displayId); - // Do not start home before booting, or it may accidentally finish booting before it - // starts. Instead, we expect home activities to be launched when the system is ready - // (ActivityManagerService#systemReady). - if (mService.isBooted() || mService.isBooting()) { - startHomeOnDisplay(mCurrentUser, "displayAdded", displayId); - } - } - } - - @Override - public void onDisplayRemoved(int displayId) { - if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId); - if (displayId == DEFAULT_DISPLAY) { - throw new IllegalArgumentException("Can't remove the primary display."); - } - - synchronized (mService.mGlobalLock) { - final ActivityDisplay activityDisplay = getActivityDisplay(displayId); - if (activityDisplay == null) { - return; - } - - activityDisplay.remove(); - } - } - - @Override - public void onDisplayChanged(int displayId) { - if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId); - synchronized (mService.mGlobalLock) { - final ActivityDisplay activityDisplay = getActivityDisplay(displayId); - if (activityDisplay != null) { - activityDisplay.onDisplayChanged(); - } - } - } - - /** Check if display with specified id is added to the list. */ - boolean isDisplayAdded(int displayId) { - return getActivityDisplayOrCreateLocked(displayId) != null; - } - - // TODO: Look into consolidating with getActivityDisplayOrCreateLocked() - ActivityDisplay getActivityDisplay(int displayId) { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityDisplay activityDisplay = mActivityDisplays.get(i); - if (activityDisplay.mDisplayId == displayId) { - return activityDisplay; - } - } - return null; - } - - // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display. - ActivityDisplay getDefaultDisplay() { - return mDefaultDisplay; - } - - /** - * Get an existing instance of {@link ActivityDisplay} or create new if there is a - * corresponding record in display manager. - */ - // TODO: Look into consolidating with getActivityDisplay() - ActivityDisplay getActivityDisplayOrCreateLocked(int displayId) { - ActivityDisplay activityDisplay = getActivityDisplay(displayId); - if (activityDisplay != null) { - return activityDisplay; - } - if (mDisplayManager == null) { - // The system isn't fully initialized yet. - return null; - } - final Display display = mDisplayManager.getDisplay(displayId); - if (display == null) { - // The display is not registered in DisplayManager. - return null; - } - // The display hasn't been added to ActivityManager yet, create a new record now. - activityDisplay = new ActivityDisplay(this, display); - addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM); - return activityDisplay; - } - - /** - * Get an existing instance of {@link ActivityDisplay} that has the given uniqueId. Unique ID is - * defined in {@link DisplayInfo#uniqueId}. - * - * @param uniqueId the unique ID of the display - * @return the {@link ActivityDisplay} or {@code null} if nothing is found. - */ - ActivityDisplay getActivityDisplay(String uniqueId) { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityDisplay display = mActivityDisplays.get(i); - final boolean isValid = display.mDisplay.isValid(); - if (isValid && display.mDisplay.getUniqueId().equals(uniqueId)) { - return display; - } - } - - return null; - } - - boolean startHomeOnAllDisplays(int userId, String reason) { - boolean homeStarted = false; - for (int i = mActivityDisplays.size() - 1; i >= 0; i--) { - final int displayId = mActivityDisplays.get(i).mDisplayId; - homeStarted |= startHomeOnDisplay(userId, reason, displayId); - } - return homeStarted; - } - - /** - * This starts home activity on displays that can have system decorations and only if the - * home activity can have multiple instances. - */ - boolean startHomeOnDisplay(int userId, String reason, int displayId) { - final Intent homeIntent = mService.getHomeIntent(); - final ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent); - if (aInfo == null) { - return false; - } - - if (!canStartHomeOnDisplay(aInfo, displayId, false /* allowInstrumenting */)) { - return false; - } - - // Update the reason for ANR debugging to verify if the user activity is the one that - // actually launched. - final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId( - aInfo.applicationInfo.uid); - mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason, - displayId); - return true; - } - - /** - * This resolves the home activity info and updates the home component of the given intent. - * @return the home activity info if any. - */ - private ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) { - final int flags = ActivityManagerService.STOCK_PM_FLAGS; - final ComponentName comp = homeIntent.getComponent(); - ActivityInfo aInfo = null; - try { - if (comp != null) { - // Factory test. - aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId); - } else { - final String resolvedType = - homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver()); - final ResolveInfo info = AppGlobals.getPackageManager() - .resolveIntent(homeIntent, resolvedType, flags, userId); - if (info != null) { - aInfo = info.activityInfo; - } - } - } catch (RemoteException e) { - // ignore - } - - if (aInfo == null) { - Slog.wtf(TAG, "No home screen found for " + homeIntent, new Throwable()); - return null; - } - - homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name)); - aInfo = new ActivityInfo(aInfo); - aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId); - homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK); - return aInfo; - } - - @VisibleForTesting - void addChild(ActivityDisplay activityDisplay, int position) { - positionChildAt(activityDisplay, position); - mWindowContainerController.positionChildAt( - activityDisplay.getWindowContainerController(), position); - } - - void removeChild(ActivityDisplay activityDisplay) { - // The caller must tell the controller of {@link ActivityDisplay} to release its container - // {@link DisplayContent}. That is done in {@link ActivityDisplay#releaseSelfIfNeeded}). - mActivityDisplays.remove(activityDisplay); - } - - private void calculateDefaultMinimalSizeOfResizeableTasks() { - final Resources res = mService.mContext.getResources(); - final float minimalSize = res.getDimension( - com.android.internal.R.dimen.default_minimal_size_resizable_task); - final DisplayMetrics dm = res.getDisplayMetrics(); - - mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density); - } - - SleepToken createSleepTokenLocked(String tag, int displayId) { - final ActivityDisplay display = getActivityDisplay(displayId); - if (display == null) { - throw new IllegalArgumentException("Invalid display: " + displayId); - } - - final SleepTokenImpl token = new SleepTokenImpl(tag, displayId); - mSleepTokens.add(token); - display.mAllSleepTokens.add(token); - return token; - } - - private void removeSleepTokenLocked(SleepTokenImpl token) { - mSleepTokens.remove(token); - - final ActivityDisplay display = getActivityDisplay(token.mDisplayId); - if (display != null) { - display.mAllSleepTokens.remove(token); - if (display.mAllSleepTokens.isEmpty()) { - mService.updateSleepIfNeededLocked(); - } - } - } - - private StackInfo getStackInfo(ActivityStack stack) { - final int displayId = stack.mDisplayId; - final ActivityDisplay display = getActivityDisplay(displayId); - StackInfo info = new StackInfo(); - stack.getWindowContainerBounds(info.bounds); - info.displayId = displayId; - info.stackId = stack.mStackId; - info.userId = stack.mCurrentUser; - info.visible = stack.shouldBeVisible(null); - // A stack might be not attached to a display. - info.position = display != null ? display.getIndexOf(stack) : 0; - info.configuration.setTo(stack.getConfiguration()); - - ArrayList<TaskRecord> tasks = stack.getAllTasks(); - final int numTasks = tasks.size(); - int[] taskIds = new int[numTasks]; - String[] taskNames = new String[numTasks]; - Rect[] taskBounds = new Rect[numTasks]; - int[] taskUserIds = new int[numTasks]; - for (int i = 0; i < numTasks; ++i) { - final TaskRecord task = tasks.get(i); - taskIds[i] = task.taskId; - taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString() - : task.realActivity != null ? task.realActivity.flattenToString() - : task.getTopActivity() != null ? task.getTopActivity().packageName - : "unknown"; - taskBounds[i] = new Rect(); - task.getWindowContainerBounds(taskBounds[i]); - taskUserIds[i] = task.userId; - } - info.taskIds = taskIds; - info.taskNames = taskNames; - info.taskBounds = taskBounds; - info.taskUserIds = taskUserIds; - - final ActivityRecord top = stack.topRunningActivityLocked(); - info.topActivity = top != null ? top.intent.getComponent() : null; - return info; - } - - StackInfo getStackInfo(int stackId) { - ActivityStack stack = getStack(stackId); - if (stack != null) { - return getStackInfo(stack); - } - return null; - } - - StackInfo getStackInfo(int windowingMode, int activityType) { - final ActivityStack stack = getStack(windowingMode, activityType); - return (stack != null) ? getStackInfo(stack) : null; - } - - ArrayList<StackInfo> getAllStackInfosLocked() { - ArrayList<StackInfo> list = new ArrayList<>(); - for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - list.add(getStackInfo(stack)); - } - } - return list; - } - void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredWindowingMode, int preferredDisplayId, ActivityStack actualStack) { handleNonResizableTaskIfNeeded(task, preferredWindowingMode, preferredDisplayId, @@ -4616,21 +2447,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - void setDockedStackMinimized(boolean minimized) { - // Get currently focused stack before setting mIsDockMinimized. We do this because if - // split-screen is active, primary stack will not be focusable (see #isFocusable) while - // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null. - final ActivityStack current = getTopDisplayFocusedStack(); - mIsDockMinimized = minimized; - if (mIsDockMinimized) { - if (current.inSplitScreenPrimaryWindowingMode()) { - // The primary split-screen stack can't be focused while it is minimize, so move - // focus to something else. - current.adjustFocusToNextFocusableStack("setDockedStackMinimized"); - } - } - } - void wakeUp(String reason) { mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.am:TURN_ON:" + reason); } @@ -4649,10 +2465,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mDeferResumeCount--; } - /** - * @return True if resume can be called. - */ - private boolean readyToResume() { + /** @return True if resume can be called. */ + boolean readyToResume() { return mDeferResumeCount == 0; } @@ -4704,7 +2518,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } break; case RESUME_TOP_ACTIVITY_MSG: { synchronized (mService.mGlobalLock) { - resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } } break; case SLEEP_TIMEOUT_MSG: { @@ -4740,19 +2554,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - ActivityStack findStackBehind(ActivityStack stack) { - final ActivityDisplay display = getActivityDisplay(stack.mDisplayId); - if (display != null) { - for (int i = display.getChildCount() - 1; i >= 0; i--) { - if (display.getChildAt(i) == stack && i > 0) { - return display.getChildAt(i - 1); - } - } - } - throw new IllegalStateException("Failed to find a stack behind stack=" + stack - + " in=" + display); - } - /** * Puts a task into resizing mode during the next app transition. * @@ -4797,8 +2598,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false); } - task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, - activityOptions, ON_TOP); + task = mRootActivityContainer.anyTaskForId(taskId, + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP); if (task == null) { continueUpdateRecentsHomeStackBounds(); mWindowManager.executeAppTransition(); @@ -4811,7 +2612,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // from whatever is started from the recents activity, so move the home stack // forward. // TODO (b/115289124): Multi-display supports for recents. - getDefaultDisplay().moveHomeStackToFront("startActivityFromRecents"); + mRootActivityContainer.getDefaultDisplay().moveHomeStackToFront( + "startActivityFromRecents"); } // If the user must confirm credentials (e.g. when first launching a work app and the @@ -4820,7 +2622,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D && task.getRootActivity() != null) { final ActivityRecord targetActivity = task.getTopActivity(); - sendPowerHintForLaunchStartIfNeeded(true /* forceSend */, targetActivity); + mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded( + true /* forceSend */, targetActivity); mActivityMetricsLogger.notifyActivityLaunching(task.intent); try { mService.moveTaskToFrontLocked(task.taskId, 0, options, @@ -4873,35 +2676,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } /** - * @return a list of activities which are the top ones in each visible stack. The first - * entry will be the focused activity. - */ - List<IBinder> getTopVisibleActivities() { - final ArrayList<IBinder> topActivityTokens = new ArrayList<>(); - final ActivityStack topFocusedStack = getTopDisplayFocusedStack(); - // Traverse all displays. - for (int i = mActivityDisplays.size() - 1; i >= 0; i--) { - final ActivityDisplay display = mActivityDisplays.get(i); - // Traverse all stacks on a display. - for (int j = display.getChildCount() - 1; j >= 0; --j) { - final ActivityStack stack = display.getChildAt(j); - // Get top activity from a visible stack and add it to the list. - if (stack.shouldBeVisible(null /* starting */)) { - final ActivityRecord top = stack.getTopActivity(); - if (top != null) { - if (stack == topFocusedStack) { - topActivityTokens.add(0, top.appToken); - } else { - topActivityTokens.add(top.appToken); - } - } - } - } - } - return topActivityTokens; - } - - /** * Internal container to store a match qualifier alongside a WaitResult. */ static class WaitInfo { @@ -4939,30 +2713,4 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mResult.dump(pw, prefix); } } - - private final class SleepTokenImpl extends SleepToken { - private final String mTag; - private final long mAcquireTime; - private final int mDisplayId; - - public SleepTokenImpl(String tag, int displayId) { - mTag = tag; - mDisplayId = displayId; - mAcquireTime = SystemClock.uptimeMillis(); - } - - @Override - public void release() { - synchronized (mService.mGlobalLock) { - removeSleepTokenLocked(this); - } - } - - @Override - public String toString() { - return "{\"" + mTag + "\", display " + mDisplayId - + ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}"; - } - } - } diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java index ee5a43ce0edb..54a63a168588 100644 --- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java @@ -70,6 +70,7 @@ class ActivityStartInterceptor { private final ActivityTaskManagerService mService; private final ActivityStackSupervisor mSupervisor; + private final RootActivityContainer mRootActivityContainer; private final Context mServiceContext; // UserManager cannot be final as it's not ready when this class is instantiated during boot @@ -102,14 +103,15 @@ class ActivityStartInterceptor { ActivityStartInterceptor( ActivityTaskManagerService service, ActivityStackSupervisor supervisor) { - this(service, supervisor, service.mContext); + this(service, supervisor, service.mRootActivityContainer, service.mContext); } @VisibleForTesting ActivityStartInterceptor(ActivityTaskManagerService service, ActivityStackSupervisor supervisor, - Context context) { + RootActivityContainer root, Context context) { mService = service; mSupervisor = supervisor; + mRootActivityContainer = root; mServiceContext = context; } @@ -279,7 +281,7 @@ class ActivityStartInterceptor { mActivityOptions = ActivityOptions.makeBasic(); } - ActivityRecord homeActivityRecord = mSupervisor.getDefaultDisplayHomeActivity(); + ActivityRecord homeActivityRecord = mRootActivityContainer.getDefaultDisplayHomeActivity(); if (homeActivityRecord != null && homeActivityRecord.getTask() != null) { // Showing credential confirmation activity in home task to avoid stopping multi-windowed // mode after showing the full-screen credential confirmation activity. diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 90f3ff84a027..c4421bac9bba 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -98,6 +98,7 @@ import android.graphics.Rect; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; +import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; @@ -108,6 +109,7 @@ import android.text.TextUtils; import android.util.EventLog; import android.util.Pools.SynchronizedPool; import android.util.Slog; +import android.widget.Toast; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.HeavyWeightSwitcherActivity; @@ -137,6 +139,7 @@ class ActivityStarter { private static final int INVALID_LAUNCH_MODE = -1; private final ActivityTaskManagerService mService; + private final RootActivityContainer mRootActivityContainer; private final ActivityStackSupervisor mSupervisor; private final ActivityStartInterceptor mInterceptor; private final ActivityStartController mController; @@ -421,6 +424,7 @@ class ActivityStarter { ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) { mController = controller; mService = service; + mRootActivityContainer = service.mRootActivityContainer; mSupervisor = supervisor; mInterceptor = interceptor; reset(true); @@ -617,7 +621,7 @@ class ActivityStarter { ActivityRecord sourceRecord = null; ActivityRecord resultRecord = null; if (resultTo != null) { - sourceRecord = mSupervisor.isInAnyStackLocked(resultTo); + sourceRecord = mRootActivityContainer.isInAnyStack(resultTo); if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "Will send result to " + resultTo + " " + sourceRecord); if (sourceRecord != null) { @@ -731,6 +735,13 @@ class ActivityStarter { abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid, callingPid, resolvedType, aInfo.applicationInfo); + // not sure if we need to create START_ABORTED_BACKGROUND so for now piggybacking + // on START_ABORTED + if (!abort) { + abort |= shouldAbortBackgroundActivityStart(callingUid, callingPackage, realCallingUid, + callerApp); + } + // Merge the two options bundles, while realCallerOptions takes precedence. ActivityOptions checkedOptions = options != null ? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null; @@ -774,6 +785,8 @@ class ActivityStarter { // We pretend to the caller that it was really started, but // they will just get a cancel result. ActivityOptions.abort(checkedOptions); + maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp, + null /*r*/, originatingPendingIntent, true /*abortedStart*/); return START_ABORTED; } @@ -811,7 +824,8 @@ class ActivityStarter { null /*profilerInfo*/); if (DEBUG_PERMISSIONS_REVIEW) { - final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack(); + final ActivityStack focusedStack = + mRootActivityContainer.getTopDisplayFocusedStack(); Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false) + "} from uid " + callingUid + " on display " + (focusedStack == null ? DEFAULT_DISPLAY : focusedStack.mDisplayId)); @@ -847,7 +861,7 @@ class ActivityStarter { r.appTimeTracker = sourceRecord.appTimeTracker; } - final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack(); + final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack(); // If we are starting an activity that is not from the same uid as the currently resumed // one, check whether app switches are allowed. @@ -866,19 +880,60 @@ class ActivityStarter { mController.doPendingActivityLaunches(false); maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp, r, - originatingPendingIntent); + originatingPendingIntent, false /*abortedStart*/); return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask, outActivity); } + private boolean shouldAbortBackgroundActivityStart(int callingUid, final String callingPackage, + int realCallingUid, WindowProcessController callerApp) { + if (mService.isBackgroundActivityStartsEnabled()) { + return false; + } + // don't abort for the most important UIDs + if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID) { + return false; + } + // don't abort if the callerApp has any visible activity + if (callerApp != null && callerApp.hasForegroundActivities()) { + return false; + } + // don't abort if the callingUid is in the foreground + if (isUidForeground(callingUid)) { + return false; + } + // don't abort if the realCallingUid is in the foreground and callingUid isn't + if ((realCallingUid != callingUid) && isUidForeground(realCallingUid)) { + return false; + } + // don't abort if the caller has the same uid as the recents component + if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) { + return false; + } + // anything that has fallen through will currently be aborted + // TODO: remove this toast after feature development is done + mService.mUiHandler.post(() -> { + Toast.makeText(mService.mContext, + "Blocking background activity start for " + callingPackage, + Toast.LENGTH_SHORT).show(); + }); + return true; + } + + /** Returns true if uid has a visible window or its process is in top or persistent state. */ + private boolean isUidForeground(int uid) { + return (mService.getUidStateLocked(uid) <= ActivityManager.PROCESS_STATE_TOP) + || mService.mWindowManager.isAnyWindowVisibleForUid(uid); + } + private void maybeLogActivityStart(int callingUid, String callingPackage, int realCallingUid, Intent intent, WindowProcessController callerApp, ActivityRecord r, - PendingIntentRecord originatingPendingIntent) { + PendingIntentRecord originatingPendingIntent, boolean abortedStart) { boolean callerAppHasForegroundActivity = callerApp != null && callerApp.hasForegroundActivities(); if (!mService.isActivityStartsLoggingEnabled() || callerAppHasForegroundActivity - || r == null) { + || (!abortedStart && r == null)) { // skip logging in this case return; } @@ -894,8 +949,8 @@ class ActivityStarter { final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid) ? callingUidHasAnyVisibleWindow : mService.mWindowManager.isAnyWindowVisibleForUid(realCallingUid); - final String targetPackage = r.packageName; - final int targetUid = (r.appInfo != null) ? r.appInfo.uid : -1; + final String targetPackage = (r != null) ? r.packageName : null; + final int targetUid = (r!= null) ? ((r.appInfo != null) ? r.appInfo.uid : -1) : -1; final int targetUidProcState = mService.getUidStateLocked(targetUid); final boolean targetUidHasAnyVisibleWindow = (targetUid != -1) ? mService.mWindowManager.isAnyWindowVisibleForUid(targetUid) @@ -1063,7 +1118,7 @@ class ActivityStarter { ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo); synchronized (mService.mGlobalLock) { - final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack(); + final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack(); stack.mConfigWillChange = globalConfig != null && mService.getGlobalConfiguration().diff(globalConfig) != 0; if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, @@ -1249,7 +1304,8 @@ class ActivityStarter { final ActivityRecord currentTop = startedActivityStack.topRunningActivityLocked(); if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) { - mSupervisor.ensureVisibilityAndConfig(currentTop, currentTop.getDisplayId(), + mRootActivityContainer.ensureVisibilityAndConfig( + currentTop, currentTop.getDisplayId(), true /* markFrozenIfConfigChanged */, false /* deferResume */); } } @@ -1284,7 +1340,7 @@ class ActivityStarter { // Do not start home activity if it cannot be launched on preferred display. We are not // doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might // fallback to launch on other displays. - if (r.isActivityTypeHome() && !mSupervisor.canStartHomeOnDisplay(r.info, + if (r.isActivityTypeHome() && !mRootActivityContainer.canStartHomeOnDisplay(r.info, mPreferredDisplayId, true /* allowInstrumenting */)) { Slog.w(TAG, "Cannot launch home on display " + mPreferredDisplayId); return START_CANCELED; @@ -1361,7 +1417,8 @@ class ActivityStarter { } } - mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, reusedActivity); + mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded + (false /* forceSend */, reusedActivity); reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity); @@ -1413,7 +1470,7 @@ class ActivityStarter { // If the activity being launched is the same as the one currently at the top, then // we need to check if it should only be launched once. - final ActivityStack topStack = mSupervisor.getTopDisplayFocusedStack(); + final ActivityStack topStack = mRootActivityContainer.getTopDisplayFocusedStack(); final ActivityRecord topFocused = topStack.getTopActivity(); final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop); final boolean dontStart = top != null && mStartActivity.resultTo == null @@ -1430,7 +1487,7 @@ class ActivityStarter { // For paranoia, make sure we have correctly resumed the top activity. topStack.mLastPausedActivity = null; if (mDoResume) { - mSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } ActivityOptions.abort(mOptions); if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) { @@ -1485,7 +1542,8 @@ class ActivityStarter { EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTask()); mTargetStack.mLastPausedActivity = null; - mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, mStartActivity); + mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded( + false /* forceSend */, mStartActivity); mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition, mOptions); @@ -1512,16 +1570,16 @@ class ActivityStarter { // task stack to be focusable, then ensure that we now update the focused stack // accordingly. if (mTargetStack.isFocusable() - && !mSupervisor.isTopDisplayFocusedStack(mTargetStack)) { + && !mRootActivityContainer.isTopDisplayFocusedStack(mTargetStack)) { mTargetStack.moveToFront("startActivityUnchecked"); } - mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, mStartActivity, - mOptions); + mRootActivityContainer.resumeFocusedStacksTopActivities( + mTargetStack, mStartActivity, mOptions); } } else if (mStartActivity != null) { mSupervisor.mRecentTasks.add(mStartActivity.getTask()); } - mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack); + mRootActivityContainer.updateUserStack(mStartActivity.userId, mTargetStack); mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode, mPreferredDisplayId, mTargetStack); @@ -1642,7 +1700,7 @@ class ActivityStarter { if (mOptions.getLaunchTaskId() != -1 && mOptions.getTaskOverlay()) { r.mTaskOverlay = true; if (!mOptions.canTaskOverlayResume()) { - final TaskRecord task = mSupervisor.anyTaskForIdLocked( + final TaskRecord task = mRootActivityContainer.anyTaskForId( mOptions.getLaunchTaskId()); final ActivityRecord top = task != null ? task.getTopActivity() : null; if (top != null && !top.isState(RESUMED)) { @@ -1678,7 +1736,7 @@ class ActivityStarter { if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) { ActivityRecord checkedCaller = sourceRecord; if (checkedCaller == null) { - checkedCaller = mSupervisor.getTopDisplayFocusedStack() + checkedCaller = mRootActivityContainer.getTopDisplayFocusedStack() .topRunningNonDelayedActivityLocked(mNotTop); } if (!checkedCaller.realActivity.equals(r.realActivity)) { @@ -1840,22 +1898,23 @@ class ActivityStarter { putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null; ActivityRecord intentActivity = null; if (mOptions != null && mOptions.getLaunchTaskId() != -1) { - final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId()); + final TaskRecord task = mRootActivityContainer.anyTaskForId(mOptions.getLaunchTaskId()); intentActivity = task != null ? task.getTopActivity() : null; } else if (putIntoExistingTask) { if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) { // There can be one and only one instance of single instance activity in the // history, and it is always in its own unique task, so we do a special search. - intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info, + intentActivity = mRootActivityContainer.findActivity(mIntent, mStartActivity.info, mStartActivity.isActivityTypeHome()); } else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) { // For the launch adjacent case we only want to put the activity in an existing // task if the activity already exists in the history. - intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info, + intentActivity = mRootActivityContainer.findActivity(mIntent, mStartActivity.info, !(LAUNCH_SINGLE_TASK == mLaunchMode)); } else { // Otherwise find the best task to put the activity in. - intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId); + intentActivity = + mRootActivityContainer.findTask(mStartActivity, mPreferredDisplayId); } } @@ -2067,11 +2126,11 @@ class ActivityStarter { private void resumeTargetStackIfNeeded() { if (mDoResume) { - mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, null, mOptions); + mRootActivityContainer.resumeFocusedStacksTopActivities(mTargetStack, null, mOptions); } else { ActivityOptions.abort(mOptions); } - mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack); + mRootActivityContainer.updateUserStack(mStartActivity.userId, mTargetStack); } private int setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate) { @@ -2145,13 +2204,13 @@ class ActivityStarter { // be not suitable. Let's check other displays. if (mTargetStack == null && targetDisplayId != sourceStack.mDisplayId) { // Can't use target display, lets find a stack on the source display. - mTargetStack = mSupervisor.getValidLaunchStackOnDisplay( + mTargetStack = mRootActivityContainer.getValidLaunchStackOnDisplay( sourceStack.mDisplayId, mStartActivity, mOptions, mLaunchParams); } if (mTargetStack == null) { // There are no suitable stacks on the target and source display(s). Look on all // displays. - mTargetStack = mSupervisor.getNextValidLaunchStackLocked( + mTargetStack = mRootActivityContainer.getNextValidLaunchStack( mStartActivity, -1 /* currentFocus */); } } @@ -2182,7 +2241,7 @@ class ActivityStarter { // For paranoia, make sure we have correctly resumed the top activity. mTargetStack.mLastPausedActivity = null; if (mDoResume) { - mSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } ActivityOptions.abort(mOptions); return START_DELIVERED_TO_TOP; @@ -2200,7 +2259,7 @@ class ActivityStarter { deliverNewIntent(top); mTargetStack.mLastPausedActivity = null; if (mDoResume) { - mSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } return START_DELIVERED_TO_TOP; } @@ -2254,7 +2313,8 @@ class ActivityStarter { if (!mLaunchParams.mBounds.isEmpty()) { // TODO: Shouldn't we already know what stack to use by the time we get here? - ActivityStack stack = mSupervisor.getLaunchStack(null, null, mInTask, ON_TOP); + ActivityStack stack = mRootActivityContainer.getLaunchStack( + null, null, mInTask, ON_TOP); if (stack != mInTask.getStack()) { mInTask.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, DEFER_RESUME, "inTaskToFront"); @@ -2348,7 +2408,7 @@ class ActivityStarter { } final ActivityStack currentStack = task != null ? task.getStack() : null; - final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack(); + final ActivityStack focusedStack = mRootActivityContainer.getTopDisplayFocusedStack(); if (currentStack != null) { if (focusedStack != currentStack) { if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, @@ -2369,18 +2429,18 @@ class ActivityStarter { if (mPreferredDisplayId != DEFAULT_DISPLAY) { // Try to put the activity in a stack on a secondary display. - stack = mSupervisor.getValidLaunchStackOnDisplay(mPreferredDisplayId, r, aOptions, - mLaunchParams); + stack = mRootActivityContainer.getValidLaunchStackOnDisplay( + mPreferredDisplayId, r, aOptions, mLaunchParams); if (stack == null) { // If source display is not suitable - look for topmost valid stack in the system. if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: Can't launch on mPreferredDisplayId=" + mPreferredDisplayId + ", looking on all displays."); - stack = mSupervisor.getNextValidLaunchStackLocked(r, mPreferredDisplayId); + stack = mRootActivityContainer.getNextValidLaunchStack(r, mPreferredDisplayId); } } if (stack == null) { - stack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP); + stack = mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP); } if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r=" + r + " stackId=" + stack.mStackId); @@ -2390,7 +2450,7 @@ class ActivityStarter { /** Check if provided activity record can launch in currently focused stack. */ // TODO: This method can probably be consolidated into getLaunchStack() below. private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) { - final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack(); + final ActivityStack focusedStack = mRootActivityContainer.getTopDisplayFocusedStack(); final boolean canUseFocusedStack; if (focusedStack.isActivityTypeAssistant()) { canUseFocusedStack = r.isActivityTypeAssistant(); @@ -2436,14 +2496,14 @@ class ActivityStarter { // full resolution. mLaunchParams.mPreferredDisplayId = mPreferredDisplayId != DEFAULT_DISPLAY ? mPreferredDisplayId : INVALID_DISPLAY; - final ActivityStack stack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP, - mLaunchParams); + final ActivityStack stack = + mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP, mLaunchParams); mLaunchParams.mPreferredDisplayId = mPreferredDisplayId; return stack; } // Otherwise handle adjacent launch. - final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack(); + final ActivityStack focusedStack = mRootActivityContainer.getTopDisplayFocusedStack(); // The parent activity doesn't want to launch the activity on top of itself, but // instead tries to put it onto other side in side-by-side mode. final ActivityStack parentStack = task != null ? task.getStack(): focusedStack; @@ -2461,7 +2521,8 @@ class ActivityStarter { if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) { // If parent was in docked stack, the natural place to launch another activity // will be fullscreen, so it can appear alongside the docked window. - final int activityType = mSupervisor.resolveActivityType(r, mOptions, task); + final int activityType = + mRootActivityContainer.resolveActivityType(r, mOptions, task); return parentStack.getDisplay().getOrCreateStack( WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, activityType, ON_TOP); } else { @@ -2469,10 +2530,10 @@ class ActivityStarter { // and if yes, we will launch into that stack. If not, we just put the new // activity into parent's stack, because we can't find a better place. final ActivityStack dockedStack = - mSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack(); + mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack(); if (dockedStack != null && !dockedStack.shouldBeVisible(r)) { // There is a docked stack, but it isn't visible, so we can't launch into that. - return mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP); + return mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP); } else { return dockedStack; } @@ -2660,7 +2721,7 @@ class ActivityStarter { prefix = prefix + " "; pw.print(prefix); pw.print("mCurrentUser="); - pw.println(mSupervisor.mCurrentUser); + pw.println(mRootActivityContainer.mCurrentUser); pw.print(prefix); pw.print("mLastStartReason="); pw.println(mLastStartReason); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index d6655928105e..0cdbedba7318 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -473,4 +473,6 @@ public abstract class ActivityTaskManagerInternal { public abstract void setProfileApp(String profileApp); public abstract void setProfileProc(WindowProcessController wpc); public abstract void setProfilerInfo(ProfilerInfo profilerInfo); + + public abstract ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry(); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index d0e3fb47730e..e1a1e6125104 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -91,8 +91,6 @@ import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.Scr .PACKAGE; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME; -import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY; -import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS; @@ -122,6 +120,8 @@ import static com.android.server.wm.ActivityTaskManagerService.H.REPORT_TIME_TRA import static com.android.server.wm.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; +import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_ONLY; +import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK; import static com.android.server.wm.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE; @@ -352,6 +352,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { /* Global service lock used by the package the owns this service. */ final WindowManagerGlobalLock mGlobalLock = new WindowManagerGlobalLock(); ActivityStackSupervisor mStackSupervisor; + RootActivityContainer mRootActivityContainer; WindowManagerService mWindowManager; private UserManagerService mUserManager; private AppOpsService mAppOpsService; @@ -766,7 +767,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mTempConfig.setLocales(LocaleList.getDefault()); mConfigurationSeq = mTempConfig.seq = 1; mStackSupervisor = createStackSupervisor(); - mStackSupervisor.onConfigurationChanged(mTempConfig); + mRootActivityContainer = new RootActivityContainer(this); + mRootActivityContainer.onConfigurationChanged(mTempConfig); mTaskChangeNotificationController = new TaskChangeNotificationController(mGlobalLock, mStackSupervisor, mH); @@ -801,6 +803,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mWindowManager = wm; mLockTaskController.setWindowManager(wm); mStackSupervisor.setWindowManager(wm); + mRootActivityContainer.setWindowManager(wm); } } @@ -1255,7 +1258,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { sourceToken = resultTo; } - sourceRecord = mStackSupervisor.isInAnyStackLocked(sourceToken); + sourceRecord = mRootActivityContainer.isInAnyStack(sourceToken); if (sourceRecord == null) { throw new SecurityException("Called with bad activity token: " + sourceToken); } @@ -1799,7 +1802,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final boolean translucentChanged = r.changeWindowTranslucency(true); if (translucentChanged) { - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); } mWindowManager.setAppFullscreen(token, true); return translucentChanged; @@ -1829,7 +1832,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (translucentChanged) { r.getStack().convertActivityToTranslucent(r); } - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); mWindowManager.setAppFullscreen(token, false); return translucentChanged; } @@ -1842,7 +1845,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public void notifyActivityDrawn(IBinder token) { if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "notifyActivityDrawn: token=" + token); synchronized (mGlobalLock) { - ActivityRecord r = mStackSupervisor.isInAnyStackLocked(token); + ActivityRecord r = mRootActivityContainer.isInAnyStack(token); if (r != null) { r.getStack().notifyActivityDrawnLocked(r); } @@ -1879,7 +1882,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { ActivityStack focusedStack = getTopDisplayFocusedStack(); if (focusedStack != null) { - return mStackSupervisor.getStackInfo(focusedStack.mStackId); + return mRootActivityContainer.getStackInfo(focusedStack.mStackId); } return null; } @@ -1895,14 +1898,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final long callingId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - final ActivityStack stack = mStackSupervisor.getStack(stackId); + final ActivityStack stack = mRootActivityContainer.getStack(stackId); if (stack == null) { Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId); return; } final ActivityRecord r = stack.topRunningActivityLocked(); if (r != null && r.moveFocusableActivityToTop("setFocusedStack")) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } } } finally { @@ -1917,14 +1920,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final long callingId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY); if (task == null) { return; } final ActivityRecord r = task.topRunningActivityLocked(); if (r != null && r.moveFocusableActivityToTop("setFocusedTask")) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } } } finally { @@ -2009,7 +2012,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final long origId = Binder.clearCallingIdentity(); try { int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot); - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId); if (task != null) { return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId); } @@ -2027,7 +2030,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { Rect rect = new Rect(); try { synchronized (mGlobalLock) { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (task == null) { Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found"); @@ -2058,7 +2061,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { enforceCallerIsRecentsOrHasPermission( MANAGE_ACTIVITY_STACKS, "getTaskDescription()"); - final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id, + final TaskRecord tr = mRootActivityContainer.anyTaskForId(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (tr != null) { return tr.lastTaskDescription; @@ -2078,7 +2081,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY); if (task == null) { Slog.w(TAG, "setTaskWindowingMode: No task for id=" + taskId); @@ -2167,7 +2170,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final long origId = Binder.clearCallingIdentity(); try { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId); if (task == null) { Slog.d(TAG, "Could not find task for id: "+ taskId); SafeActivityOptions.abort(options); @@ -2284,7 +2287,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final boolean allowed = isGetTasksAllowed("getTasks", Binder.getCallingPid(), callingUid); - mStackSupervisor.getRunningTasks(maxNum, list, ignoreActivityType, + mRootActivityContainer.getRunningTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, callingUid, allowed); } @@ -2320,7 +2323,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId); if (task == null) { Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId); return; @@ -2329,7 +2332,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId + " to stackId=" + stackId + " toTop=" + toTop); - final ActivityStack stack = mStackSupervisor.getStack(stackId); + final ActivityStack stack = mRootActivityContainer.getStack(stackId); if (stack == null) { throw new IllegalStateException( "moveTaskToStack: No stack for stackId=" + stackId); @@ -2359,7 +2362,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { synchronized (mGlobalLock) { if (animate) { - final PinnedActivityStack stack = mStackSupervisor.getStack(stackId); + final PinnedActivityStack stack = mRootActivityContainer.getStack(stackId); if (stack == null) { Slog.w(TAG, "resizeStack: stackId " + stackId + " not found."); return; @@ -2371,12 +2374,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds, animationDuration, false /* fromFullscreen */); } else { - final ActivityStack stack = mStackSupervisor.getStack(stackId); + final ActivityStack stack = mRootActivityContainer.getStack(stackId); if (stack == null) { Slog.w(TAG, "resizeStack: stackId " + stackId + " not found."); return; } - mStackSupervisor.resizeStackLocked(stack, destBounds, + mRootActivityContainer.resizeStack(stack, destBounds, null /* tempTaskBounds */, null /* tempTaskInsetBounds */, preserveWindows, allowResizeInDockedMode, !DEFER_RESUME); } @@ -2410,7 +2413,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY); if (task == null) { Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId); @@ -2452,7 +2455,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { - mStackSupervisor.removeStacksInWindowingModes(windowingModes); + mRootActivityContainer.removeStacksInWindowingModes(windowingModes); } finally { Binder.restoreCallingIdentity(ident); } @@ -2467,7 +2470,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { - mStackSupervisor.removeStacksWithActivityTypes(activityTypes); + mRootActivityContainer.removeStacksWithActivityTypes(activityTypes); } finally { Binder.restoreCallingIdentity(ident); } @@ -2498,7 +2501,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - return mStackSupervisor.getAllStackInfosLocked(); + return mRootActivityContainer.getAllStackInfos(); } } finally { Binder.restoreCallingIdentity(ident); @@ -2511,7 +2514,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - return mStackSupervisor.getStackInfo(windowingMode, activityType); + return mRootActivityContainer.getStackInfo(windowingMode, activityType); } } finally { Binder.restoreCallingIdentity(ident); @@ -2553,7 +2556,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY); if (task == null) { return; @@ -2595,7 +2598,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return; } - final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack(); + final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack(); if (stack == null || task != stack.topTask()) { throw new IllegalArgumentException("Invalid task, not in foreground"); } @@ -2610,7 +2613,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { long ident = Binder.clearCallingIdentity(); try { // When a task is locked, dismiss the pinned stack if it exists - mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED); + mRootActivityContainer.removeStacksInWindowingModes(WINDOWING_MODE_PINNED); getLockTaskController().startLockTaskMode(task, isSystemCaller, callingUid); } finally { @@ -2712,7 +2715,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { // TODO: VI Consider treating local voice interactions and voice tasks // differently here - mStackSupervisor.finishVoiceTask(session); + mRootActivityContainer.finishVoiceTask(session); } finally { Binder.restoreCallingIdentity(origId); } @@ -2902,7 +2905,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void setTaskResizeable(int taskId, int resizeableMode) { synchronized (mGlobalLock) { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked( + final TaskRecord task = mRootActivityContainer.anyTaskForId( taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (task == null) { Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found"); @@ -2918,7 +2921,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY); if (task == null) { Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found"); @@ -2983,7 +2986,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final long origId = Binder.clearCallingIdentity(); try { final WindowProcessController app = getProcessController(appInt); - mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem"); + mRootActivityContainer.releaseSomeActivitiesLocked(app, "low-mem"); } finally { Binder.restoreCallingIdentity(origId); } @@ -3077,7 +3080,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { - final ActivityStack stack = mStackSupervisor.getStack(stackId); + final ActivityStack stack = mRootActivityContainer.getStack(stackId); if (stack == null) { Slog.w(TAG, "removeStack: No stack with id=" + stackId); return; @@ -3102,7 +3105,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { if (DEBUG_STACK) Slog.d(TAG_STACK, "moveStackToDisplay: moving stackId=" + stackId + " to displayId=" + displayId); - mStackSupervisor.moveStackToDisplayLocked(stackId, displayId, ON_TOP); + mRootActivityContainer.moveStackToDisplay(stackId, displayId, ON_TOP); } finally { Binder.restoreCallingIdentity(ident); } @@ -3564,13 +3567,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { if (DEBUG_STACK) Slog.d(TAG_STACK, "positionTaskInStack: positioning task=" + taskId + " in stackId=" + stackId + " at position=" + position); - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId); if (task == null) { throw new IllegalArgumentException("positionTaskInStack: no task for id=" + taskId); } - final ActivityStack stack = mStackSupervisor.getStack(stackId); + final ActivityStack stack = mRootActivityContainer.getStack(stackId); if (stack == null) { throw new IllegalArgumentException("positionTaskInStack: no stack for id=" @@ -3625,7 +3628,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { synchronized (mGlobalLock) { final ActivityStack stack = - mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack(); + mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack(); if (stack == null) { Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found."); return; @@ -3635,7 +3638,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // Caller wants the current split-screen primary stack to be the top stack after // it goes fullscreen, so move it to the front. stack.moveToFront("dismissSplitScreenMode"); - } else if (mStackSupervisor.isTopDisplayFocusedStack(stack)) { + } else if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) { // In this case the current split-screen primary stack shouldn't be the top // stack after it goes fullscreen, but it current has focus, so we move the // focus to the top-most split-screen secondary stack next to it. @@ -3666,7 +3669,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { synchronized (mGlobalLock) { final PinnedActivityStack stack = - mStackSupervisor.getDefaultDisplay().getPinnedStack(); + mRootActivityContainer.getDefaultDisplay().getPinnedStack(); if (stack == null) { Slog.w(TAG, "dismissPip: pinned stack not found."); return; @@ -3708,7 +3711,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final long origId = Binder.clearCallingIdentity(); try { - final ActivityStack stack = mStackSupervisor.getStack(fromStackId); + final ActivityStack stack = mRootActivityContainer.getStack(fromStackId); if (stack != null){ if (!stack.isActivityTypeStandardOrUndefined()) { throw new IllegalArgumentException( @@ -3743,7 +3746,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { long ident = Binder.clearCallingIdentity(); try { - return mStackSupervisor.moveTopStackActivityToPinnedStackLocked(stackId, bounds); + return mRootActivityContainer.moveTopStackActivityToPinnedStack(stackId); } finally { Binder.restoreCallingIdentity(ident); } @@ -3821,7 +3824,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // Adjust the source bounds by the insets for the transition down final Rect sourceBounds = new Rect( r.pictureInPictureArgs.getSourceRectHint()); - mStackSupervisor.moveActivityToPinnedStackLocked( + mRootActivityContainer.moveActivityToPinnedStack( r, sourceBounds, aspectRatio, "enterPictureInPictureMode"); final PinnedActivityStack stack = r.getStack(); stack.setPictureInPictureAspectRatio(aspectRatio); @@ -4100,7 +4103,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { // Check if display is initialized in AM. - if (!mStackSupervisor.isDisplayAdded(displayId)) { + if (!mRootActivityContainer.isDisplayAdded(displayId)) { // Call might come when display is not yet added or has already been removed. if (DEBUG_CONFIGURATION) { Slog.w(TAG, "Trying to update display configuration for non-existing displayId=" @@ -4190,7 +4193,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY); if (task == null) { Slog.w(TAG, "cancelTaskWindowTransition: taskId=" + taskId + " not found"); @@ -4210,7 +4213,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { final TaskRecord task; synchronized (mGlobalLock) { - task = mStackSupervisor.anyTaskForIdLocked(taskId, + task = mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (task == null) { Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found"); @@ -4430,7 +4433,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) { Slog.i(TAG, "Moving " + r.shortComponentName + " from stack " + r.getStackId() + " to main stack for VR"); - final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getOrCreateStack( + final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().getOrCreateStack( WINDOWING_MODE_FULLSCREEN, r.getActivityType(), true /* toTop */); moveTaskToStack(r.getTask().taskId, stack.mStackId, true /* toTop */); } @@ -4444,7 +4447,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (disableNonVrUi) { // If we are in a VR mode where Picture-in-Picture mode is unsupported, // then remove the pinned stack. - mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED); + mRootActivityContainer.removeStacksInWindowingModes(WINDOWING_MODE_PINNED); } } }); @@ -4496,7 +4499,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } ActivityStack getTopDisplayFocusedStack() { - return mStackSupervisor.getTopDisplayFocusedStack(); + return mRootActivityContainer.getTopDisplayFocusedStack(); } /** Pokes the task persister. */ @@ -4508,6 +4511,21 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return mKeyguardController.isKeyguardLocked(); } + /** + * Clears launch params for the given package. + * @param packageNames the names of the packages of which the launch params are to be cleared + */ + @Override + public void clearLaunchParamsForPackages(List<String> packageNames) { + mAmInternal.enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS, + "clearLaunchParamsForPackages"); + synchronized (mGlobalLock) { + for (int i = 0; i < packageNames.size(); ++i) { + mStackSupervisor.mLaunchParamsPersister.removeRecordForPackage(packageNames.get(i)); + } + } + } + void dumpLastANRLocked(PrintWriter pw) { pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)"); if (mLastANRState == null) { @@ -4557,12 +4575,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { int opti, boolean dumpAll, boolean dumpClient, String dumpPackage, String header) { pw.println(header); - boolean printedAnything = mStackSupervisor.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, + boolean printedAnything = mRootActivityContainer.dumpActivities(fd, pw, dumpAll, dumpClient, dumpPackage); boolean needSep = printedAnything; boolean printed = ActivityStackSupervisor.printThisActivity(pw, - mStackSupervisor.getTopResumedActivity(), dumpPackage, needSep, + mRootActivityContainer.getTopResumedActivity(), dumpPackage, needSep, " ResumedActivity: "); if (printed) { printedAnything = true; @@ -4584,7 +4602,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { void dumpActivityContainersLocked(PrintWriter pw) { pw.println("ACTIVITY MANAGER STARTER (dumpsys activity containers)"); - mStackSupervisor.dumpChildrenNames(pw, " "); + mRootActivityContainer.dumpChildrenNames(pw, " "); pw.println(" "); } @@ -4608,7 +4626,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { ArrayList<ActivityRecord> activities; synchronized (mGlobalLock) { - activities = mStackSupervisor.getDumpActivitiesLocked(name, dumpVisibleStacksOnly, + activities = mRootActivityContainer.getDumpActivities(name, dumpVisibleStacksOnly, dumpFocusedStackOnly); } @@ -4683,7 +4701,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } void writeSleepStateToProto(ProtoOutputStream proto) { - for (ActivityTaskManagerInternal.SleepToken st : mStackSupervisor.mSleepTokens) { + for (ActivityTaskManagerInternal.SleepToken st : mRootActivityContainer.mSleepTokens) { proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS, st.toString()); } @@ -4728,7 +4746,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { * also corresponds to the merged configuration of the default display. */ Configuration getGlobalConfiguration() { - return mStackSupervisor.getConfiguration(); + return mRootActivityContainer.getConfiguration(); } boolean updateConfigurationLocked(Configuration values, ActivityRecord starting, @@ -4860,7 +4878,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mTempConfig.seq = increaseConfigurationSeqLocked(); // Update stored global config and notify everyone about the change. - mStackSupervisor.onConfigurationChanged(mTempConfig); + mRootActivityContainer.onConfigurationChanged(mTempConfig); Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig); // TODO(multi-display): Update UsageEvents#Event to include displayId. @@ -4907,7 +4925,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // Override configuration of the default display duplicates global config, so we need to // update it also. This will also notify WindowManager about changes. - performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume, + performDisplayOverrideConfigUpdate(mRootActivityContainer.getConfiguration(), deferResume, DEFAULT_DISPLAY); return changes; @@ -4961,12 +4979,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume, int displayId) { - mTempConfig.setTo(mStackSupervisor.getDisplayOverrideConfiguration(displayId)); + mTempConfig.setTo(mRootActivityContainer.getDisplayOverrideConfiguration(displayId)); final int changes = mTempConfig.updateFrom(values); if (changes != 0) { Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " " + mTempConfig + " for displayId=" + displayId); - mStackSupervisor.setDisplayOverrideConfiguration(mTempConfig, displayId); + mRootActivityContainer.setDisplayOverrideConfiguration(mTempConfig, displayId); final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0; if (isDensityChange && displayId == DEFAULT_DISPLAY) { @@ -5016,6 +5034,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return mAmInternal.isActivityStartsLoggingEnabled(); } + boolean isBackgroundActivityStartsEnabled() { + return mAmInternal.isBackgroundActivityStartsEnabled(); + } + void enableScreenAfterBoot(boolean booted) { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN, SystemClock.uptimeMillis()); @@ -5096,7 +5118,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mCurAppTimeTracker.stop(); mH.obtainMessage( REPORT_TIME_TRACKER_MSG, mCurAppTimeTracker).sendToTarget(); - mStackSupervisor.clearOtherAppTimeTrackers(r.appTimeTracker); + mRootActivityContainer.clearOtherAppTimeTrackers(r.appTimeTracker); mCurAppTimeTracker = null; } if (r.appTimeTracker != null) { @@ -5157,14 +5179,15 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { ActivityTaskManagerInternal.SleepToken acquireSleepToken(String tag, int displayId) { synchronized (mGlobalLock) { - final ActivityTaskManagerInternal.SleepToken token = mStackSupervisor.createSleepTokenLocked(tag, displayId); + final ActivityTaskManagerInternal.SleepToken token = + mRootActivityContainer.createSleepToken(tag, displayId); updateSleepIfNeededLocked(); return token; } } void updateSleepIfNeededLocked() { - final boolean shouldSleep = !mStackSupervisor.hasAwakeDisplay(); + final boolean shouldSleep = !mRootActivityContainer.hasAwakeDisplay(); final boolean wasSleeping = mSleeping; boolean updateOomAdj = false; @@ -5180,7 +5203,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mTopProcessState = ActivityManager.PROCESS_STATE_TOP; mStackSupervisor.comeOutOfSleepIfNeededLocked(); } - mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */); + mRootActivityContainer.applySleepTokens(true /* applyToStacks */); if (wasSleeping) { updateOomAdj = true; } @@ -5356,7 +5379,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // TODO(b/111541062): Update app time tracking to make it aware of multiple resumed activities private void startTimeTrackingFocusedActivityLocked() { - final ActivityRecord resumedActivity = mStackSupervisor.getTopResumedActivity(); + final ActivityRecord resumedActivity = mRootActivityContainer.getTopResumedActivity(); if (!mSleeping && mCurAppTimeTracker != null && resumedActivity != null) { mCurAppTimeTracker.start(resumedActivity.packageName); } @@ -5381,7 +5404,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { /** Applies latest configuration and/or visibility updates if needed. */ private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) { boolean kept = true; - final ActivityStack mainStack = mStackSupervisor.getTopDisplayFocusedStack(); + final ActivityStack mainStack = mRootActivityContainer.getTopDisplayFocusedStack(); // mainStack is null during startup. if (mainStack != null) { if (changes != 0 && starting == null) { @@ -5396,7 +5419,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { false /* preserveWindow */); // And we need to make sure at this point that all other activities // are made visible with the correct configuration. - mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes, + mRootActivityContainer.ensureActivitiesVisible(starting, changes, !PRESERVE_WINDOWS); } } @@ -5612,8 +5635,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public ComponentName getHomeActivityForUser(int userId) { synchronized (mGlobalLock) { - ActivityRecord homeActivity = - mStackSupervisor.getDefaultDisplayHomeActivityForUser(userId); + final ActivityRecord homeActivity = + mRootActivityContainer.getDefaultDisplayHomeActivityForUser(userId); return homeActivity == null ? null : homeActivity.realActivity; } } @@ -5651,14 +5674,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public List<IBinder> getTopVisibleActivities() { synchronized (mGlobalLock) { - return mStackSupervisor.getTopVisibleActivities(); + return mRootActivityContainer.getTopVisibleActivities(); } } @Override public void notifyDockedStackMinimizedChanged(boolean minimized) { synchronized (mGlobalLock) { - mStackSupervisor.setDockedStackMinimized(minimized); + mRootActivityContainer.setDockedStackMinimized(minimized); } } @@ -5739,7 +5762,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // We might change the visibilities here, so prepare an empty app transition which // might be overridden later if we actually change visibilities. final ActivityDisplay activityDisplay = - mStackSupervisor.getActivityDisplay(displayId); + mRootActivityContainer.getActivityDisplay(displayId); if (activityDisplay == null) { return; } @@ -5748,7 +5771,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (!wasTransitionSet) { dwc.prepareAppTransition(TRANSIT_NONE, false /* alwaysKeepCurrent */); } - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); // If there was a transition set already we don't want to interfere with it as we // might be starting it too early. @@ -5765,7 +5788,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public void notifyKeyguardTrustedChanged() { synchronized (mGlobalLock) { if (mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) { - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); } } } @@ -5792,7 +5815,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { "setFocusedActivity: No activity record matching token=" + token); } if (r.moveFocusableActivityToTop("setFocusedActivity")) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } } } @@ -5943,7 +5966,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public boolean shuttingDown(boolean booted, int timeout) { synchronized (mGlobalLock) { mShuttingDown = true; - mStackSupervisor.prepareForShutdownLocked(); + mRootActivityContainer.prepareForShutdown(); updateEventDispatchingLocked(booted); notifyTaskPersisterLocked(null, true); return mStackSupervisor.shutdownLocked(timeout); @@ -6050,7 +6073,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void onPackageReplaced(ApplicationInfo aInfo) { synchronized (mGlobalLock) { - mStackSupervisor.updateActivityApplicationInfoLocked(aInfo); + mRootActivityContainer.updateActivityApplicationInfo(aInfo); } } @@ -6080,7 +6103,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mH.post(() -> { synchronized (mGlobalLock) { final ActivityDisplay activityDisplay = - mStackSupervisor.getActivityDisplay(displayId); + mRootActivityContainer.getActivityDisplay(displayId); if (activityDisplay == null) { // Call might come when display is not yet added or has been removed. if (DEBUG_CONFIGURATION) { @@ -6163,14 +6186,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public boolean startHomeActivity(int userId, String reason) { synchronized (mGlobalLock) { - return mStackSupervisor.startHomeOnDisplay(userId, reason, DEFAULT_DISPLAY); + return mRootActivityContainer.startHomeOnDisplay(userId, reason, DEFAULT_DISPLAY); } } @Override public boolean startHomeOnAllDisplays(int userId, String reason) { synchronized (mGlobalLock) { - return mStackSupervisor.startHomeOnAllDisplays(userId, reason); + return mRootActivityContainer.startHomeOnAllDisplays(userId, reason); } } @@ -6234,7 +6257,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { Runnable finishInstrumentationCallback) { synchronized (mGlobalLock) { // Remove this application's activities from active lists. - boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(wpc); + boolean hasVisibleActivities = mRootActivityContainer.handleAppDied(wpc); wpc.clearRecentTasks(); wpc.clearActivities(); @@ -6246,12 +6269,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mWindowManager.deferSurfaceLayout(); try { if (!restarting && hasVisibleActivities - && !mStackSupervisor.resumeFocusedStacksTopActivitiesLocked()) { + && !mRootActivityContainer.resumeFocusedStacksTopActivities()) { // If there was nothing to resume, and we are not already restarting this // process, but there is a visible activity that is hosted by the process... // then make sure all visible activities are running, taking care of // restarting this process. - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); } } finally { mWindowManager.continueSurfaceLayout(); @@ -6280,7 +6303,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } mWindowManager.closeSystemDialogs(reason); - mStackSupervisor.closeSystemDialogsLocked(); + mRootActivityContainer.closeSystemDialogs(); } // Call into AM outside the synchronized block. mAmInternal.broadcastCloseSystemDialogs(reason); @@ -6294,9 +6317,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { String packageName, Set<String> disabledClasses, int userId, boolean booted) { synchronized (mGlobalLock) { // Clean-up disabled activities. - if (mStackSupervisor.finishDisabledPackageActivitiesLocked( + if (mRootActivityContainer.finishDisabledPackageActivities( packageName, disabledClasses, true, false, userId) && booted) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); mStackSupervisor.scheduleIdleLocked(); } @@ -6313,7 +6336,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { boolean didSomething = getActivityStartController().clearPendingActivityLaunches(packageName); - didSomething |= mStackSupervisor.finishDisabledPackageActivitiesLocked(packageName, + didSomething |= mRootActivityContainer.finishDisabledPackageActivities(packageName, null, doit, evenPersistent, userId); return didSomething; } @@ -6322,7 +6345,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void resumeTopActivities(boolean scheduleIdle) { synchronized (mGlobalLock) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); if (scheduleIdle) { mStackSupervisor.scheduleIdleLocked(); } @@ -6339,7 +6362,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public boolean attachApplication(WindowProcessController wpc) throws RemoteException { synchronized (mGlobalLock) { - return mStackSupervisor.attachApplicationLocked(wpc); + return mRootActivityContainer.attachApplication(wpc); } } @@ -6361,7 +6384,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // Showing launcher to avoid user entering credential twice. startHomeActivity(currentUserId, "notifyLockedProfile"); } - mStackSupervisor.lockAllProfileTasks(userId); + mRootActivityContainer.lockAllProfileTasks(userId); } } finally { Binder.restoreCallingIdentity(ident); @@ -6382,7 +6405,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { ActivityOptions activityOptions = options != null ? new ActivityOptions(options) : ActivityOptions.makeBasic(); final ActivityRecord homeActivity = - mStackSupervisor.getDefaultDisplayHomeActivity(); + mRootActivityContainer.getDefaultDisplayHomeActivity(); if (homeActivity != null) { activityOptions.setLaunchTaskId(homeActivity.getTask().taskId); } @@ -6399,7 +6422,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { // The output proto of "activity --proto activities" // is ActivityManagerServiceDumpActivitiesProto - mStackSupervisor.writeToProto(proto, + mRootActivityContainer.writeToProto(proto, ActivityManagerServiceDumpActivitiesProto.ACTIVITY_STACK_SUPERVISOR); } } @@ -6494,7 +6517,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } if (dumpPackage == null) { pw.println(" mGlobalConfiguration: " + getGlobalConfiguration()); - mStackSupervisor.dumpDisplayConfigs(pw, " "); + mRootActivityContainer.dumpDisplayConfigs(pw, " "); } if (dumpAll) { if (dumpPackage == null) { @@ -6522,7 +6545,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (dumpPackage == null) { pw.println(" mWakefulness=" + PowerManagerInternal.wakefulnessToString(wakefulness)); - pw.println(" mSleepTokens=" + mStackSupervisor.mSleepTokens); + pw.println(" mSleepTokens=" + mRootActivityContainer.mSleepTokens); if (mRunningVoice != null) { pw.println(" mRunningVoice=" + mRunningVoice); pw.println(" mVoiceWakeLock" + mVoiceWakeLock); @@ -6649,14 +6672,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public boolean canGcNow() { synchronized (mGlobalLock) { - return isSleeping() || mStackSupervisor.allResumedActivitiesIdle(); + return isSleeping() || mRootActivityContainer.allResumedActivitiesIdle(); } } @Override public WindowProcessController getTopApp() { synchronized (mGlobalLock) { - final ActivityRecord top = mStackSupervisor.getTopResumedActivity(); + final ActivityRecord top = mRootActivityContainer.getTopResumedActivity(); return top != null ? top.app : null; } } @@ -6664,8 +6687,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void rankTaskLayersIfNeeded() { synchronized (mGlobalLock) { - if (mStackSupervisor != null) { - mStackSupervisor.rankTaskLayersIfNeeded(); + if (mRootActivityContainer != null) { + mRootActivityContainer.rankTaskLayersIfNeeded(); } } } @@ -6673,35 +6696,35 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void scheduleDestroyAllActivities(String reason) { synchronized (mGlobalLock) { - mStackSupervisor.scheduleDestroyAllActivities(null, reason); + mRootActivityContainer.scheduleDestroyAllActivities(null, reason); } } @Override public void removeUser(int userId) { synchronized (mGlobalLock) { - mStackSupervisor.removeUserLocked(userId); + mRootActivityContainer.removeUser(userId); } } @Override public boolean switchUser(int userId, UserState userState) { synchronized (mGlobalLock) { - return mStackSupervisor.switchUserLocked(userId, userState); + return mRootActivityContainer.switchUser(userId, userState); } } @Override public void onHandleAppCrash(WindowProcessController wpc) { synchronized (mGlobalLock) { - mStackSupervisor.handleAppCrashLocked(wpc); + mRootActivityContainer.handleAppCrash(wpc); } } @Override public int finishTopCrashedActivities(WindowProcessController crashedApp, String reason) { synchronized (mGlobalLock) { - return mStackSupervisor.finishTopCrashedActivitiesLocked(crashedApp, reason); + return mRootActivityContainer.finishTopCrashedActivities(crashedApp, reason); } } @@ -6871,5 +6894,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mProfilerInfo = profilerInfo; } } + + @Override + public ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry() { + synchronized (mGlobalLock) { + return mStackSupervisor.getActivityMetricsLogger().getLaunchObserverRegistry(); + } + } } } diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index 04fef02cd3b4..441c5935a507 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -16,8 +16,8 @@ package com.android.server.wm; -import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS; +import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; import android.app.ActivityManager; import android.app.IAppTask; @@ -77,7 +77,7 @@ class AppTaskImpl extends IAppTask.Stub { synchronized (mService.mGlobalLock) { long origId = Binder.clearCallingIdentity(); try { - TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId, + TaskRecord tr = mService.mRootActivityContainer.anyTaskForId(mTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (tr == null) { throw new IllegalArgumentException("Unable to find task ID " + mTaskId); @@ -115,7 +115,7 @@ class AppTaskImpl extends IAppTask.Stub { TaskRecord tr; IApplicationThread appThread; synchronized (mService.mGlobalLock) { - tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId, + tr = mService.mRootActivityContainer.anyTaskForId(mTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (tr == null) { throw new IllegalArgumentException("Unable to find task ID " + mTaskId); @@ -143,7 +143,7 @@ class AppTaskImpl extends IAppTask.Stub { synchronized (mService.mGlobalLock) { long origId = Binder.clearCallingIdentity(); try { - TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId, + TaskRecord tr = mService.mRootActivityContainer.anyTaskForId(mTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (tr == null) { throw new IllegalArgumentException("Unable to find task ID " + mTaskId); diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 32a6f74b5a11..bf00ffb30222 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -305,7 +305,7 @@ public class AppTransitionController { AppWindowToken wtoken = openingApps.valueAt(i); if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken); - if (!wtoken.setVisibility(animLp, true, transit, false, voiceInteraction)) { + if (!wtoken.commitVisibility(animLp, true, transit, false, voiceInteraction)) { // This token isn't going to be animating. Add it to the list of tokens to // be notified of app transition complete since the notification will not be // sent be the app window animator. @@ -341,7 +341,7 @@ public class AppTransitionController { if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken); // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not // animating? - wtoken.setVisibility(animLp, false, transit, false, voiceInteraction); + wtoken.commitVisibility(animLp, false, transit, false, voiceInteraction); wtoken.updateReportedVisibilityLocked(); // Force the allDrawn flag, because we want to start // this guy's animations regardless of whether it's @@ -350,9 +350,8 @@ public class AppTransitionController { wtoken.deferClearAllDrawn = false; // Ensure that apps that are mid-starting are also scheduled to have their // starting windows removed after the animation is complete - if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit - && wtoken.getController() != null) { - wtoken.getController().removeStartingWindow(); + if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit) { + wtoken.removeStartingWindow(); } if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailDown()) { diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java deleted file mode 100644 index 7fdea10e3e2c..000000000000 --- a/services/core/java/com/android/server/wm/AppWindowContainerController.java +++ /dev/null @@ -1,912 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.wm; - -import static android.app.ActivityOptions.ANIM_CLIP_REVEAL; -import static android.app.ActivityOptions.ANIM_CUSTOM; -import static android.app.ActivityOptions.ANIM_NONE; -import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS; -import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION; -import static android.app.ActivityOptions.ANIM_SCALE_UP; -import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN; -import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP; -import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; -import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; -import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS; -import static android.view.WindowManager.TRANSIT_UNSET; - -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; - -import android.app.ActivityManager.TaskSnapshot; -import android.app.ActivityOptions; -import android.content.ComponentName; -import android.content.Intent; -import android.content.res.CompatibilityInfo; -import android.content.res.Configuration; -import android.graphics.GraphicBuffer; -import android.graphics.Rect; -import android.os.Debug; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.util.Slog; -import android.view.AppTransitionAnimationSpec; -import android.view.IAppTransitionAnimationSpecsFuture; -import android.view.IApplicationToken; -import android.view.RemoteAnimationDefinition; -import android.view.WindowManager; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.AttributeCache; -import com.android.server.policy.WindowManagerPolicy.StartingSurface; - -/** - * Controller for the app window token container. This is created by activity manager to link - * activity records to the app window token container they use in window manager. - * - * Test class: {@link AppWindowContainerControllerTests} - */ -public class AppWindowContainerController - extends WindowContainerController<AppWindowToken, AppWindowContainerListener> { - - private static final int STARTING_WINDOW_TYPE_NONE = 0; - private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1; - private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2; - - private final IApplicationToken mToken; - private final Handler mHandler; - - private final class H extends Handler { - public static final int NOTIFY_WINDOWS_DRAWN = 1; - public static final int NOTIFY_STARTING_WINDOW_DRAWN = 2; - public static final int NOTIFY_WINDOWS_NOTDRAWN = 3; - - public H(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case NOTIFY_WINDOWS_DRAWN: - if (mListener == null) { - return; - } - if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in " - + AppWindowContainerController.this.mToken); - mListener.onWindowsDrawn(true /* drawn */, msg.getWhen()); - break; - case NOTIFY_STARTING_WINDOW_DRAWN: - if (mListener == null) { - return; - } - if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting starting window drawn in " - + AppWindowContainerController.this.mToken); - mListener.onStartingWindowDrawn(msg.getWhen()); - break; - case NOTIFY_WINDOWS_NOTDRAWN: - if (mListener == null) { - return; - } - if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting not drawn in " - + AppWindowContainerController.this.mToken); - mListener.onWindowsDrawn(false /* drawn */, msg.getWhen()); - break; - default: - break; - } - } - } - - private final Runnable mOnWindowsVisible = () -> { - if (mListener == null) { - return; - } - if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting visible in " - + AppWindowContainerController.this.mToken); - mListener.onWindowsVisible(); - }; - - private final Runnable mOnWindowsGone = () -> { - if (mListener == null) { - return; - } - if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting gone in " - + AppWindowContainerController.this.mToken); - mListener.onWindowsGone(); - }; - - private final Runnable mAddStartingWindow = new Runnable() { - - @Override - public void run() { - final StartingData startingData; - final AppWindowToken container; - - synchronized (mGlobalLock) { - if (mContainer == null) { - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "mContainer was null while trying to" - + " add starting window"); - return; - } - - // There can only be one adding request, silly caller! - mService.mAnimationHandler.removeCallbacks(this); - - startingData = mContainer.startingData; - container = mContainer; - } - - if (startingData == null) { - // Animation has been canceled... do nothing. - if (DEBUG_STARTING_WINDOW) - Slog.v(TAG_WM, "startingData was nulled out before handling" - + " mAddStartingWindow: " + mContainer); - return; - } - - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting " - + AppWindowContainerController.this + ": startingData=" - + container.startingData); - - StartingSurface surface = null; - try { - surface = startingData.createStartingSurface(container); - } catch (Exception e) { - Slog.w(TAG_WM, "Exception when adding starting window", e); - } - if (surface != null) { - boolean abort = false; - synchronized (mGlobalLock) { - // If the window was successfully added, then - // we need to remove it. - if (container.removed || container.startingData == null) { - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, - "Aborted starting " + container - + ": removed=" + container.removed - + " startingData=" + container.startingData); - container.startingWindow = null; - container.startingData = null; - abort = true; - } else { - container.startingSurface = surface; - } - if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM, - "Added starting " + mContainer - + ": startingWindow=" - + container.startingWindow + " startingView=" - + container.startingSurface); - } - if (abort) { - surface.remove(); - } - } else if (DEBUG_STARTING_WINDOW) { - Slog.v(TAG_WM, "Surface returned was null: " + mContainer); - } - } - }; - - public AppWindowContainerController(TaskWindowContainerController taskController, - IApplicationToken token, ComponentName activityComponent, - AppWindowContainerListener listener, int index, int requestedOrientation, - boolean fullscreen, boolean showForAllUsers, int configChanges, - boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable, - int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos) { - this(taskController, token, activityComponent, listener, index, requestedOrientation, - fullscreen, showForAllUsers, configChanges, voiceInteraction, launchTaskBehind, - alwaysFocusable, targetSdkVersion, rotationAnimationHint, - inputDispatchingTimeoutNanos, WindowManagerService.getInstance()); - } - - public AppWindowContainerController(TaskWindowContainerController taskController, - IApplicationToken token, ComponentName activityComponent, - AppWindowContainerListener listener, int index, int requestedOrientation, - boolean fullscreen, boolean showForAllUsers, int configChanges, - boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable, - int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos, - WindowManagerService service) { - super(listener, service); - mHandler = new H(service.mH.getLooper()); - mToken = token; - synchronized (mGlobalLock) { - AppWindowToken atoken = mRoot.getAppWindowToken(mToken.asBinder()); - if (atoken != null) { - // TODO: Should this throw an exception instead? - Slog.w(TAG_WM, "Attempted to add existing app token: " + mToken); - return; - } - - final Task task = taskController.mContainer; - if (task == null) { - throw new IllegalArgumentException("AppWindowContainerController: invalid " - + " controller=" + taskController); - } - - atoken = createAppWindow(mService, token, activityComponent, voiceInteraction, - task.getDisplayContent(), inputDispatchingTimeoutNanos, fullscreen, - showForAllUsers, targetSdkVersion, requestedOrientation, rotationAnimationHint, - configChanges, launchTaskBehind, alwaysFocusable, this); - if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken - + " controller=" + taskController + " at " + index); - task.addChild(atoken, index); - } - } - - @VisibleForTesting - AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token, - ComponentName component, boolean voiceInteraction, DisplayContent dc, - long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, - int targetSdk, int orientation, int rotationAnimationHint, int configChanges, - boolean launchTaskBehind, boolean alwaysFocusable, - AppWindowContainerController controller) { - return new AppWindowToken(service, token, component, voiceInteraction, dc, - inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation, - rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, - controller); - } - - public void removeContainer(int displayId) { - synchronized (mGlobalLock) { - final DisplayContent dc = mRoot.getDisplayContent(displayId); - if (dc == null) { - Slog.w(TAG_WM, "removeAppToken: Attempted to remove binder token: " - + mToken + " from non-existing displayId=" + displayId); - return; - } - dc.removeAppToken(mToken.asBinder()); - super.removeContainer(); - } - } - - @Override - public void removeContainer() { - throw new UnsupportedOperationException("Use removeContainer(displayId) instead."); - } - - public void reparent(TaskWindowContainerController taskController, int position) { - synchronized (mGlobalLock) { - if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM, "reparent: moving app token=" + mToken - + " to task=" + taskController + " at " + position); - if (mContainer == null) { - if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM, - "reparent: could not find app token=" + mToken); - return; - } - final Task task = taskController.mContainer; - if (task == null) { - throw new IllegalArgumentException("reparent: could not find task=" - + taskController); - } - mContainer.reparent(task, position); - mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); - } - } - - public Configuration setOrientation(int requestedOrientation, int displayId, - Configuration displayConfig, boolean freezeScreenIfNeeded) { - synchronized (mGlobalLock) { - if (mContainer == null) { - Slog.w(TAG_WM, - "Attempted to set orientation of non-existing app token: " + mToken); - return null; - } - - mContainer.setOrientation(requestedOrientation); - - final IBinder binder = freezeScreenIfNeeded ? mToken.asBinder() : null; - return mService.updateOrientationFromAppTokens(displayConfig, binder, displayId); - - } - } - - public int getOrientation() { - synchronized (mGlobalLock) { - if (mContainer == null) { - return SCREEN_ORIENTATION_UNSPECIFIED; - } - - return mContainer.getOrientationIgnoreVisibility(); - } - } - - public void setDisablePreviewScreenshots(boolean disable) { - synchronized (mGlobalLock) { - if (mContainer == null) { - Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app" - + " token: " + mToken); - return; - } - mContainer.setDisablePreviewScreenshots(disable); - } - } - - public void setVisibility(boolean visible, boolean deferHidingClient) { - synchronized (mGlobalLock) { - if (mContainer == null) { - Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " - + mToken); - return; - } - - final AppWindowToken wtoken = mContainer; - final AppTransition appTransition = mContainer.getDisplayContent().mAppTransition; - - // Don't set visibility to false if we were already not visible. This prevents WM from - // adding the app to the closing app list which doesn't make sense for something that is - // already not visible. However, set visibility to true even if we are already visible. - // This makes sure the app is added to the opening apps list so that the right - // transition can be selected. - // TODO: Probably a good idea to separate the concept of opening/closing apps from the - // concept of setting visibility... - if (!visible && wtoken.hiddenRequested) { - - if (!deferHidingClient && wtoken.mDeferHidingClient) { - // We previously deferred telling the client to hide itself when visibility was - // initially set to false. Now we would like it to hide, so go ahead and set it. - wtoken.mDeferHidingClient = deferHidingClient; - wtoken.setClientHidden(true); - } - return; - } - - if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG_WM, "setAppVisibility(" - + mToken + ", visible=" + visible + "): " + appTransition - + " hidden=" + wtoken.isHidden() + " hiddenRequested=" - + wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6)); - - final DisplayContent displayContent = mContainer.getDisplayContent(); - displayContent.mOpeningApps.remove(wtoken); - displayContent.mClosingApps.remove(wtoken); - wtoken.waitingToShow = false; - wtoken.hiddenRequested = !visible; - wtoken.mDeferHidingClient = deferHidingClient; - - if (!visible) { - // If the app is dead while it was visible, we kept its dead window on screen. - // Now that the app is going invisible, we can remove it. It will be restarted - // if made visible again. - wtoken.removeDeadWindows(); - } else { - if (!appTransition.isTransitionSet() - && appTransition.isReady()) { - // Add the app mOpeningApps if transition is unset but ready. This means - // we're doing a screen freeze, and the unfreeze will wait for all opening - // apps to be ready. - displayContent.mOpeningApps.add(wtoken); - } - wtoken.startingMoved = false; - // If the token is currently hidden (should be the common case), or has been - // stopped, then we need to set up to wait for its windows to be ready. - if (wtoken.isHidden() || wtoken.mAppStopped) { - wtoken.clearAllDrawn(); - - // If the app was already visible, don't reset the waitingToShow state. - if (wtoken.isHidden()) { - wtoken.waitingToShow = true; - } - } - - // In the case where we are making an app visible but holding off for a transition, - // we still need to tell the client to make its windows visible so they get drawn. - // Otherwise, we will wait on performing the transition until all windows have been - // drawn, they never will be, and we are sad. - wtoken.setClientHidden(false); - - wtoken.requestUpdateWallpaperIfNeeded(); - - if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + wtoken); - wtoken.mAppStopped = false; - - mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded(); - } - - // If we are preparing an app transition, then delay changing - // the visibility of this token until we execute that transition. - if (wtoken.okToAnimate() && appTransition.isTransitionSet()) { - wtoken.inPendingTransaction = true; - if (visible) { - displayContent.mOpeningApps.add(wtoken); - wtoken.mEnteringAnimation = true; - } else { - displayContent.mClosingApps.add(wtoken); - wtoken.mEnteringAnimation = false; - } - if (appTransition.getAppTransition() - == WindowManager.TRANSIT_TASK_OPEN_BEHIND) { - // We're launchingBehind, add the launching activity to mOpeningApps. - final WindowState win = mContainer.getDisplayContent().findFocusedWindow(); - if (win != null) { - final AppWindowToken focusedToken = win.mAppToken; - if (focusedToken != null) { - if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, " - + " adding " + focusedToken + " to mOpeningApps"); - // Force animation to be loaded. - focusedToken.setHidden(true); - displayContent.mOpeningApps.add(focusedToken); - } - } - } - return; - } - - wtoken.setVisibility(null, visible, TRANSIT_UNSET, true, wtoken.mVoiceInteraction); - wtoken.updateReportedVisibilityLocked(); - } - } - - /** - * Notifies that we launched an app that might be visible or not visible depending on what kind - * of Keyguard flags it's going to set on its windows. - */ - public void notifyUnknownVisibilityLaunched() { - synchronized (mGlobalLock) { - if (mContainer != null) { - mContainer.getDisplayContent().mUnknownAppVisibilityController.notifyLaunched( - mContainer); - } - } - } - - public boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo, - CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, - IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning, - boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) { - synchronized (mGlobalLock) { - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "setAppStartingWindow: token=" + mToken - + " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask - + " taskSwitch=" + taskSwitch + " processRunning=" + processRunning - + " allowTaskSnapshot=" + allowTaskSnapshot); - - if (mContainer == null) { - Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + mToken); - return false; - } - - // If the display is frozen, we won't do anything until the actual window is - // displayed so there is no reason to put in the starting window. - if (!mContainer.okToDisplay()) { - return false; - } - - if (mContainer.startingData != null) { - return false; - } - - final WindowState mainWin = mContainer.findMainWindow(); - if (mainWin != null && mainWin.mWinAnimator.getShown()) { - // App already has a visible window...why would you want a starting window? - return false; - } - - final TaskSnapshot snapshot = mService.mTaskSnapshotController.getSnapshot( - mContainer.getTask().mTaskId, mContainer.getTask().mUserId, - false /* restoreFromDisk */, false /* reducedResolution */); - final int type = getStartingWindowType(newTask, taskSwitch, processRunning, - allowTaskSnapshot, activityCreated, fromRecents, snapshot); - - if (type == STARTING_WINDOW_TYPE_SNAPSHOT) { - return createSnapshot(snapshot); - } - - // If this is a translucent window, then don't show a starting window -- the current - // effect (a full-screen opaque starting window that fades away to the real contents - // when it is ready) does not work for this. - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Checking theme of starting window: 0x" - + Integer.toHexString(theme)); - if (theme != 0) { - AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme, - com.android.internal.R.styleable.Window, mService.mCurrentUserId); - if (ent == null) { - // Whoops! App doesn't exist. Um. Okay. We'll just pretend like we didn't - // see that. - return false; - } - final boolean windowIsTranslucent = ent.array.getBoolean( - com.android.internal.R.styleable.Window_windowIsTranslucent, false); - final boolean windowIsFloating = ent.array.getBoolean( - com.android.internal.R.styleable.Window_windowIsFloating, false); - final boolean windowShowWallpaper = ent.array.getBoolean( - com.android.internal.R.styleable.Window_windowShowWallpaper, false); - final boolean windowDisableStarting = ent.array.getBoolean( - com.android.internal.R.styleable.Window_windowDisablePreview, false); - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Translucent=" + windowIsTranslucent - + " Floating=" + windowIsFloating - + " ShowWallpaper=" + windowShowWallpaper); - if (windowIsTranslucent) { - return false; - } - if (windowIsFloating || windowDisableStarting) { - return false; - } - if (windowShowWallpaper) { - if (mContainer.getDisplayContent().mWallpaperController.getWallpaperTarget() - == null) { - // If this theme is requesting a wallpaper, and the wallpaper - // is not currently visible, then this effectively serves as - // an opaque window and our starting window transition animation - // can still work. We just need to make sure the starting window - // is also showing the wallpaper. - windowFlags |= FLAG_SHOW_WALLPAPER; - } else { - return false; - } - } - } - - if (mContainer.transferStartingWindow(transferFrom)) { - return true; - } - - // There is no existing starting window, and we don't want to create a splash screen, so - // that's it! - if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) { - return false; - } - - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData"); - mContainer.startingData = new SplashScreenStartingData(mService, pkg, theme, - compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, - mContainer.getMergedOverrideConfiguration()); - scheduleAddStartingWindow(); - } - return true; - } - - private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning, - boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents, - TaskSnapshot snapshot) { - if (mContainer.getDisplayContent().mAppTransition.getAppTransition() - == TRANSIT_DOCK_TASK_FROM_RECENTS) { - // TODO(b/34099271): Remove this statement to add back the starting window and figure - // out why it causes flickering, the starting window appears over the thumbnail while - // the docked from recents transition occurs - return STARTING_WINDOW_TYPE_NONE; - } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) { - return STARTING_WINDOW_TYPE_SPLASH_SCREEN; - } else if (taskSwitch && allowTaskSnapshot) { - return snapshot == null ? STARTING_WINDOW_TYPE_NONE - : snapshotOrientationSameAsTask(snapshot) || fromRecents - ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN; - } else { - return STARTING_WINDOW_TYPE_NONE; - } - } - - void scheduleAddStartingWindow() { - // Note: we really want to do sendMessageAtFrontOfQueue() because we - // want to process the message ASAP, before any other queued - // messages. - if (!mService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) { - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING"); - mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow); - } - } - - private boolean createSnapshot(TaskSnapshot snapshot) { - if (snapshot == null) { - return false; - } - - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData"); - mContainer.startingData = new SnapshotStartingData(mService, snapshot); - scheduleAddStartingWindow(); - return true; - } - - private boolean snapshotOrientationSameAsTask(TaskSnapshot snapshot) { - if (snapshot == null) { - return false; - } - return mContainer.getTask().getConfiguration().orientation == snapshot.getOrientation(); - } - - public void removeStartingWindow() { - synchronized (mGlobalLock) { - if (mContainer.startingWindow == null) { - if (mContainer.startingData != null) { - // Starting window has not been added yet, but it is scheduled to be added. - // Go ahead and cancel the request. - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, - "Clearing startingData for token=" + mContainer); - mContainer.startingData = null; - } - return; - } - - final StartingSurface surface; - if (mContainer.startingData != null) { - surface = mContainer.startingSurface; - mContainer.startingData = null; - mContainer.startingSurface = null; - mContainer.startingWindow = null; - mContainer.startingDisplayed = false; - if (surface == null) { - if (DEBUG_STARTING_WINDOW) { - Slog.v(TAG_WM, "startingWindow was set but startingSurface==null, couldn't " - + "remove"); - } - return; - } - } else { - if (DEBUG_STARTING_WINDOW) { - Slog.v(TAG_WM, "Tried to remove starting window but startingWindow was null:" - + mContainer); - } - return; - } - - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Schedule remove starting " + mContainer - + " startingWindow=" + mContainer.startingWindow - + " startingView=" + mContainer.startingSurface - + " Callers=" + Debug.getCallers(5)); - - // Use the same thread to remove the window as we used to add it, as otherwise we end up - // with things in the view hierarchy being called from different threads. - mService.mAnimationHandler.post(() -> { - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Removing startingView=" + surface); - try { - surface.remove(); - } catch (Exception e) { - Slog.w(TAG_WM, "Exception when removing starting window", e); - } - }); - } - } - - public void pauseKeyDispatching() { - synchronized (mGlobalLock) { - if (mContainer != null) { - mContainer.getDisplayContent().getInputMonitor().pauseDispatchingLw(mContainer); - } - } - } - - public void resumeKeyDispatching() { - synchronized (mGlobalLock) { - if (mContainer != null) { - mContainer.getDisplayContent().getInputMonitor().resumeDispatchingLw(mContainer); - } - } - } - - public void notifyAppResumed(boolean wasStopped) { - synchronized (mGlobalLock) { - if (mContainer == null) { - Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: " + mToken); - return; - } - mContainer.notifyAppResumed(wasStopped); - } - } - - public void notifyAppStopping() { - synchronized (mGlobalLock) { - if (mContainer == null) { - Slog.w(TAG_WM, "Attempted to notify stopping on non-existing app token: " - + mToken); - return; - } - mContainer.detachChildren(); - } - } - - public void notifyAppStopped() { - synchronized (mGlobalLock) { - if (mContainer == null) { - Slog.w(TAG_WM, "Attempted to notify stopped of non-existing app token: " - + mToken); - return; - } - mContainer.notifyAppStopped(); - } - } - - public void startFreezingScreen(int configChanges) { - synchronized (mGlobalLock) { - if (mContainer == null) { - Slog.w(TAG_WM, - "Attempted to freeze screen with non-existing app token: " + mContainer); - return; - } - - if (configChanges == 0 && mContainer.okToDisplay()) { - if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + mToken); - return; - } - - mContainer.startFreezingScreen(); - } - } - - public void stopFreezingScreen(boolean force) { - synchronized (mGlobalLock) { - if (mContainer == null) { - return; - } - if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + mToken + ": hidden=" - + mContainer.isHidden() + " freezing=" + mContainer.isFreezingScreen()); - mContainer.stopFreezingScreen(true, force); - } - } - - public void registerRemoteAnimations(RemoteAnimationDefinition definition) { - synchronized (mGlobalLock) { - if (mContainer == null) { - Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app" - + " token: " + mToken); - return; - } - mContainer.registerRemoteAnimations(definition); - } - } - - void reportStartingWindowDrawn() { - mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_STARTING_WINDOW_DRAWN)); - } - - void reportWindowsDrawn() { - mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_DRAWN)); - } - - void reportWindowsNotDrawn() { - mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_NOTDRAWN)); - } - - void reportWindowsVisible() { - mHandler.post(mOnWindowsVisible); - } - - void reportWindowsGone() { - mHandler.post(mOnWindowsGone); - } - - /** Calls directly into activity manager so window manager lock shouldn't held. */ - boolean keyDispatchingTimedOut(String reason, int windowPid) { - return mListener != null && mListener.keyDispatchingTimedOut(reason, windowPid); - } - - /** - * Apply override app transition base on options & animation type. - */ - public void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) { - synchronized (mGlobalLock) { - final int animationType = pendingOptions.getAnimationType(); - final DisplayContent displayContent = mContainer.getDisplayContent(); - switch (animationType) { - case ANIM_CUSTOM: - displayContent.mAppTransition.overridePendingAppTransition( - pendingOptions.getPackageName(), - pendingOptions.getCustomEnterResId(), - pendingOptions.getCustomExitResId(), - pendingOptions.getOnAnimationStartListener()); - break; - case ANIM_CLIP_REVEAL: - displayContent.mAppTransition.overridePendingAppTransitionClipReveal( - pendingOptions.getStartX(), pendingOptions.getStartY(), - pendingOptions.getWidth(), pendingOptions.getHeight()); - if (intent.getSourceBounds() == null) { - intent.setSourceBounds(new Rect(pendingOptions.getStartX(), - pendingOptions.getStartY(), - pendingOptions.getStartX() + pendingOptions.getWidth(), - pendingOptions.getStartY() + pendingOptions.getHeight())); - } - break; - case ANIM_SCALE_UP: - displayContent.mAppTransition.overridePendingAppTransitionScaleUp( - pendingOptions.getStartX(), pendingOptions.getStartY(), - pendingOptions.getWidth(), pendingOptions.getHeight()); - if (intent.getSourceBounds() == null) { - intent.setSourceBounds(new Rect(pendingOptions.getStartX(), - pendingOptions.getStartY(), - pendingOptions.getStartX() + pendingOptions.getWidth(), - pendingOptions.getStartY() + pendingOptions.getHeight())); - } - break; - case ANIM_THUMBNAIL_SCALE_UP: - case ANIM_THUMBNAIL_SCALE_DOWN: - final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP); - final GraphicBuffer buffer = pendingOptions.getThumbnail(); - displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer, - pendingOptions.getStartX(), pendingOptions.getStartY(), - pendingOptions.getOnAnimationStartListener(), - scaleUp); - if (intent.getSourceBounds() == null && buffer != null) { - intent.setSourceBounds(new Rect(pendingOptions.getStartX(), - pendingOptions.getStartY(), - pendingOptions.getStartX() + buffer.getWidth(), - pendingOptions.getStartY() + buffer.getHeight())); - } - break; - case ANIM_THUMBNAIL_ASPECT_SCALE_UP: - case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN: - final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs(); - final IAppTransitionAnimationSpecsFuture specsFuture = - pendingOptions.getSpecsFuture(); - if (specsFuture != null) { - displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture( - specsFuture, pendingOptions.getOnAnimationStartListener(), - animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP); - } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN - && specs != null) { - displayContent.mAppTransition.overridePendingAppTransitionMultiThumb( - specs, pendingOptions.getOnAnimationStartListener(), - pendingOptions.getAnimationFinishedListener(), false); - } else { - displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb( - pendingOptions.getThumbnail(), - pendingOptions.getStartX(), pendingOptions.getStartY(), - pendingOptions.getWidth(), pendingOptions.getHeight(), - pendingOptions.getOnAnimationStartListener(), - (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP)); - if (intent.getSourceBounds() == null) { - intent.setSourceBounds(new Rect(pendingOptions.getStartX(), - pendingOptions.getStartY(), - pendingOptions.getStartX() + pendingOptions.getWidth(), - pendingOptions.getStartY() + pendingOptions.getHeight())); - } - } - break; - case ANIM_OPEN_CROSS_PROFILE_APPS: - displayContent.mAppTransition - .overridePendingAppTransitionStartCrossProfileApps(); - break; - case ANIM_REMOTE_ANIMATION: - displayContent.mAppTransition.overridePendingAppTransitionRemote( - pendingOptions.getRemoteAnimationAdapter()); - break; - case ANIM_NONE: - break; - default: - Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType); - break; - } - } - } - - /** - * Notifies AWT that this app is waiting to pause in order to determine if it will enter PIP. - * This information helps AWT know that the app is in the process of pausing before it gets the - * signal on the WM side. - */ - public void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) { - synchronized (mGlobalLock) { - if (mContainer == null) { - return; - } - - mContainer.setWillCloseOrEnterPip(willCloseOrEnterPip); - } - } - - @Override - public String toString() { - return "AppWindowContainerController{" - + " token=" + mToken - + " mContainer=" + mContainer - + " mListener=" + mListener - + "}"; - } -} diff --git a/services/core/java/com/android/server/wm/AppWindowContainerListener.java b/services/core/java/com/android/server/wm/AppWindowContainerListener.java deleted file mode 100644 index ad27669fc030..000000000000 --- a/services/core/java/com/android/server/wm/AppWindowContainerListener.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.wm; - -/** Interface used by the creator of the controller to listen to changes with the container. */ -public interface AppWindowContainerListener extends WindowContainerListener { - /** Called when the windows associated app window container drawn state changes. */ - void onWindowsDrawn(boolean drawn, long timestamp); - /** Called when the windows associated app window container are visible. */ - void onWindowsVisible(); - /** Called when the windows associated app window container are no longer visible. */ - void onWindowsGone(); - - /** - * Called when the starting window for this container is drawn. - */ - void onStartingWindowDrawn(long timestamp); - - /** - * Called when the key dispatching to a window associated with the app window container - * timed-out. - * - * @param reason The reason for the key dispatching time out. - * @param windowPid The pid of the window key dispatching timed out on. - * @return True if input dispatching should be aborted. - */ - boolean keyDispatchingTimedOut(String reason, int windowPid); -} diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java index 729f89bb2611..b9b9d31f5b71 100644 --- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java +++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java @@ -16,13 +16,13 @@ package com.android.server.wm; +import static com.android.server.wm.AppWindowThumbnailProto.HEIGHT; +import static com.android.server.wm.AppWindowThumbnailProto.SURFACE_ANIMATOR; +import static com.android.server.wm.AppWindowThumbnailProto.WIDTH; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; 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.MAX_ANIMATION_DURATION; -import static com.android.server.wm.AppWindowThumbnailProto.HEIGHT; -import static com.android.server.wm.AppWindowThumbnailProto.SURFACE_ANIMATOR; -import static com.android.server.wm.AppWindowThumbnailProto.WIDTH; import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; @@ -65,7 +65,7 @@ class AppWindowThumbnail implements Animatable { // this to the task. mSurfaceControl = appToken.makeSurface() .setName("thumbnail anim: " + appToken.toString()) - .setSize(mWidth, mHeight) + .setBufferSize(mWidth, mHeight) .setFormat(PixelFormat.TRANSLUCENT) .setMetadata(appToken.windowType, window != null ? window.mOwnerUid : Binder.getCallingUid()) diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 6d402f291fcb..df81c07ba530 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -28,10 +28,12 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_SECURE; +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.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS; import static android.view.WindowManager.TRANSIT_UNSET; import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN; @@ -81,7 +83,9 @@ import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; import android.annotation.CallSuper; import android.app.Activity; +import android.app.ActivityManager; import android.content.ComponentName; +import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.GraphicBuffer; import android.graphics.Point; @@ -90,6 +94,7 @@ import android.os.Binder; import android.os.Debug; import android.os.IBinder; import android.os.RemoteException; +import android.os.SystemClock; import android.os.Trace; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -106,6 +111,8 @@ import android.view.animation.Animation; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ToBooleanFunction; +import com.android.server.AttributeCache; +import com.android.server.policy.WindowManagerPolicy; import com.android.server.policy.WindowManagerPolicy.StartingSurface; import com.android.server.wm.WindowManagerService.H; @@ -121,7 +128,8 @@ class AppTokenList extends ArrayList<AppWindowToken> { * Version of WindowToken that is specifically for a particular application (or * really activity) that is displaying windows. */ -class AppWindowToken extends WindowToken implements WindowManagerService.AppFreezeListener { +class AppWindowToken extends WindowToken implements WindowManagerService.AppFreezeListener, + ConfigurationContainerListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "AppWindowToken" : TAG_WM; /** @@ -226,6 +234,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree private Task mLastParent; + // TODO: Remove after unification + ActivityRecord mActivityRecord; + /** * See {@link #canTurnScreenOn()} */ @@ -273,14 +284,20 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree /** Whether this token needs to create mAnimationBoundsLayer for cropping animations. */ boolean mNeedsAnimationBoundsLayer; + private static final int STARTING_WINDOW_TYPE_NONE = 0; + private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1; + private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2; + AppWindowToken(WindowManagerService service, IApplicationToken token, ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint, int configChanges, boolean launchTaskBehind, boolean alwaysFocusable, - AppWindowContainerController controller) { + ActivityRecord activityRecord) { this(service, token, activityComponent, voiceInteraction, dc, fullscreen); - setController(controller); + // TODO: remove after unification + mActivityRecord = activityRecord; + mActivityRecord.registerConfigurationChangeListener(this); mInputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos; mShowForAllUsers = showForAllUsers; mTargetSdk = targetSdk; @@ -320,9 +337,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // it from behind the starting window, so there is no need for it to also be doing its // own stuff. win.cancelAnimation(); - if (getController() != null) { - getController().removeStartingWindow(); - } + removeStartingWindow(); } updateReportedVisibilityLocked(); } @@ -360,16 +375,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } if (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting=" + numInteresting + " visible=" + numVisible); - final AppWindowContainerController controller = getController(); if (nowDrawn != reportedDrawn) { - if (nowDrawn) { - if (controller != null) { - controller.reportWindowsDrawn(); - } - } else { - if (controller != null) { - controller.reportWindowsNotDrawn(); - } + if (mActivityRecord != null) { + mActivityRecord.onWindowsDrawn(nowDrawn, SystemClock.uptimeMillis()); } reportedDrawn = nowDrawn; } @@ -377,16 +385,36 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (DEBUG_VISIBILITY) Slog.v(TAG, "Visibility changed in " + this + ": vis=" + nowVisible); reportedVisible = nowVisible; - if (controller != null) { + if (mActivityRecord != null) { if (nowVisible) { - controller.reportWindowsVisible(); + onWindowsVisible(); } else { - controller.reportWindowsGone(); + onWindowsGone(); } } } } + private void onWindowsGone() { + if (mActivityRecord == null) { + return; + } + if (DEBUG_VISIBILITY) { + Slog.v(TAG_WM, "Reporting gone in " + mActivityRecord.appToken); + } + mActivityRecord.onWindowsGone(); + } + + private void onWindowsVisible() { + if (mActivityRecord == null) { + return; + } + if (DEBUG_VISIBILITY) { + Slog.v(TAG_WM, "Reporting visible in " + mActivityRecord.appToken); + } + mActivityRecord.onWindowsVisible(); + } + boolean isClientHidden() { return mClientHidden; } @@ -401,7 +429,116 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree sendAppVisibilityToClients(); } - boolean setVisibility(WindowManager.LayoutParams lp, + void setVisibility(boolean visible, boolean deferHidingClient) { + final AppTransition appTransition = getDisplayContent().mAppTransition; + + // Don't set visibility to false if we were already not visible. This prevents WM from + // adding the app to the closing app list which doesn't make sense for something that is + // already not visible. However, set visibility to true even if we are already visible. + // This makes sure the app is added to the opening apps list so that the right + // transition can be selected. + // TODO: Probably a good idea to separate the concept of opening/closing apps from the + // concept of setting visibility... + if (!visible && hiddenRequested) { + + if (!deferHidingClient && mDeferHidingClient) { + // We previously deferred telling the client to hide itself when visibility was + // initially set to false. Now we would like it to hide, so go ahead and set it. + mDeferHidingClient = deferHidingClient; + setClientHidden(true); + } + return; + } + + if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) { + Slog.v(TAG_WM, "setAppVisibility(" + + appToken + ", visible=" + visible + "): " + appTransition + + " hidden=" + isHidden() + " hiddenRequested=" + + hiddenRequested + " Callers=" + Debug.getCallers(6)); + } + + final DisplayContent displayContent = getDisplayContent(); + displayContent.mOpeningApps.remove(this); + displayContent.mClosingApps.remove(this); + waitingToShow = false; + hiddenRequested = !visible; + mDeferHidingClient = deferHidingClient; + + if (!visible) { + // If the app is dead while it was visible, we kept its dead window on screen. + // Now that the app is going invisible, we can remove it. It will be restarted + // if made visible again. + removeDeadWindows(); + } else { + if (!appTransition.isTransitionSet() + && appTransition.isReady()) { + // Add the app mOpeningApps if transition is unset but ready. This means + // we're doing a screen freeze, and the unfreeze will wait for all opening + // apps to be ready. + displayContent.mOpeningApps.add(this); + } + startingMoved = false; + // If the token is currently hidden (should be the common case), or has been + // stopped, then we need to set up to wait for its windows to be ready. + if (isHidden() || mAppStopped) { + clearAllDrawn(); + + // If the app was already visible, don't reset the waitingToShow state. + if (isHidden()) { + waitingToShow = true; + } + } + + // In the case where we are making an app visible but holding off for a transition, + // we still need to tell the client to make its windows visible so they get drawn. + // Otherwise, we will wait on performing the transition until all windows have been + // drawn, they never will be, and we are sad. + setClientHidden(false); + + requestUpdateWallpaperIfNeeded(); + + if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + this); + mAppStopped = false; + + transferStartingWindowFromHiddenAboveTokenIfNeeded(); + } + + // If we are preparing an app transition, then delay changing + // the visibility of this token until we execute that transition. + if (okToAnimate() && appTransition.isTransitionSet()) { + inPendingTransaction = true; + if (visible) { + displayContent.mOpeningApps.add(this); + mEnteringAnimation = true; + } else { + displayContent.mClosingApps.add(this); + mEnteringAnimation = false; + } + if (appTransition.getAppTransition() + == WindowManager.TRANSIT_TASK_OPEN_BEHIND) { + // We're launchingBehind, add the launching activity to mOpeningApps. + final WindowState win = getDisplayContent().findFocusedWindow(); + if (win != null) { + final AppWindowToken focusedToken = win.mAppToken; + if (focusedToken != null) { + if (DEBUG_APP_TRANSITIONS) { + Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, " + + " adding " + focusedToken + " to mOpeningApps"); + } + // Force animation to be loaded. + focusedToken.setHidden(true); + displayContent.mOpeningApps.add(focusedToken); + } + } + } + return; + } + + commitVisibility(null, visible, TRANSIT_UNSET, true, mVoiceInteraction); + updateReportedVisibilityLocked(); + } + + boolean commitVisibility(WindowManager.LayoutParams lp, boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) { boolean delayed = false; @@ -461,8 +598,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree forAllWindows(mService::makeWindowFreezingScreenIfNeededLocked, true); } - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "setVisibility: " + this - + ": hidden=" + isHidden() + " hiddenRequested=" + hiddenRequested); + if (DEBUG_APP_TRANSITIONS) { + Slog.v(TAG_WM, "commitVisibility: " + this + + ": hidden=" + isHidden() + " hiddenRequested=" + hiddenRequested); + } if (changed) { getDisplayContent().getInputMonitor().setUpdateInputWindowsNeededLw(); @@ -499,10 +638,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token); } - // If we're becoming visible, immediately change client visibility as well although it - // usually gets changed in AppWindowContainerController.setVisibility already. However, - // there seem to be some edge cases where we change our visibility but client visibility - // never gets updated. + // If we're becoming visible, immediately change client visibility as well. there seem + // to be some edge cases where we change our visibility but client visibility never gets + // updated. // If we're becoming invisible, update the client visibility if we are not running an // animation. Otherwise, we'll update client visibility in onAnimationFinished. if (visible || !isReallyAnimating()) { @@ -596,11 +734,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return getWindowConfiguration().canReceiveKeys() || mAlwaysFocusable; } - AppWindowContainerController getController() { - final WindowContainerController controller = super.getController(); - return controller != null ? (AppWindowContainerController) controller : null; - } - @Override boolean isVisible() { // If the app token isn't hidden then it is considered visible and there is no need to check @@ -637,7 +770,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app token: " + this); - boolean delayed = setVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction); + boolean delayed = commitVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction); getDisplayContent().mOpeningApps.remove(this); getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this); @@ -656,8 +789,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: " + this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4)); - if (startingData != null && getController() != null) { - getController().removeStartingWindow(); + if (startingData != null) { + removeStartingWindow(); } // If this window was animating, then we need to ensure that the app transition notifies @@ -768,9 +901,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree mAppStopped = true; destroySurfaces(); // Remove any starting window that was added for this app if they are still around. - if (getController() != null) { - getController().removeStartingWindow(); - } + removeStartingWindow(); } void clearAllDrawn() { @@ -826,9 +957,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // TODO: Something smells about the code below...Is there a better way? if (startingWindow == win) { if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win); - if (getController() != null) { - getController().removeStartingWindow(); - } + removeStartingWindow(); } else if (mChildren.size() == 0) { // If this is the last window and we had requested a starting transition window, // well there is no point now. @@ -845,9 +974,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // we need to get rid of the starting transition. if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Last window, removing starting window " + win); - if (getController() != null) { - getController().removeStartingWindow(); - } + removeStartingWindow(); } } @@ -1021,6 +1148,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree @Override void removeChild(WindowState child) { + if (!mChildren.contains(child)) { + // This can be true when testing. + return; + } super.removeChild(child); checkKeyguardFlagsChanged(); updateLetterboxSurface(child); @@ -1042,6 +1173,20 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } + void reparent(TaskWindowContainerController taskController, int position) { + if (DEBUG_ADD_REMOVE) { + Slog.i(TAG_WM, "reparent: moving app token=" + this + + " to task=" + taskController + " at " + position); + } + final Task task = taskController.mContainer; + if (task == null) { + throw new IllegalArgumentException("reparent: could not find task=" + + taskController); + } + reparent(task, position); + getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); + } + void reparent(Task task, int position) { final Task currentTask = getTask(); if (task == currentTask) { @@ -1300,9 +1445,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree startingData = fromToken.startingData; fromToken.startingData = null; fromToken.startingMoved = true; - if (getController() != null) { - getController().scheduleAddStartingWindow(); - } + scheduleAddStartingWindow(); return true; } @@ -1471,6 +1614,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } + boolean keyDispatchingTimedOut(String reason, int windowPid) { + return mActivityRecord != null && mActivityRecord.keyDispatchingTimedOut(reason, windowPid); + } + /** * Updated this app token tracking states for interesting and drawn windows based on the window. * @@ -1533,8 +1680,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } } else if (w.isDrawnLw()) { - if (getController() != null) { - getController().reportStartingWindowDrawn(); + if (mActivityRecord != null) { + mActivityRecord.onStartingWindowDrawn(SystemClock.uptimeMillis()); } startingDisplayed = true; } @@ -1601,6 +1748,266 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return this; } + boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo, + CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, + IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning, + boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) { + // If the display is frozen, we won't do anything until the actual window is + // displayed so there is no reason to put in the starting window. + if (!okToDisplay()) { + return false; + } + + if (startingData != null) { + return false; + } + + final WindowState mainWin = findMainWindow(); + if (mainWin != null && mainWin.mWinAnimator.getShown()) { + // App already has a visible window...why would you want a starting window? + return false; + } + + final ActivityManager.TaskSnapshot snapshot = + mService.mTaskSnapshotController.getSnapshot( + getTask().mTaskId, getTask().mUserId, + false /* restoreFromDisk */, false /* reducedResolution */); + final int type = getStartingWindowType(newTask, taskSwitch, processRunning, + allowTaskSnapshot, activityCreated, fromRecents, snapshot); + + if (type == STARTING_WINDOW_TYPE_SNAPSHOT) { + return createSnapshot(snapshot); + } + + // If this is a translucent window, then don't show a starting window -- the current + // effect (a full-screen opaque starting window that fades away to the real contents + // when it is ready) does not work for this. + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG, "Checking theme of starting window: 0x" + Integer.toHexString(theme)); + } + if (theme != 0) { + AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme, + com.android.internal.R.styleable.Window, + mService.mCurrentUserId); + if (ent == null) { + // Whoops! App doesn't exist. Um. Okay. We'll just pretend like we didn't + // see that. + return false; + } + final boolean windowIsTranslucent = ent.array.getBoolean( + com.android.internal.R.styleable.Window_windowIsTranslucent, false); + final boolean windowIsFloating = ent.array.getBoolean( + com.android.internal.R.styleable.Window_windowIsFloating, false); + final boolean windowShowWallpaper = ent.array.getBoolean( + com.android.internal.R.styleable.Window_windowShowWallpaper, false); + final boolean windowDisableStarting = ent.array.getBoolean( + com.android.internal.R.styleable.Window_windowDisablePreview, false); + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG, "Translucent=" + windowIsTranslucent + + " Floating=" + windowIsFloating + + " ShowWallpaper=" + windowShowWallpaper); + } + if (windowIsTranslucent) { + return false; + } + if (windowIsFloating || windowDisableStarting) { + return false; + } + if (windowShowWallpaper) { + if (getDisplayContent().mWallpaperController + .getWallpaperTarget() == null) { + // If this theme is requesting a wallpaper, and the wallpaper + // is not currently visible, then this effectively serves as + // an opaque window and our starting window transition animation + // can still work. We just need to make sure the starting window + // is also showing the wallpaper. + windowFlags |= FLAG_SHOW_WALLPAPER; + } else { + return false; + } + } + + if (transferStartingWindow(transferFrom)) { + return true; + } + + // There is no existing starting window, and we don't want to create a splash screen, so + // that's it! + if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) { + return false; + } + + if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData"); + startingData = new SplashScreenStartingData(mService, pkg, + theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, + getMergedOverrideConfiguration()); + scheduleAddStartingWindow(); + } + return true; + } + + + private boolean createSnapshot(ActivityManager.TaskSnapshot snapshot) { + if (snapshot == null) { + return false; + } + + if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData"); + startingData = new SnapshotStartingData(mService, snapshot); + scheduleAddStartingWindow(); + return true; + } + + void scheduleAddStartingWindow() { + // Note: we really want to do sendMessageAtFrontOfQueue() because we + // want to process the message ASAP, before any other queued + // messages. + if (!mService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) { + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Enqueueing ADD_STARTING"); + mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow); + } + } + + private final Runnable mAddStartingWindow = new Runnable() { + + @Override + public void run() { + synchronized (mService.mGlobalLock) { + // There can only be one adding request, silly caller! + mService.mAnimationHandler.removeCallbacks(this); + } + + if (startingData == null) { + // Animation has been canceled... do nothing. + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG, "startingData was nulled out before handling" + + " mAddStartingWindow: " + AppWindowToken.this); + } + return; + } + + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG, "Add starting " + this + ": startingData=" + startingData); + } + + WindowManagerPolicy.StartingSurface surface = null; + try { + surface = startingData.createStartingSurface(AppWindowToken.this); + } catch (Exception e) { + Slog.w(TAG, "Exception when adding starting window", e); + } + if (surface != null) { + boolean abort = false; + synchronized (mService.mGlobalLock) { + // If the window was successfully added, then + // we need to remove it. + if (removed || startingData == null) { + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG, "Aborted starting " + AppWindowToken.this + + ": removed=" + removed + " startingData=" + startingData); + } + startingWindow = null; + startingData = null; + abort = true; + } else { + startingSurface = surface; + } + if (DEBUG_STARTING_WINDOW && !abort) { + Slog.v(TAG, "Added starting " + AppWindowToken.this + ": startingWindow=" + + startingWindow + " startingView=" + startingSurface); + } + } + if (abort) { + surface.remove(); + } + } else if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG, "Surface returned was null: " + AppWindowToken.this); + } + } + }; + + private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning, + boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents, + ActivityManager.TaskSnapshot snapshot) { + if (getDisplayContent().mAppTransition.getAppTransition() + == TRANSIT_DOCK_TASK_FROM_RECENTS) { + // TODO(b/34099271): Remove this statement to add back the starting window and figure + // out why it causes flickering, the starting window appears over the thumbnail while + // the docked from recents transition occurs + return STARTING_WINDOW_TYPE_NONE; + } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) { + return STARTING_WINDOW_TYPE_SPLASH_SCREEN; + } else if (taskSwitch && allowTaskSnapshot) { + return snapshot == null ? STARTING_WINDOW_TYPE_NONE + : snapshotOrientationSameAsTask(snapshot) || fromRecents + ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN; + } else { + return STARTING_WINDOW_TYPE_NONE; + } + } + + + private boolean snapshotOrientationSameAsTask(ActivityManager.TaskSnapshot snapshot) { + if (snapshot == null) { + return false; + } + return getTask().getConfiguration().orientation == snapshot.getOrientation(); + } + + void removeStartingWindow() { + if (startingWindow == null) { + if (startingData != null) { + // Starting window has not been added yet, but it is scheduled to be added. + // Go ahead and cancel the request. + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG_WM, "Clearing startingData for token=" + this); + } + startingData = null; + } + return; + } + + final WindowManagerPolicy.StartingSurface surface; + if (startingData != null) { + surface = startingSurface; + startingData = null; + startingSurface = null; + startingWindow = null; + startingDisplayed = false; + if (surface == null) { + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG_WM, "startingWindow was set but startingSurface==null, couldn't " + + "remove"); + } + return; + } + } else { + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG_WM, "Tried to remove starting window but startingWindow was null:" + + this); + } + return; + } + + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG_WM, "Schedule remove starting " + this + + " startingWindow=" + startingWindow + + " startingView=" + startingSurface + + " Callers=" + Debug.getCallers(5)); + } + + // Use the same thread to remove the window as we used to add it, as otherwise we end up + // with things in the view hierarchy being called from different threads. + mService.mAnimationHandler.post(() -> { + if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Removing startingView=" + surface); + try { + surface.remove(); + } catch (Exception e) { + Slog.w(TAG_WM, "Exception when removing starting window", e); + } + }); + } + @Override boolean fillsParent() { return mFillsParent; @@ -1754,10 +2161,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.i(TAG, "Creating animation bounds layer"); final SurfaceControl.Builder builder = makeAnimationLeash() .setParent(getAnimationLeashParent()) - .setName(getSurfaceControl() + " - animation-bounds") - .setSize(getSurfaceWidth(), getSurfaceHeight()); + .setName(getSurfaceControl() + " - animation-bounds"); final SurfaceControl boundsLayer = builder.build(); - t.setWindowCrop(boundsLayer, getSurfaceWidth(), getSurfaceHeight()); t.show(boundsLayer); return boundsLayer; } @@ -2215,9 +2620,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (mPendingRelaunchCount != 0) { pw.print(prefix); pw.print("mPendingRelaunchCount="); pw.println(mPendingRelaunchCount); } - if (getController() != null) { - pw.print(prefix); pw.print("controller="); pw.println(getController()); - } if (mRemovingFromDisplay) { pw.println(prefix + "mRemovingFromDisplay=" + mRemovingFromDisplay); } diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java index 9633864ed77e..c90f5bfb7ee0 100644 --- a/services/core/java/com/android/server/wm/BlackFrame.java +++ b/services/core/java/com/android/server/wm/BlackFrame.java @@ -48,7 +48,6 @@ public class BlackFrame { surface = dc.makeOverlay() .setName("BlackSurface") - .setSize(w, h) .setColorLayer(true) .setParent(null) // TODO: Work-around for b/69259549 .build(); diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java index 2a216abbe4ac..c3d621124afc 100644 --- a/services/core/java/com/android/server/wm/CircularDisplayMask.java +++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java @@ -69,7 +69,7 @@ class CircularDisplayMask { try { ctrl = dc.makeOverlay() .setName("CircularDisplayMask") - .setSize(mScreenSize.x, mScreenSize.y) // not a typo + .setBufferSize(mScreenSize.x, mScreenSize.y) // not a typo .setFormat(PixelFormat.TRANSLUCENT) .build(); diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java index cc14afce7ff6..fa3c7ca29284 100644 --- a/services/core/java/com/android/server/wm/Dimmer.java +++ b/services/core/java/com/android/server/wm/Dimmer.java @@ -308,7 +308,6 @@ class Dimmer { return false; } else { // TODO: Once we use geometry from hierarchy this falls away. - t.setSize(mDimState.mDimLayer, bounds.width(), bounds.height()); t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top); t.setWindowCrop(mDimState.mDimLayer, bounds.width(), bounds.height()); if (!mDimState.isVisible) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 478340d85d01..c0e983653b27 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -34,6 +34,7 @@ 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.View.GONE; +import static android.view.InsetsState.TYPE_IME; import static android.view.WindowManager.DOCKED_BOTTOM; import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.DOCKED_TOP; @@ -78,7 +79,6 @@ import static com.android.server.wm.DisplayContentProto.PINNED_STACK_CONTROLLER; import static com.android.server.wm.DisplayContentProto.ROTATION; import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION; import static com.android.server.wm.DisplayContentProto.STACKS; -import static com.android.server.wm.DisplayContentProto.SURFACE_SIZE; import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; @@ -124,6 +124,7 @@ import android.animation.AnimationHandler; import android.annotation.CallSuper; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; @@ -151,6 +152,7 @@ import android.view.DisplayInfo; import android.view.Gravity; import android.view.InputChannel; import android.view.InputDevice; +import android.view.InsetsState.InternalInsetType; import android.view.MagnificationSpec; import android.view.Surface; import android.view.SurfaceControl; @@ -162,6 +164,7 @@ import android.view.WindowManagerPolicyConstants.PointerEventListener; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ToBooleanFunction; +import com.android.internal.util.function.TriConsumer; import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.utils.DisplayRotationUtil; import com.android.server.wm.utils.RotationCache; @@ -470,14 +473,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private SurfaceControl mWindowingLayer; /** - * Specifies the size of the surfaces in {@link #mOverlayLayer} and {@link #mWindowingLayer}. - * <p> - * For these surfaces currently we use a surface based on the larger of width or height so we - * don't have to resize when rotating the display. - */ - private int mSurfaceSize; - - /** * Sequence number for the current layout pass. */ int mLayoutSeq = 0; @@ -515,6 +510,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final PointerEventDispatcher mPointerEventDispatcher; + private final InsetsStateController mInsetsStateController; + // Last systemUiVisibility we received from status bar. private int mLastStatusBarVisibility = 0; // Last systemUiVisibility we dispatched to windows. @@ -884,18 +881,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo 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 - // due to surfaceInsets. 2 times the largest display dimension feels like an - // appropriately arbitrary number. Eventually we would like to give SurfaceFlinger - // layers the ability to match their parent sizes and be able to skip - // such arbitrary size settings. - mSurfaceSize = Math.max(mBaseDisplayHeight, mBaseDisplayWidth) * 2; - - final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession) - .setSize(mSurfaceSize, mSurfaceSize) - .setOpaque(true); + final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession).setOpaque(true); mWindowingLayer = b.setName("Display Root").build(); mOverlayLayer = b.setName("Display Overlays").build(); @@ -922,6 +908,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mService.mAnimator.addDisplayLocked(mDisplayId); mInputMonitor = new InputMonitor(service, mDisplayId); + mInsetsStateController = new InsetsStateController(this); } boolean isReady() { @@ -1058,6 +1045,23 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mDisplayRotation; } + /** + * Marks a window as providing insets for the rest of the windows in the system. + * + * @param type The type of inset this window provides. + * @param win The window. + * @param frameProvider Function to compute the frame, or {@code null} if the just the frame of + * the window should be taken. + */ + void setInsetProvider(@InternalInsetType int type, WindowState win, + @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) { + mInsetsStateController.getSourceProvider(type).setWindow(win, frameProvider); + } + + InsetsStateController getInsetsStateController() { + return mInsetsStateController; + } + @VisibleForTesting void setDisplayRotation(DisplayRotation displayRotation) { mDisplayRotation = displayRotation; @@ -2612,7 +2616,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } mDisplayFrames.writeToProto(proto, DISPLAY_FRAMES); mAppTransition.writeToProto(proto, APP_TRANSITION); - proto.write(SURFACE_SIZE, mSurfaceSize); if (mFocusedApp != null) { mFocusedApp.writeNameToProto(proto, FOCUSED_APP); } @@ -2733,6 +2736,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mDisplayRotation.dump(prefix, pw); pw.println(); mInputMonitor.dump(pw, " "); + pw.println(); + mInsetsStateController.dump(prefix, pw); } @Override @@ -3015,6 +3020,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mInputMethodWindow.getDisplayId()); } computeImeTarget(true /* updateImeTarget */); + mInsetsStateController.getSourceProvider(TYPE_IME).setWindow(win, + null /* frameProvider */); } /** @@ -3470,6 +3477,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw(); if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats( "after finishPostLayoutPolicyLw", pendingLayoutChanges); + mInsetsStateController.onPostLayout(); } while (pendingLayoutChanges != 0); mTmpApplySurfaceChangesTransactionState.reset(); @@ -3537,10 +3545,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } - int getSurfaceSize() { - return mSurfaceSize; - } - void performLayout(boolean initial, boolean updateInputWindows) { if (!isLayoutNeeded()) { return; @@ -4483,8 +4487,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo SurfaceControl.Builder makeChildSurface(WindowContainer child) { SurfaceSession s = child != null ? child.getSession() : getSession(); final SurfaceControl.Builder b = mService.makeSurfaceBuilder(s); - b.setSize(mSurfaceSize, mSurfaceSize); - if (child == null) { return b; } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index c16f95ee1160..0e5947af0c61 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -25,6 +25,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECOND 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.InsetsState.TYPE_TOP_BAR; 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; @@ -128,6 +129,7 @@ import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; import android.view.InputEventReceiver; +import android.view.InsetsState; import android.view.MotionEvent; import android.view.PointerIcon; import android.view.Surface; @@ -804,6 +806,11 @@ public class DisplayPolicy { if (mDisplayContent.isDefaultDisplay) { mService.mPolicy.setKeyguardCandidateLw(win); } + mDisplayContent.setInsetProvider(TYPE_TOP_BAR, win, + (displayFrames, windowState, rect) -> { + rect.top = 0; + rect.bottom = getStatusBarHeight(displayFrames); + }); break; case TYPE_NAVIGATION_BAR: mContext.enforceCallingOrSelfPermission( @@ -818,6 +825,8 @@ public class DisplayPolicy { mNavigationBarController.setWindow(win); mNavigationBarController.setOnBarVisibilityChangedListener( mNavBarVisibilityListener, true); + mDisplayContent.setInsetProvider(InsetsState.TYPE_NAVIGATION_BAR, + win, null /* frameProvider */); if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar); break; case TYPE_NAVIGATION_BAR_PANEL: @@ -845,9 +854,11 @@ public class DisplayPolicy { if (mDisplayContent.isDefaultDisplay) { mService.mPolicy.setKeyguardCandidateLw(null); } + mDisplayContent.setInsetProvider(TYPE_TOP_BAR, null, null); } else if (mNavigationBar == win) { mNavigationBar = null; mNavigationBarController.setWindow(null); + mDisplayContent.setInsetProvider(InsetsState.TYPE_NAVIGATION_BAR, null, null); } if (mLastFocusedWindow == win) { mLastFocusedWindow = null; @@ -855,6 +866,11 @@ public class DisplayPolicy { mScreenDecorWindows.remove(win); } + private int getStatusBarHeight(DisplayFrames displayFrames) { + return Math.max(mStatusBarHeightForRotation[displayFrames.mRotation], + displayFrames.mDisplayCutoutSafe.top); + } + /** * 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 diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index f1d1e49c1004..7aabc15d9860 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -21,6 +21,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.annotation.UserIdInt; import android.app.ActivityManager; import android.content.ContentResolver; import android.content.Context; @@ -30,6 +31,7 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.power.V1_0.PowerHint; +import android.net.Uri; import android.os.Handler; import android.os.SystemProperties; import android.os.UserHandle; @@ -57,6 +59,7 @@ public class DisplayRotation { private final WindowManagerService mService; private final DisplayContent mDisplayContent; private final DisplayPolicy mDisplayPolicy; + private final DisplayWindowSettings mDisplayWindowSettings; private final Context mContext; private final Object mLock; @@ -71,10 +74,6 @@ public class DisplayRotation { private StatusBarManagerInternal mStatusBarManagerInternal; private SettingsObserver mSettingsObserver; - // Default display does not rotate, apps that require non-default orientation will have to - // have the orientation emulated. - private boolean mForceDefaultOrientation; - private int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @VisibleForTesting @@ -93,6 +92,13 @@ public class DisplayRotation { private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE; private int mUserRotation = Surface.ROTATION_0; + /** + * A flag to indicate if the display rotation should be fixed to user specified rotation + * regardless of all other states (including app requrested orientation). {@code true} the + * display rotation should be fixed to user specified rotation, {@code false} otherwise. + */ + private boolean mFixedToUserRotation; + private int mDemoHdmiRotation; private int mDemoRotation; private boolean mDemoHdmiRotationLock; @@ -100,15 +106,17 @@ public class DisplayRotation { DisplayRotation(WindowManagerService service, DisplayContent displayContent) { this(service, displayContent, displayContent.getDisplayPolicy(), - service.mContext, service.getWindowManagerLock()); + service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock()); } @VisibleForTesting DisplayRotation(WindowManagerService service, DisplayContent displayContent, - DisplayPolicy displayPolicy, Context context, Object lock) { + DisplayPolicy displayPolicy, DisplayWindowSettings displayWindowSettings, + Context context, Object lock) { mService = service; mDisplayContent = displayContent; mDisplayPolicy = displayPolicy; + mDisplayWindowSettings = displayWindowSettings; mContext = context; mLock = lock; isDefaultDisplay = displayContent.isDefaultDisplay; @@ -204,12 +212,19 @@ public class DisplayRotation { // so if the orientation is forced, we need to respect that no matter what. final boolean isTv = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_LEANBACK); - mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) && - res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) && - // For debug purposes the next line turns this feature off with: - // $ adb shell setprop config.override_forced_orient true - // $ adb shell wm size reset - !"true".equals(SystemProperties.get("config.override_forced_orient")); + final boolean forceDefaultOrientationInRes = + res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation); + final boolean forceDefaultOrienation = + ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) + && forceDefaultOrientationInRes + // For debug purposes the next line turns this feature off with: + // $ adb shell setprop config.override_forced_orient true + // $ adb shell wm size reset + && !"true".equals(SystemProperties.get("config.override_forced_orient")); + // Configuration says we force to use the default orientation. We can fall back to fix + // rotation to only user rotation. As long as OEM doesn't change user rotation then the + // rotation of this display is effectively stuck at 0 deg. + setFixedToUserRotation(forceDefaultOrienation); } void setRotation(int rotation) { @@ -227,7 +242,14 @@ public class DisplayRotation { } } - void restoreUserRotation(int userRotationMode, int userRotation) { + void restoreSettings(int userRotationMode, int userRotation, + boolean fixedToUserRotation) { + mFixedToUserRotation = fixedToUserRotation; + + // We will retrieve user rotation and user rotation mode from settings for default display. + if (isDefaultDisplay) { + return; + } if (userRotationMode != WindowManagerPolicy.USER_ROTATION_FREE && userRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) { Slog.w(TAG, "Trying to restore an invalid user rotation mode " + userRotationMode @@ -243,6 +265,18 @@ public class DisplayRotation { mUserRotation = userRotation; } + void setFixedToUserRotation(boolean fixedToUserRotation) { + if (mFixedToUserRotation == fixedToUserRotation) { + return; + } + + mFixedToUserRotation = fixedToUserRotation; + mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent, + fixedToUserRotation); + mService.updateRotation(true /* alwaysSendConfiguration */, + false /* forceRelayout */); + } + private void setUserRotation(int userRotationMode, int userRotation) { if (isDefaultDisplay) { // We'll be notified via settings listener, so we don't need to update internal values. @@ -265,7 +299,7 @@ public class DisplayRotation { mUserRotation = userRotation; changed = true; } - mService.mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode, + mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode, userRotation); if (changed) { mService.updateRotation(true /* alwaysSendConfiguration */, @@ -291,9 +325,8 @@ public class DisplayRotation { Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0; } - /** @return true if com.android.internal.R.bool#config_forceDefaultOrientation is true. */ - boolean isDefaultOrientationForced() { - return mForceDefaultOrientation; + boolean isFixedToUserRotation() { + return mFixedToUserRotation; } public int getLandscapeRotation() { @@ -399,6 +432,12 @@ public class DisplayRotation { * screen is switched off. */ private boolean needSensorRunning() { + if (mFixedToUserRotation) { + // We are sure we only respect user rotation settings, so we are sure we will not + // support sensor rotation. + return false; + } + if (mSupportAutoRotation) { if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR @@ -459,8 +498,8 @@ public class DisplayRotation { ); } - if (mForceDefaultOrientation) { - return Surface.ROTATION_0; + if (mFixedToUserRotation) { + return mUserRotation; } int sensorRotation = mOrientationListener != null @@ -701,8 +740,8 @@ public class DisplayRotation { // demo, hdmi, vr, etc mode. // Determine if the rotation is currently forced. - if (mForceDefaultOrientation) { - return false; // Rotation is forced to default orientation. + if (mFixedToUserRotation) { + return false; // Rotation is forced to user settings. } final int lidState = mDisplayPolicy.getLidState(); @@ -861,6 +900,7 @@ public class DisplayRotation { pw.print(" mDemoHdmiRotationLock=" + mDemoHdmiRotationLock); pw.println(" mUndockedHdmiRotation=" + Surface.rotationToString(mUndockedHdmiRotation)); pw.println(prefix + " mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation)); + pw.println(prefix + " mFixedToUserRotation=" + mFixedToUserRotation); } private class OrientationListener extends WindowOrientationListener { @@ -945,4 +985,10 @@ public class DisplayRotation { } } } + + @VisibleForTesting + interface ContentObserverRegister { + void registerContentObserver(Uri uri, boolean notifyForDescendants, + ContentObserver observer, @UserIdInt int userHandle); + } } diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java index f7dfd3ffc8bf..45d77dee1851 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java @@ -80,6 +80,7 @@ class DisplayWindowSettings { private boolean mShouldShowWithInsecureKeyguard = false; private boolean mShouldShowSystemDecors = false; private boolean mShouldShowIme = false; + private boolean mFixedToUserRotation; private Entry(String name) { mName = name; @@ -97,7 +98,8 @@ class DisplayWindowSettings { && mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED && !mShouldShowWithInsecureKeyguard && !mShouldShowSystemDecors - && !mShouldShowIme; + && !mShouldShowIme + && !mFixedToUserRotation; } } @@ -186,6 +188,13 @@ class DisplayWindowSettings { writeSettingsIfNeeded(entry, displayInfo); } + void setFixedToUserRotation(DisplayContent displayContent, boolean fixedToUserRotation) { + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + final Entry entry = getOrCreateEntry(displayInfo); + entry.mFixedToUserRotation = fixedToUserRotation; + writeSettingsIfNeeded(entry, displayInfo); + } + private int getWindowingModeLocked(Entry entry, int displayId) { int windowingMode = entry != null ? entry.mWindowingMode : WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -331,7 +340,8 @@ class DisplayWindowSettings { displayInfo.overscanRight = entry.mOverscanRight; displayInfo.overscanBottom = entry.mOverscanBottom; - dc.getDisplayRotation().restoreUserRotation(entry.mUserRotationMode, entry.mUserRotation); + dc.getDisplayRotation().restoreSettings(entry.mUserRotationMode, + entry.mUserRotation, entry.mFixedToUserRotation); if (entry.mForcedDensity != 0) { dc.mBaseDisplayDensity = entry.mForcedDensity; @@ -458,6 +468,8 @@ class DisplayWindowSettings { "shouldShowWithInsecureKeyguard"); entry.mShouldShowSystemDecors = getBooleanAttribute(parser, "shouldShowSystemDecors"); entry.mShouldShowIme = getBooleanAttribute(parser, "shouldShowIme"); + entry.mFixedToUserRotation = getBooleanAttribute(parser, + "fixedToUserRotation"); mEntries.put(name, entry); } XmlUtils.skipCurrentTag(parser); @@ -541,6 +553,10 @@ class DisplayWindowSettings { if (entry.mShouldShowIme) { out.attribute(null, "shouldShowIme", Boolean.toString(entry.mShouldShowIme)); } + if (entry.mFixedToUserRotation) { + out.attribute(null, "fixedToUserRotation", + Boolean.toString(entry.mFixedToUserRotation)); + } out.endTag(null, "display"); } diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index 9832df6ff3a3..8f6ed85d122d 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -155,7 +155,7 @@ class DragState { if (mInputSurface == null) { mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId) .getSession()).setContainerLayer(true) - .setName("Drag and Drop Input Consumer").setSize(1, 1).build(); + .setName("Drag and Drop Input Consumer").build(); } final InputWindowHandle h = getInputWindowHandle(); if (h == null) { @@ -169,7 +169,7 @@ class DragState { t.setLayer(mInputSurface, Integer.MAX_VALUE); mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y); - t.setWindowCrop(mSurfaceControl, mTmpClipRect); + t.setWindowCrop(mInputSurface, mTmpClipRect); } /** diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java index fddf6ca2a698..7cb4a43a9ede 100644 --- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java +++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java @@ -16,7 +16,6 @@ package com.android.server.wm; - import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -32,7 +31,6 @@ import android.view.Display; import android.view.Surface; import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl; -import android.view.SurfaceSession; class EmulatorDisplayOverlay { private static final String TAG = TAG_WITH_CLASS_NAME ? "EmulatorDisplayOverlay" : TAG_WM; @@ -59,7 +57,7 @@ class EmulatorDisplayOverlay { try { ctrl = dc.makeOverlay() .setName("EmulatorDisplayOverlay") - .setSize(mScreenSize.x, mScreenSize.y) + .setBufferSize(mScreenSize.x, mScreenSize.y) .setFormat(PixelFormat.TRANSLUCENT) .build(); ctrl.setLayer(zOrder); diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java index 8140820871da..4df5a0b5ad9e 100644 --- a/services/core/java/com/android/server/wm/InputConsumerImpl.java +++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java @@ -90,7 +90,6 @@ class InputConsumerImpl implements IBinder.DeathRecipient { mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId) .getSession()).setContainerLayer(true).setName("Input Consumer " + name) - .setSize(1, 1) .build(); } diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index 94355395b38e..639ed02a1e48 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -6,21 +6,16 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; -import android.app.ActivityManager; import android.os.Debug; import android.os.IBinder; -import android.os.RemoteException; import android.util.Slog; +import android.view.InputApplicationHandle; import android.view.KeyEvent; import android.view.WindowManager; -import android.view.InputApplicationHandle; import com.android.server.input.InputManagerService; -import android.view.InputWindowHandle; -import android.view.InputChannel; import java.io.PrintWriter; -import java.util.HashMap; final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks { private final WindowManagerService mService; @@ -112,9 +107,7 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal if (appWindowToken != null && appWindowToken.appToken != null) { // Notify the activity manager about the timeout and let it decide whether // to abort dispatching or keep waiting. - final AppWindowContainerController controller = appWindowToken.getController(); - final boolean abort = controller != null - && controller.keyDispatchingTimedOut(reason, + final boolean abort = appWindowToken.keyDispatchingTimedOut(reason, (windowState != null) ? windowState.mSession.mPid : -1); if (!abort) { // The activity manager declined to abort dispatching. diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 55cbae15f9d8..88b22cb5e01e 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -435,10 +435,10 @@ final class InputMonitor { if (mAddPipInputConsumerHandle) { // Update the bounds of the Pip input consumer to match the window bounds. w.getBounds(mTmpRect); - // The touchable region is relative to the surface top-left - mTmpRect.top = mTmpRect.left = 0; - pipInputConsumer.layout(mInputTransaction, mTmpRect); + + // The touchable region is relative to the surface top-left + mTmpRect.offsetTo(0, 0); pipInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect); pipInputConsumer.show(mInputTransaction, w); mAddPipInputConsumerHandle = false; diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java new file mode 100644 index 000000000000..e96f0b1c4416 --- /dev/null +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -0,0 +1,87 @@ +/* + * 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 android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.Rect; +import android.view.InsetsSource; + +import com.android.internal.util.function.TriConsumer; +import com.android.server.policy.WindowManagerPolicy; + +/** + * Controller for a specific inset source on the server. It's called provider as it provides the + * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}. + */ +class InsetsSourceProvider { + + private final Rect mTmpRect = new Rect(); + private final @NonNull InsetsSource mSource; + private WindowState mWin; + private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider; + + InsetsSourceProvider(InsetsSource source) { + mSource = source; + } + + InsetsSource getSource() { + return mSource; + } + + /** + * Updates the window that currently backs this source. + * + * @param win The window that links to this source. + * @param frameProvider Based on display frame state and the window, calculates the resulting + * frame that should be reported to clients. + */ + void setWindow(@Nullable WindowState win, + @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) { + if (mWin != null) { + mWin.setInsetProvider(null); + } + mWin = win; + mFrameProvider = frameProvider; + if (win == null) { + mSource.setVisible(false); + mSource.setFrame(new Rect()); + } else { + mSource.setVisible(true); + mWin.setInsetProvider(this); + } + } + + /** + * Called when a layout pass has occurred. + */ + void onPostLayout() { + if (mWin == null) { + return; + } + + mTmpRect.set(mWin.getFrameLw()); + if (mFrameProvider != null) { + mFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin, mTmpRect); + } else { + mTmpRect.inset(mWin.mGivenContentInsets); + } + mSource.setFrame(mTmpRect); + mSource.setVisible(mWin.isVisible() && !mWin.mGivenInsetsPending); + + } +} diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java new file mode 100644 index 000000000000..1189ee660605 --- /dev/null +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -0,0 +1,104 @@ +/* + * 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.InsetsState.TYPE_IME; +import static android.view.InsetsState.TYPE_NAVIGATION_BAR; +import static android.view.InsetsState.TYPE_TOP_BAR; + +import android.util.ArrayMap; +import android.view.InsetsState; + +import java.io.PrintWriter; +import java.util.function.Consumer; + +/** + * Manages global window inset state in the system represented by {@link InsetsState}. + */ +class InsetsStateController { + + private final InsetsState mLastState = new InsetsState(); + private final InsetsState mState = new InsetsState(); + private final DisplayContent mDisplayContent; + private ArrayMap<Integer, InsetsSourceProvider> mControllers = new ArrayMap<>(); + + private final Consumer<WindowState> mDispatchInsetsChanged = w -> { + if (w.isVisible()) { + w.notifyInsetsChanged(); + } + }; + + InsetsStateController(DisplayContent displayContent) { + mDisplayContent = displayContent; + } + + /** + * When dispatching window state to the client, we'll need to exclude the source that represents + * the window that is being dispatched. + * + * @param target The client we dispatch the state to. + * @return The state stripped of the necessary information. + */ + InsetsState getInsetsForDispatch(WindowState target) { + final InsetsSourceProvider provider = target.getInsetProvider(); + if (provider == null) { + return mState; + } + + final InsetsState state = new InsetsState(); + state.set(mState); + final int type = provider.getSource().getType(); + state.removeSource(type); + + // Navigation bar doesn't get influenced by anything else + if (type == TYPE_NAVIGATION_BAR) { + state.removeSource(TYPE_IME); + state.removeSource(TYPE_TOP_BAR); + } + return state; + } + + /** + * @return The provider of a specific type. + */ + InsetsSourceProvider getSourceProvider(int type) { + return mControllers.computeIfAbsent(type, + key -> new InsetsSourceProvider(mState.getSource(key))); + } + + /** + * Called when a layout pass has occurred. + */ + void onPostLayout() { + for (int i = mControllers.size() - 1; i>= 0; i--) { + mControllers.valueAt(i).onPostLayout(); + } + if (!mLastState.equals(mState)) { + mLastState.set(mState, true /* copySources */); + notifyInsetsChanged(); + } + } + + private void notifyInsetsChanged() { + mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */); + } + + void dump(String prefix, PrintWriter pw) { + pw.println(prefix + "WindowInsetsStateController"); + mState.dump(prefix + " ", pw); + } +} diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index c91af73dc6dd..4ef351390c16 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -72,6 +72,7 @@ class KeyguardController { private int mVisibilityTransactionDepth; private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>(); private final ActivityTaskManagerService mService; + private RootActivityContainer mRootActivityContainer; KeyguardController(ActivityTaskManagerService service, ActivityStackSupervisor stackSupervisor) { @@ -81,6 +82,7 @@ class KeyguardController { void setWindowManager(WindowManagerService windowManager) { mWindowManager = windowManager; + mRootActivityContainer = mService.mRootActivityContainer; } /** @@ -146,7 +148,7 @@ class KeyguardController { mDismissalRequested = false; } } - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); updateKeyguardSleepToken(); } @@ -172,16 +174,17 @@ class KeyguardController { mWindowManager.deferSurfaceLayout(); try { setKeyguardGoingAway(true); - mStackSupervisor.getDefaultDisplay().getWindowContainerController() + mRootActivityContainer.getDefaultDisplay().getWindowContainerController() .prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */, convertTransitFlags(flags), false /* forceOverride */); updateKeyguardSleepToken(); // Some stack visibility might change (e.g. docked stack) - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); - mStackSupervisor.addStartingWindowsForVisibleActivities(true /* taskSwitch */); + mRootActivityContainer.resumeFocusedStacksTopActivities(); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.addStartingWindowsForVisibleActivities( + true /* taskSwitch */); mWindowManager.executeAppTransition(); } finally { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout"); @@ -277,8 +280,9 @@ class KeyguardController { private void visibilitiesUpdated() { boolean requestDismissKeyguard = false; - for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) { - final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx); + for (int displayNdx = mRootActivityContainer.getChildCount() - 1; + displayNdx >= 0; displayNdx--) { + final ActivityDisplay display = mRootActivityContainer.getChildAt(displayNdx); final KeyguardDisplayState state = getDisplay(display.mDisplayId); state.visibilitiesUpdated(this, display); requestDismissKeyguard |= state.mRequestDismissKeyguard; @@ -298,12 +302,12 @@ class KeyguardController { if (isKeyguardLocked()) { mWindowManager.deferSurfaceLayout(); try { - mStackSupervisor.getDefaultDisplay().getWindowContainerController() + mRootActivityContainer.getDefaultDisplay().getWindowContainerController() .prepareAppTransition(resolveOccludeTransit(), false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */); updateKeyguardSleepToken(); - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); mWindowManager.executeAppTransition(); } finally { mWindowManager.continueSurfaceLayout(); @@ -319,21 +323,23 @@ class KeyguardController { // We only allow dismissing Keyguard via the flag when Keyguard is secure for legacy // reasons, because that's how apps used to dismiss Keyguard in the secure case. In the // insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded. - if (mWindowManager.isKeyguardSecure()) { - mWindowManager.dismissKeyguard(null /* callback */, null /* message */); - mDismissalRequested = true; - - // If we are about to unocclude the Keyguard, but we can dismiss it without security, - // we immediately dismiss the Keyguard so the activity gets shown without a flicker. - final DisplayWindowController dwc = - mStackSupervisor.getDefaultDisplay().getWindowContainerController(); - if (mKeyguardShowing && canDismissKeyguard() - && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) { - dwc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */, - 0 /* flags */, true /* forceOverride */); - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); - mWindowManager.executeAppTransition(); - } + if (!mWindowManager.isKeyguardSecure()) { + return; + } + + mWindowManager.dismissKeyguard(null /* callback */, null /* message */); + mDismissalRequested = true; + + // If we are about to unocclude the Keyguard, but we can dismiss it without security, + // we immediately dismiss the Keyguard so the activity gets shown without a flicker. + final DisplayWindowController dwc = + mRootActivityContainer.getDefaultDisplay().getWindowContainerController(); + if (mKeyguardShowing && canDismissKeyguard() + && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) { + dwc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */, + 0 /* flags */, true /* forceOverride */); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); + mWindowManager.executeAppTransition(); } } @@ -350,7 +356,7 @@ class KeyguardController { private int resolveOccludeTransit() { final DisplayWindowController dwc = - mStackSupervisor.getDefaultDisplay().getWindowContainerController(); + mRootActivityContainer.getDefaultDisplay().getWindowContainerController(); if (mBeforeUnoccludeTransit != TRANSIT_UNSET && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE // TODO(b/113840485): Handle app transition for individual display. @@ -377,7 +383,8 @@ class KeyguardController { // show on top of the lock screen. In this can we want to dismiss the docked // stack since it will be complicated/risky to try to put the activity on top // of the lock screen in the right fullscreen configuration. - final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack(); + final ActivityStack stack = + mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack(); if (stack == null) { return; } @@ -387,8 +394,9 @@ class KeyguardController { } private void updateKeyguardSleepToken() { - for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) { - final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx); + for (int displayNdx = mRootActivityContainer.getChildCount() - 1; + displayNdx >= 0; displayNdx--) { + final ActivityDisplay display = mRootActivityContainer.getChildAt(displayNdx); final KeyguardDisplayState state = getDisplay(display.mDisplayId); if (isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken == null) { state.acquiredSleepToken(); diff --git a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java new file mode 100644 index 000000000000..93e2d8d6fba4 --- /dev/null +++ b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java @@ -0,0 +1,162 @@ +/* + * 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 android.content.Intent; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; + +import com.android.internal.os.BackgroundThread; +import com.android.internal.util.function.pooled.PooledLambda; + +import java.util.ArrayList; + +/** + * Multi-cast implementation of {@link ActivityMetricsLaunchObserver}. + * + * <br /><br /> + * If this class is called through the {@link ActivityMetricsLaunchObserver} interface, + * then the call is forwarded to all registered observers at the time. + * + * <br /><br /> + * All calls are invoked asynchronously in-order on a background thread. This fulfills the + * sequential ordering guarantee in {@link ActivityMetricsLaunchObserverRegistry}. + * + * @see ActivityTaskManagerInternal#getLaunchObserverRegistry() + */ +class LaunchObserverRegistryImpl implements + ActivityMetricsLaunchObserverRegistry, ActivityMetricsLaunchObserver { + private final ArrayList<ActivityMetricsLaunchObserver> mList = new ArrayList<>(); + + /** + * All calls are posted to a handler because: + * + * 1. We don't know how long the observer will take to handle this call and we don't want + * to block the WM critical section on it. + * 2. We don't know the lock ordering of the observer so we don't want to expose a chance + * of deadlock. + */ + private final Handler mHandler; + + public LaunchObserverRegistryImpl(Looper looper) { + mHandler = new Handler(looper); + } + + @Override + public void registerLaunchObserver(ActivityMetricsLaunchObserver launchObserver) { + mHandler.sendMessage(PooledLambda.obtainMessage( + LaunchObserverRegistryImpl::handleRegisterLaunchObserver, this, launchObserver)); + } + + @Override + public void unregisterLaunchObserver(ActivityMetricsLaunchObserver launchObserver) { + mHandler.sendMessage(PooledLambda.obtainMessage( + LaunchObserverRegistryImpl::handleUnregisterLaunchObserver, this, launchObserver)); + } + + @Override + public void onIntentStarted(Intent intent) { + mHandler.sendMessage(PooledLambda.obtainMessage( + LaunchObserverRegistryImpl::handleOnIntentStarted, this, intent)); + } + + @Override + public void onIntentFailed() { + mHandler.sendMessage(PooledLambda.obtainMessage( + LaunchObserverRegistryImpl::handleOnIntentFailed, this)); + } + + @Override + public void onActivityLaunched( + @ActivityRecordProto byte[] activity, + int temperature) { + mHandler.sendMessage(PooledLambda.obtainMessage( + LaunchObserverRegistryImpl::handleOnActivityLaunched, + this, activity, temperature)); + } + + @Override + public void onActivityLaunchCancelled( + @ActivityRecordProto byte[] activity) { + mHandler.sendMessage(PooledLambda.obtainMessage( + LaunchObserverRegistryImpl::handleOnActivityLaunchCancelled, this, activity)); + } + + @Override + public void onActivityLaunchFinished( + @ActivityRecordProto byte[] activity) { + mHandler.sendMessage(PooledLambda.obtainMessage( + LaunchObserverRegistryImpl::handleOnActivityLaunchFinished, this, activity)); + } + + // Use PooledLambda.obtainMessage to invoke below methods. Every method reference must be + // unbound (i.e. not capture any variables explicitly or implicitly) to fulfill the + // singleton-lambda requirement. + + private void handleRegisterLaunchObserver(ActivityMetricsLaunchObserver observer) { + mList.add(observer); + } + + private void handleUnregisterLaunchObserver(ActivityMetricsLaunchObserver observer) { + mList.remove(observer); + } + + private void handleOnIntentStarted(Intent intent) { + // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee. + for (int i = 0; i < mList.size(); i++) { + ActivityMetricsLaunchObserver o = mList.get(i); + o.onIntentStarted(intent); + } + } + + private void handleOnIntentFailed() { + // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee. + for (int i = 0; i < mList.size(); i++) { + ActivityMetricsLaunchObserver o = mList.get(i); + o.onIntentFailed(); + } + } + + private void handleOnActivityLaunched( + @ActivityRecordProto byte[] activity, + @Temperature int temperature) { + // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee. + for (int i = 0; i < mList.size(); i++) { + ActivityMetricsLaunchObserver o = mList.get(i); + o.onActivityLaunched(activity, temperature); + } + } + + private void handleOnActivityLaunchCancelled( + @ActivityRecordProto byte[] activity) { + // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee. + for (int i = 0; i < mList.size(); i++) { + ActivityMetricsLaunchObserver o = mList.get(i); + o.onActivityLaunchCancelled(activity); + } + } + + private void handleOnActivityLaunchFinished( + @ActivityRecordProto byte[] activity) { + // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee. + for (int i = 0; i < mList.size(); i++) { + ActivityMetricsLaunchObserver o = mList.get(i); + o.onActivityLaunchFinished(activity); + } + } +} diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java index 72d51439d9f7..bc6a690b6e74 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java +++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java @@ -223,7 +223,8 @@ class LaunchParamsPersister { private boolean saveTaskToLaunchParam(TaskRecord task, PersistableLaunchParams params) { final ActivityStack<?> stack = task.getStack(); final int displayId = stack.mDisplayId; - final ActivityDisplay display = mSupervisor.getActivityDisplay(displayId); + final ActivityDisplay display = + mSupervisor.mRootActivityContainer.getActivityDisplay(displayId); final DisplayInfo info = new DisplayInfo(); display.mDisplay.getDisplayInfo(info); @@ -259,7 +260,7 @@ class LaunchParamsPersister { return; } - final ActivityDisplay display = mSupervisor.getActivityDisplay( + final ActivityDisplay display = mSupervisor.mRootActivityContainer.getActivityDisplay( persistableParams.mDisplayUniqueId); if (display != null) { outParams.mPreferredDisplayId = display.mDisplayId; @@ -268,7 +269,7 @@ class LaunchParamsPersister { outParams.mBounds.set(persistableParams.mBounds); } - private void onPackageRemoved(String packageName) { + void removeRecordForPackage(String packageName) { final List<File> fileToDelete = new ArrayList<>(); for (int i = 0; i < mMap.size(); ++i) { int userId = mMap.keyAt(i); @@ -309,7 +310,7 @@ class LaunchParamsPersister { @Override public void onPackageRemoved(String packageName) { - LaunchParamsPersister.this.onPackageRemoved(packageName); + removeRecordForPackage(packageName); } } diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java index b49d304cf9a8..1a2aa2f252e6 100644 --- a/services/core/java/com/android/server/wm/Letterbox.java +++ b/services/core/java/com/android/server/wm/Letterbox.java @@ -186,7 +186,6 @@ public class Letterbox { createSurface(); } t.setPosition(mSurface, mSurfaceFrame.left, mSurfaceFrame.top); - t.setSize(mSurface, mSurfaceFrame.width(), mSurfaceFrame.height()); t.setWindowCrop(mSurface, mSurfaceFrame.width(), mSurfaceFrame.height()); t.show(mSurface); } else if (mSurface != null) { diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index 41d0777d1c78..80dc2458d7f5 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -447,7 +447,7 @@ public class LockTaskController { return; } task.performClearTaskLocked(); - mSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities(); } /** @@ -579,7 +579,7 @@ public class LockTaskController { if (andResume) { mSupervisor.findTaskToMoveToFront(task, 0, null, reason, lockTaskModeState != LOCK_TASK_MODE_NONE); - mSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities(); final ActivityStack stack = task.getStack(); if (stack != null) { stack.getDisplay().getWindowContainerController().executeAppTransition(); @@ -641,11 +641,12 @@ public class LockTaskController { taskChanged = true; } - for (int displayNdx = mSupervisor.getChildCount() - 1; displayNdx >= 0; --displayNdx) { - mSupervisor.getChildAt(displayNdx).onLockTaskPackagesUpdated(); + for (int displayNdx = mSupervisor.mRootActivityContainer.getChildCount() - 1; + displayNdx >= 0; --displayNdx) { + mSupervisor.mRootActivityContainer.getChildAt(displayNdx).onLockTaskPackagesUpdated(); } - final ActivityRecord r = mSupervisor.topRunningActivityLocked(); + final ActivityRecord r = mSupervisor.mRootActivityContainer.topRunningActivity(); final TaskRecord task = (r != null) ? r.getTask() : null; if (mLockTaskModeTasks.isEmpty() && task!= null && task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) { @@ -657,7 +658,7 @@ public class LockTaskController { } if (taskChanged) { - mSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities(); } } diff --git a/services/core/java/com/android/server/wm/PinnedActivityStack.java b/services/core/java/com/android/server/wm/PinnedActivityStack.java index 3ef42e7be8d7..1c7ebd63dfb3 100644 --- a/services/core/java/com/android/server/wm/PinnedActivityStack.java +++ b/services/core/java/com/android/server/wm/PinnedActivityStack.java @@ -41,7 +41,7 @@ class PinnedActivityStack extends ActivityStack<PinnedStackWindowController> PinnedStackWindowController createStackWindowController(int displayId, boolean onTop, Rect outBounds) { return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds, - mStackSupervisor.mWindowManager); + mRootActivityContainer.mWindowManager); } Rect getDefaultPictureInPictureBounds(float aspectRatio) { diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index 476c1f972fa9..24c5228ce0ec 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -79,7 +79,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, int callingPid) { mService = atm; mStackSupervisor = stackSupervisor; - mDefaultDisplay = stackSupervisor.getDefaultDisplay(); + mDefaultDisplay = mService.mRootActivityContainer.getDefaultDisplay(); mActivityStartController = activityStartController; mWindowManager = wm; mCallingPid = callingPid; @@ -94,7 +94,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // TODO(multi-display) currently only support recents animation in default display. final DisplayWindowController dwc = - mStackSupervisor.getDefaultDisplay().getWindowContainerController(); + mService.mRootActivityContainer.getDefaultDisplay().getWindowContainerController(); if (!mWindowManager.canStartRecentsAnimation()) { notifyAnimationCancelBeforeStart(recentsAnimationRunner); if (DEBUG) Slog.d(TAG, "Can't start recents animation, nextAppTransition=" @@ -124,8 +124,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // Send launch hint if we are actually launching the target. If it's already visible // (shouldn't happen in general) we don't need to send it. if (targetActivity == null || !targetActivity.visible) { - mStackSupervisor.sendPowerHintForLaunchStartIfNeeded(true /* forceSend */, - targetActivity); + mService.mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded( + true /* forceSend */, targetActivity); } mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent); @@ -192,7 +192,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // If we updated the launch-behind state, update the visibility of the activities after // we fetch the visible tasks to be controlled by the animation - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS); + mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(START_TASK_TO_FRONT, targetActivity); @@ -215,7 +215,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks, @Deprecated IAssistDataReceiver assistDataReceiver, int userId) { final AppOpsManager appOpsManager = (AppOpsManager) mService.mContext.getSystemService(Context.APP_OPS_SERVICE); - final List<IBinder> topActivities = mStackSupervisor.getTopVisibleActivities(); + final List<IBinder> topActivities = + mService.mRootActivityContainer.getTopVisibleActivities(); final AssistDataRequester.AssistDataRequesterCallbacks assistDataCallbacks; if (assistDataReceiver != null) { assistDataCallbacks = new AssistDataReceiverProxy(assistDataReceiver, @@ -283,7 +284,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // Just to be sure end the launch hint in case the target activity was never launched. // However, if we're keeping the activity and making it visible, we can leave it on. if (reorderMode != REORDER_KEEP_IN_PLACE) { - mStackSupervisor.sendPowerHintForLaunchEndIfNeeded(); + mService.mRootActivityContainer.sendPowerHintForLaunchEndIfNeeded(); } mService.mH.post( @@ -343,8 +344,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks, } mWindowManager.prepareAppTransition(TRANSIT_NONE, false); - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false); - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, false); + mService.mRootActivityContainer.resumeFocusedStacksTopActivities(); // No reason to wait for the pausing activity in this case, as the hiding of // surfaces needs to be done immediately. diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java new file mode 100644 index 000000000000..4dd48c46b891 --- /dev/null +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -0,0 +1,2297 @@ +/* + * 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.app.ActivityTaskManager.INVALID_STACK_ID; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.app.WindowConfiguration.activityTypeToString; +import static android.app.WindowConfiguration.windowingModeToString; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; +import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; +import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.INVALID_DISPLAY; + +import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER; +import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS; +import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID; +import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT; +import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER; +import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES; +import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY; +import static com.android.server.wm.ActivityStack.ActivityState.PAUSED; +import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; +import static com.android.server.wm.ActivityStack.ActivityState.STOPPED; +import static com.android.server.wm.ActivityStack.ActivityState.STOPPING; +import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME; +import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; +import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; +import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList; +import static com.android.server.wm.ActivityStackSupervisor.printThisActivity; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; +import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE; +import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT; + +import static java.lang.Integer.MAX_VALUE; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.ActivityManager; +import android.app.ActivityOptions; +import android.app.AppGlobals; +import android.app.WindowConfiguration; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ResolveInfo; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Rect; +import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerInternal; +import android.hardware.power.V1_0.PowerHint; +import android.os.Build; +import android.os.FactoryTest; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.Trace; +import android.os.UserHandle; +import android.service.voice.IVoiceInteractionSession; +import android.util.ArraySet; +import android.util.DisplayMetrics; +import android.util.IntArray; +import android.util.Slog; +import android.util.SparseArray; +import android.util.SparseIntArray; +import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; +import android.view.Display; +import android.view.DisplayInfo; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.LocalServices; +import com.android.server.am.ActivityManagerService; +import com.android.server.am.AppTimeTracker; +import com.android.server.am.UserState; + +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.Iterator; +import java.util.List; +import java.util.Set; + +/** + * Root node for activity containers. + * TODO: This class is mostly temporary to separate things out of ActivityStackSupervisor.java. The + * intention is to have this merged with RootWindowContainer.java as part of unifying the hierarchy. + */ +class RootActivityContainer extends ConfigurationContainer + implements DisplayManager.DisplayListener { + + private static final String TAG = TAG_WITH_CLASS_NAME ? "RootActivityContainer" : TAG_ATM; + static final String TAG_TASKS = TAG + POSTFIX_TASKS; + private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE; + static final String TAG_STATES = TAG + POSTFIX_STATES; + private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS; + + /** + * The modes which affect which tasks are returned when calling + * {@link RootActivityContainer#anyTaskForId(int)}. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + MATCH_TASK_IN_STACKS_ONLY, + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE + }) + public @interface AnyTaskForIdMatchTaskMode {} + // Match only tasks in the current stacks + static final int MATCH_TASK_IN_STACKS_ONLY = 0; + // Match either tasks in the current stacks, or in the recent tasks if not found in the stacks + static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS = 1; + // Match either tasks in the current stacks, or in the recent tasks, restoring it to the + // provided stack id + static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE = 2; + + ActivityTaskManagerService mService; + ActivityStackSupervisor mStackSupervisor; + WindowManagerService mWindowManager; + DisplayManager mDisplayManager; + private DisplayManagerInternal mDisplayManagerInternal; + // TODO: Remove after object merge with RootWindowContainer. + private RootWindowContainer mRootWindowContainer; + + /** + * List of displays which contain activities, sorted by z-order. + * The last entry in the list is the topmost. + */ + private final ArrayList<ActivityDisplay> mActivityDisplays = new ArrayList<>(); + + /** Reference to default display so we can quickly look it up. */ + private ActivityDisplay mDefaultDisplay; + private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>(); + + /** The current user */ + int mCurrentUser; + /** Stack id of the front stack when user switched, indexed by userId. */ + SparseIntArray mUserStackInFront = new SparseIntArray(2); + + /** + * A list of tokens that cause the top activity to be put to sleep. + * They are used by components that may hide and block interaction with underlying + * activities. + */ + final ArrayList<ActivityTaskManagerInternal.SleepToken> mSleepTokens = new ArrayList<>(); + + /** Is dock currently minimized. */ + boolean mIsDockMinimized; + + /** Set when a power hint has started, but not ended. */ + private boolean mPowerHintSent; + + // The default minimal size that will be used if the activity doesn't specify its minimal size. + // It will be calculated when the default display gets added. + int mDefaultMinSizeOfResizeableTaskDp = -1; + + // Whether tasks have moved and we need to rank the tasks before next OOM scoring + private boolean mTaskLayersChanged = true; + + private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>(); + + private final FindTaskResult mTmpFindTaskResult = new FindTaskResult(); + static class FindTaskResult { + ActivityRecord mRecord; + boolean mIdealMatch; + + void clear() { + mRecord = null; + mIdealMatch = false; + } + + void setTo(FindTaskResult result) { + mRecord = result.mRecord; + mIdealMatch = result.mIdealMatch; + } + } + + RootActivityContainer(ActivityTaskManagerService service) { + mService = service; + mStackSupervisor = service.mStackSupervisor; + mStackSupervisor.mRootActivityContainer = this; + } + + @VisibleForTesting + void setWindowContainer(RootWindowContainer container) { + mRootWindowContainer = container; + mRootWindowContainer.setRootActivityContainer(this); + } + + void setWindowManager(WindowManagerService wm) { + mWindowManager = wm; + setWindowContainer(mWindowManager.mRoot); + mDisplayManager = mService.mContext.getSystemService(DisplayManager.class); + mDisplayManager.registerDisplayListener(this, mService.mH); + mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); + + final Display[] displays = mDisplayManager.getDisplays(); + for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) { + final Display display = displays[displayNdx]; + final ActivityDisplay activityDisplay = new ActivityDisplay(this, display); + if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) { + mDefaultDisplay = activityDisplay; + } + addChild(activityDisplay, ActivityDisplay.POSITION_TOP); + } + calculateDefaultMinimalSizeOfResizeableTasks(); + + final ActivityDisplay defaultDisplay = getDefaultDisplay(); + + defaultDisplay.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); + positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP); + } + + // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display. + ActivityDisplay getDefaultDisplay() { + return mDefaultDisplay; + } + + /** + * Get an existing instance of {@link ActivityDisplay} that has the given uniqueId. Unique ID is + * defined in {@link DisplayInfo#uniqueId}. + * + * @param uniqueId the unique ID of the display + * @return the {@link ActivityDisplay} or {@code null} if nothing is found. + */ + ActivityDisplay getActivityDisplay(String uniqueId) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityDisplay display = mActivityDisplays.get(i); + final boolean isValid = display.mDisplay.isValid(); + if (isValid && display.mDisplay.getUniqueId().equals(uniqueId)) { + return display; + } + } + + return null; + } + + // TODO: Look into consolidating with getActivityDisplayOrCreate() + ActivityDisplay getActivityDisplay(int displayId) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityDisplay activityDisplay = mActivityDisplays.get(i); + if (activityDisplay.mDisplayId == displayId) { + return activityDisplay; + } + } + return null; + } + + /** + * Get an existing instance of {@link ActivityDisplay} or create new if there is a + * corresponding record in display manager. + */ + // TODO: Look into consolidating with getActivityDisplay() + ActivityDisplay getActivityDisplayOrCreate(int displayId) { + ActivityDisplay activityDisplay = getActivityDisplay(displayId); + if (activityDisplay != null) { + return activityDisplay; + } + if (mDisplayManager == null) { + // The system isn't fully initialized yet. + return null; + } + final Display display = mDisplayManager.getDisplay(displayId); + if (display == null) { + // The display is not registered in DisplayManager. + return null; + } + // The display hasn't been added to ActivityManager yet, create a new record now. + activityDisplay = new ActivityDisplay(this, display); + addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM); + return activityDisplay; + } + + /** Check if display with specified id is added to the list. */ + boolean isDisplayAdded(int displayId) { + return getActivityDisplayOrCreate(displayId) != null; + } + + ActivityRecord getDefaultDisplayHomeActivity() { + return getDefaultDisplayHomeActivityForUser(mCurrentUser); + } + + ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) { + return getActivityDisplay(DEFAULT_DISPLAY).getHomeActivityForUser(userId); + } + + boolean startHomeOnAllDisplays(int userId, String reason) { + boolean homeStarted = false; + for (int i = mActivityDisplays.size() - 1; i >= 0; i--) { + final int displayId = mActivityDisplays.get(i).mDisplayId; + homeStarted |= startHomeOnDisplay(userId, reason, displayId); + } + return homeStarted; + } + + /** + * This starts home activity on displays that can have system decorations and only if the + * home activity can have multiple instances. + */ + boolean startHomeOnDisplay(int userId, String reason, int displayId) { + final Intent homeIntent = mService.getHomeIntent(); + final ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent); + if (aInfo == null) { + return false; + } + + if (!canStartHomeOnDisplay(aInfo, displayId, + false /* allowInstrumenting */)) { + return false; + } + + // Update the reason for ANR debugging to verify if the user activity is the one that + // actually launched. + final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId( + aInfo.applicationInfo.uid); + mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason, + displayId); + return true; + } + + /** + * This resolves the home activity info and updates the home component of the given intent. + * @return the home activity info if any. + */ + private ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) { + final int flags = ActivityManagerService.STOCK_PM_FLAGS; + final ComponentName comp = homeIntent.getComponent(); + ActivityInfo aInfo = null; + try { + if (comp != null) { + // Factory test. + aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId); + } else { + final String resolvedType = + homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver()); + final ResolveInfo info = AppGlobals.getPackageManager() + .resolveIntent(homeIntent, resolvedType, flags, userId); + if (info != null) { + aInfo = info.activityInfo; + } + } + } catch (RemoteException e) { + // ignore + } + + if (aInfo == null) { + Slog.wtf(TAG, "No home screen found for " + homeIntent, new Throwable()); + return null; + } + + homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name)); + aInfo = new ActivityInfo(aInfo); + aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId); + homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK); + return aInfo; + } + + boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) { + if (!mService.isBooting() && !mService.isBooted()) { + // Not ready yet! + return false; + } + + if (displayId == INVALID_DISPLAY) { + displayId = DEFAULT_DISPLAY; + } + + final ActivityRecord r = getActivityDisplay(displayId).getHomeActivity(); + final String myReason = reason + " resumeHomeActivity"; + + // Only resume home activity if isn't finishing. + if (r != null && !r.finishing) { + r.moveFocusableActivityToTop(myReason); + return resumeFocusedStacksTopActivities(r.getStack(), prev, null); + } + return startHomeOnDisplay(mCurrentUser, myReason, displayId); + } + + /** + * Check if home activity start should be allowed on a display. + * @param homeInfo {@code ActivityInfo} of the home activity that is going to be launched. + * @param displayId The id of the target display. + * @param allowInstrumenting Whether launching home should be allowed if being instrumented. + * @return {@code true} if allow to launch, {@code false} otherwise. + */ + boolean canStartHomeOnDisplay(ActivityInfo homeInfo, int displayId, + boolean allowInstrumenting) { + if (mService.mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL + && mService.mTopAction == null) { + // We are running in factory test mode, but unable to find the factory test app, so + // just sit around displaying the error message and don't try to start anything. + return false; + } + + final WindowProcessController app = + mService.getProcessController(homeInfo.processName, homeInfo.applicationInfo.uid); + if (!allowInstrumenting && app != null && app.isInstrumenting()) { + // Don't do this if the home app is currently being instrumented. + return false; + } + + if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY + && displayId == mService.mVr2dDisplayId)) { + // No restrictions to default display or vr 2d display. + return true; + } + + final ActivityDisplay display = getActivityDisplay(displayId); + if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) { + // Can't launch home on display that doesn't support system decorations. + return false; + } + + final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK + && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE + && homeInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q; + if (!supportMultipleInstance) { + // Can't launch home on other displays if it requested to be single instance. Also we + // don't allow home applications that target before Q to have multiple home activity + // instances because they may not be expected to have multiple home scenario and + // haven't explicitly request for single instance. + return false; + } + + return true; + } + + /** + * Ensure all activities visibility, update orientation and configuration. + * + * @param starting The currently starting activity or {@code null} if there is none. + * @param displayId The id of the display where operation is executed. + * @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to + * {@code true} if config changed. + * @param deferResume Whether to defer resume while updating config. + * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched + * because of configuration update. + */ + boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId, + boolean markFrozenIfConfigChanged, boolean deferResume) { + // First ensure visibility without updating the config just yet. We need this to know what + // activities are affecting configuration now. + // Passing null here for 'starting' param value, so that visibility of actual starting + // activity will be properly updated. + ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, + false /* preserveWindows */, false /* notifyClients */); + + if (displayId == INVALID_DISPLAY) { + // The caller didn't provide a valid display id, skip updating config. + return true; + } + + // Force-update the orientation from the WindowManager, since we need the true configuration + // to send to the client now. + final Configuration config = mWindowManager.updateOrientationFromAppTokens( + getDisplayOverrideConfiguration(displayId), + starting != null && starting.mayFreezeScreenLocked(starting.app) + ? starting.appToken : null, + displayId, true /* forceUpdate */); + if (starting != null && markFrozenIfConfigChanged && config != null) { + starting.frozenBeforeDestroy = true; + } + + // Update the configuration of the activities on the display. + return mService.updateDisplayOverrideConfigurationLocked(config, starting, deferResume, + displayId); + } + + /** + * @return a list of activities which are the top ones in each visible stack. The first + * entry will be the focused activity. + */ + List<IBinder> getTopVisibleActivities() { + final ArrayList<IBinder> topActivityTokens = new ArrayList<>(); + final ActivityStack topFocusedStack = getTopDisplayFocusedStack(); + // Traverse all displays. + for (int i = mActivityDisplays.size() - 1; i >= 0; i--) { + final ActivityDisplay display = mActivityDisplays.get(i); + // Traverse all stacks on a display. + for (int j = display.getChildCount() - 1; j >= 0; --j) { + final ActivityStack stack = display.getChildAt(j); + // Get top activity from a visible stack and add it to the list. + if (stack.shouldBeVisible(null /* starting */)) { + final ActivityRecord top = stack.getTopActivity(); + if (top != null) { + if (stack == topFocusedStack) { + topActivityTokens.add(0, top.appToken); + } else { + topActivityTokens.add(top.appToken); + } + } + } + } + } + return topActivityTokens; + } + + ActivityStack getTopDisplayFocusedStack() { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityStack focusedStack = mActivityDisplays.get(i).getFocusedStack(); + if (focusedStack != null) { + return focusedStack; + } + } + return null; + } + + ActivityRecord getTopResumedActivity() { + final ActivityStack focusedStack = getTopDisplayFocusedStack(); + if (focusedStack == null) { + return null; + } + final ActivityRecord resumedActivity = focusedStack.getResumedActivity(); + if (resumedActivity != null && resumedActivity.app != null) { + return resumedActivity; + } + // The top focused stack might not have a resumed activity yet - look on all displays in + // focus order. + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityDisplay display = mActivityDisplays.get(i); + final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity(); + if (resumedActivityOnDisplay != null) { + return resumedActivityOnDisplay; + } + } + return null; + } + + boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) { + if (container.inSplitScreenPrimaryWindowingMode() && mIsDockMinimized) { + return false; + } + + return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable; + } + + boolean isTopDisplayFocusedStack(ActivityStack stack) { + return stack != null && stack == getTopDisplayFocusedStack(); + } + + void updatePreviousProcess(ActivityRecord r) { + // Now that this process has stopped, we may want to consider it to be the previous app to + // try to keep around in case the user wants to return to it. + + // First, found out what is currently the foreground app, so that we don't blow away the + // previous app if this activity is being hosted by the process that is actually still the + // foreground. + WindowProcessController fgApp = null; + 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)) { + final ActivityRecord resumedActivity = stack.getResumedActivity(); + if (resumedActivity != null) { + fgApp = resumedActivity.app; + } else if (stack.mPausingActivity != null) { + fgApp = stack.mPausingActivity.app; + } + break; + } + } + } + + // Now set this one as the previous process, only if that really makes sense to. + if (r.hasProcess() && fgApp != null && r.app != fgApp + && r.lastVisibleTime > mService.mPreviousProcessVisibleTime + && r.app != mService.mHomeProcess) { + mService.mPreviousProcess = r.app; + mService.mPreviousProcessVisibleTime = r.lastVisibleTime; + } + } + + boolean attachApplication(WindowProcessController app) throws RemoteException { + final String processName = app.mName; + boolean didSomething = false; + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + final ActivityStack stack = display.getFocusedStack(); + if (stack != null) { + stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList); + final ActivityRecord top = stack.topRunningActivityLocked(); + final int size = mTmpActivityList.size(); + for (int i = 0; i < size; i++) { + final ActivityRecord activity = mTmpActivityList.get(i); + if (activity.app == null && app.mUid == activity.info.applicationInfo.uid + && processName.equals(activity.processName)) { + try { + if (mStackSupervisor.realStartActivityLocked(activity, app, + top == activity /* andResume */, true /* checkConfig */)) { + didSomething = true; + } + } catch (RemoteException e) { + Slog.w(TAG, "Exception in new application when starting activity " + + top.intent.getComponent().flattenToShortString(), e); + throw e; + } + } + } + } + } + if (!didSomething) { + ensureActivitiesVisible(null, 0, false /* preserve_windows */); + } + return didSomething; + } + + /** + * Make sure that all activities that need to be visible in the system actually are and update + * their configuration. + */ + void ensureActivitiesVisible(ActivityRecord starting, int configChanges, + boolean preserveWindows) { + ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */); + } + + /** + * @see #ensureActivitiesVisible(ActivityRecord, int, boolean) + */ + void ensureActivitiesVisible(ActivityRecord starting, int configChanges, + boolean preserveWindows, boolean notifyClients) { + mStackSupervisor.getKeyguardController().beginActivityVisibilityUpdate(); + try { + // First the front stacks. In case any are not fullscreen and are in front of home. + 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); + stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows, + notifyClients); + } + } + } finally { + mStackSupervisor.getKeyguardController().endActivityVisibilityUpdate(); + } + } + + boolean switchUser(int userId, UserState uss) { + final int focusStackId = getTopDisplayFocusedStack().getStackId(); + // We dismiss the docked stack whenever we switch users. + final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack(); + if (dockedStack != null) { + mStackSupervisor.moveTasksToFullscreenStackLocked( + dockedStack, dockedStack.isFocusedStackOnDisplay()); + } + // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will + // also cause all tasks to be moved to the fullscreen stack at a position that is + // appropriate. + removeStacksInWindowingModes(WINDOWING_MODE_PINNED); + + mUserStackInFront.put(mCurrentUser, focusStackId); + final int restoreStackId = + mUserStackInFront.get(userId, getDefaultDisplay().getHomeStack().mStackId); + mCurrentUser = userId; + + mStackSupervisor.mStartingUsers.add(uss); + 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); + stack.switchUserLocked(userId); + TaskRecord task = stack.topTask(); + if (task != null) { + stack.positionChildWindowContainerAtTop(task); + } + } + } + + ActivityStack stack = getStack(restoreStackId); + if (stack == null) { + stack = getDefaultDisplay().getHomeStack(); + } + final boolean homeInFront = stack.isActivityTypeHome(); + if (stack.isOnHomeDisplay()) { + stack.moveToFront("switchUserOnHomeDisplay"); + } else { + // Stack was moved to another display while user was swapped out. + resumeHomeActivity(null, "switchUserOnOtherDisplay", DEFAULT_DISPLAY); + } + return homeInFront; + } + + void removeUser(int userId) { + mUserStackInFront.delete(userId); + } + + /** + * Update the last used stack id for non-current user (current user's last + * used stack is the focused stack) + */ + void updateUserStack(int userId, ActivityStack stack) { + if (userId != mCurrentUser) { + mUserStackInFront.put(userId, stack != null ? stack.getStackId() + : getDefaultDisplay().getHomeStack().mStackId); + } + } + + void resizeStack(ActivityStack stack, Rect bounds, Rect tempTaskBounds, + Rect tempTaskInsetBounds, boolean preserveWindows, boolean allowResizeInDockedMode, + boolean deferResume) { + + if (stack.inSplitScreenPrimaryWindowingMode()) { + mStackSupervisor.resizeDockedStackLocked(bounds, tempTaskBounds, + tempTaskInsetBounds, null, null, preserveWindows, deferResume); + return; + } + + final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenPrimaryStack(); + if (!allowResizeInDockedMode + && !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) { + // If the docked stack exists, don't resize non-floating stacks independently of the + // size computed from the docked stack size (otherwise they will be out of sync) + return; + } + + Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId); + mWindowManager.deferSurfaceLayout(); + try { + if (stack.affectedBySplitScreenResize()) { + if (bounds == null && stack.inSplitScreenWindowingMode()) { + // null bounds = fullscreen windowing mode...at least for now. + stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + } else if (splitScreenActive) { + // If we are in split-screen mode and this stack support split-screen, then + // it should be split-screen secondary mode. i.e. adjacent to the docked stack. + stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + } + } + stack.resize(bounds, tempTaskBounds, tempTaskInsetBounds); + if (!deferResume) { + stack.ensureVisibleActivitiesConfigurationLocked( + stack.topRunningActivityLocked(), preserveWindows); + } + } finally { + mWindowManager.continueSurfaceLayout(); + Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + } + } + + /** + * Move stack with all its existing content to specified display. + * @param stackId Id of stack to move. + * @param displayId Id of display to move stack to. + * @param onTop Indicates whether container should be place on top or on bottom. + */ + void moveStackToDisplay(int stackId, int displayId, boolean onTop) { + final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId); + if (activityDisplay == null) { + throw new IllegalArgumentException("moveStackToDisplay: Unknown displayId=" + + displayId); + } + final ActivityStack stack = getStack(stackId); + if (stack == null) { + throw new IllegalArgumentException("moveStackToDisplay: Unknown stackId=" + + stackId); + } + + final ActivityDisplay currentDisplay = stack.getDisplay(); + if (currentDisplay == null) { + throw new IllegalStateException("moveStackToDisplay: Stack with stack=" + stack + + " is not attached to any display."); + } + + if (currentDisplay.mDisplayId == displayId) { + throw new IllegalArgumentException("Trying to move stack=" + stack + + " to its current displayId=" + displayId); + } + + stack.reparent(activityDisplay, onTop, false /* displayRemoved */); + // TODO(multi-display): resize stacks properly if moved from split-screen. + } + + boolean moveTopStackActivityToPinnedStack(int stackId) { + final ActivityStack stack = getStack(stackId); + if (stack == null) { + throw new IllegalArgumentException( + "moveTopStackActivityToPinnedStack: Unknown stackId=" + stackId); + } + + final ActivityRecord r = stack.topRunningActivityLocked(); + if (r == null) { + Slog.w(TAG, "moveTopStackActivityToPinnedStack: No top running activity" + + " in stack=" + stack); + return false; + } + + if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) { + Slog.w(TAG, "moveTopStackActivityToPinnedStack: Picture-In-Picture not supported for " + + " r=" + r); + return false; + } + + moveActivityToPinnedStack(r, null /* sourceBounds */, 0f /* aspectRatio */, + "moveTopActivityToPinnedStack"); + return true; + } + + void moveActivityToPinnedStack(ActivityRecord r, Rect sourceHintBounds, float aspectRatio, + String reason) { + + mWindowManager.deferSurfaceLayout(); + + final ActivityDisplay display = r.getStack().getDisplay(); + PinnedActivityStack stack = display.getPinnedStack(); + + // This will clear the pinned stack by moving an existing task to the full screen stack, + // ensuring only one task is present. + if (stack != null) { + mStackSupervisor.moveTasksToFullscreenStackLocked(stack, !ON_TOP); + } + + // Need to make sure the pinned stack exist so we can resize it below... + stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP); + + // Calculate the target bounds here before the task is reparented back into pinned windowing + // mode (which will reset the saved bounds) + final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio); + + try { + final TaskRecord task = r.getTask(); + // Resize the pinned stack to match the current size of the task the activity we are + // going to be moving is currently contained in. We do this to have the right starting + // animation bounds for the pinned stack to the desired bounds the caller wants. + resizeStack(stack, task.getOverrideBounds(), null /* tempTaskBounds */, + null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS, + true /* allowResizeInDockedMode */, !DEFER_RESUME); + + if (task.mActivities.size() == 1) { + // Defer resume until below, and do not schedule PiP changes until we animate below + task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME, + false /* schedulePictureInPictureModeChange */, reason); + } else { + // There are multiple activities in the task and moving the top activity should + // reveal/leave the other activities in their original task. + + // Currently, we don't support reparenting activities across tasks in two different + // stacks, so instead, just create a new task in the same stack, reparent the + // activity into that task, and then reparent the whole task to the new stack. This + // ensures that all the necessary work to migrate states in the old and new stacks + // is also done. + final TaskRecord newTask = task.getStack().createTaskRecord( + mStackSupervisor.getNextTaskIdForUserLocked(r.userId), r.info, + r.intent, null, null, true); + r.reparent(newTask, MAX_VALUE, "moveActivityToStack"); + + // Defer resume until below, and do not schedule PiP changes until we animate below + newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, + DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason); + } + + // Reset the state that indicates it can enter PiP while pausing after we've moved it + // to the pinned stack + r.supportsEnterPipOnTaskSwitch = false; + } finally { + mWindowManager.continueSurfaceLayout(); + } + + stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */, + true /* fromFullscreen */); + + // Update the visibility of all activities after the they have been reparented to the new + // stack. This MUST run after the animation above is scheduled to ensure that the windows + // drawn signal is scheduled after the bounds animation start call on the bounds animator + // thread. + ensureActivitiesVisible(null, 0, false /* preserveWindows */); + resumeFocusedStacksTopActivities(); + + mService.getTaskChangeNotificationController().notifyActivityPinned(r); + } + + void executeAppTransitionForAllDisplay() { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + display.getWindowContainerController().executeAppTransition(); + } + } + + void setDockedStackMinimized(boolean minimized) { + // Get currently focused stack before setting mIsDockMinimized. We do this because if + // split-screen is active, primary stack will not be focusable (see #isFocusable) while + // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null. + final ActivityStack current = getTopDisplayFocusedStack(); + mIsDockMinimized = minimized; + if (mIsDockMinimized) { + if (current.inSplitScreenPrimaryWindowingMode()) { + // The primary split-screen stack can't be focused while it is minimize, so move + // focus to something else. + current.adjustFocusToNextFocusableStack("setDockedStackMinimized"); + } + } + } + + ActivityRecord findTask(ActivityRecord r, int preferredDisplayId) { + if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r); + mTmpFindTaskResult.clear(); + + // Looking up task on preferred display first + final ActivityDisplay preferredDisplay = getActivityDisplay(preferredDisplayId); + if (preferredDisplay != null) { + preferredDisplay.findTaskLocked(r, true /* isPreferredDisplay */, mTmpFindTaskResult); + if (mTmpFindTaskResult.mIdealMatch) { + return mTmpFindTaskResult.mRecord; + } + } + + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + if (display.mDisplayId == preferredDisplayId) { + continue; + } + + display.findTaskLocked(r, false /* isPreferredDisplay */, mTmpFindTaskResult); + if (mTmpFindTaskResult.mIdealMatch) { + return mTmpFindTaskResult.mRecord; + } + } + + if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found"); + return mTmpFindTaskResult.mRecord; + } + + /** + * Finish the topmost activities in all stacks that belong to the crashed app. + * @param app The app that crashed. + * @param reason Reason to perform this action. + * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished. + */ + int finishTopCrashedActivities(WindowProcessController app, String reason) { + TaskRecord finishedTask = null; + ActivityStack focusedStack = getTopDisplayFocusedStack(); + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + // It is possible that request to finish activity might also remove its task and stack, + // so we need to be careful with indexes in the loop and check child count every time. + for (int stackNdx = 0; stackNdx < display.getChildCount(); ++stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + final TaskRecord t = stack.finishTopCrashedActivityLocked(app, reason); + if (stack == focusedStack || finishedTask == null) { + finishedTask = t; + } + } + } + return finishedTask != null ? finishedTask.taskId : INVALID_TASK_ID; + } + + boolean resumeFocusedStacksTopActivities() { + return resumeFocusedStacksTopActivities(null, null, null); + } + + boolean resumeFocusedStacksTopActivities( + ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) { + + if (!mStackSupervisor.readyToResume()) { + return false; + } + + if (targetStack != null && (targetStack.isTopStackOnDisplay() + || getTopDisplayFocusedStack() == targetStack)) { + return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions); + } + + // Resume all top activities in focused stacks on all displays. + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + final ActivityStack focusedStack = display.getFocusedStack(); + if (focusedStack == null) { + continue; + } + final ActivityRecord r = focusedStack.topRunningActivityLocked(); + if (r == null || !r.isState(RESUMED)) { + focusedStack.resumeTopActivityUncheckedLocked(null, null); + } else if (r.isState(RESUMED)) { + // Kick off any lingering app transitions form the MoveTaskToFront operation. + focusedStack.executeAppTransition(targetOptions); + } + } + + return false; + } + + void applySleepTokens(boolean applyToStacks) { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + // Set the sleeping state of the display. + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + final boolean displayShouldSleep = display.shouldSleep(); + if (displayShouldSleep == display.isSleeping()) { + continue; + } + display.setIsSleeping(displayShouldSleep); + + if (!applyToStacks) { + continue; + } + + // Set the sleeping state of the stacks on the display. + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + if (displayShouldSleep) { + stack.goToSleepIfPossible(false /* shuttingDown */); + } else { + stack.awakeFromSleepingLocked(); + if (stack.isFocusedStackOnDisplay() + && !mStackSupervisor.getKeyguardController() + .isKeyguardOrAodShowing(display.mDisplayId)) { + // If the keyguard is unlocked - resume immediately. + // It is possible that the display will not be awake at the time we + // process the keyguard going away, which can happen before the sleep token + // is released. As a result, it is important we resume the activity here. + resumeFocusedStacksTopActivities(); + } + } + } + + if (displayShouldSleep || mStackSupervisor.mGoingToSleepActivities.isEmpty()) { + continue; + } + // The display is awake now, so clean up the going to sleep list. + for (Iterator<ActivityRecord> it = + mStackSupervisor.mGoingToSleepActivities.iterator(); it.hasNext(); ) { + final ActivityRecord r = it.next(); + if (r.getDisplayId() == display.mDisplayId) { + it.remove(); + } + } + } + } + + protected <T extends ActivityStack> T getStack(int stackId) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final T stack = mActivityDisplays.get(i).getStack(stackId); + if (stack != null) { + return stack; + } + } + return null; + } + + /** @see ActivityDisplay#getStack(int, int) */ + private <T extends ActivityStack> T getStack(int windowingMode, int activityType) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final T stack = mActivityDisplays.get(i).getStack(windowingMode, activityType); + if (stack != null) { + return stack; + } + } + return null; + } + + private ActivityManager.StackInfo getStackInfo(ActivityStack stack) { + final int displayId = stack.mDisplayId; + final ActivityDisplay display = getActivityDisplay(displayId); + ActivityManager.StackInfo info = new ActivityManager.StackInfo(); + stack.getWindowContainerBounds(info.bounds); + info.displayId = displayId; + info.stackId = stack.mStackId; + info.userId = stack.mCurrentUser; + info.visible = stack.shouldBeVisible(null); + // A stack might be not attached to a display. + info.position = display != null ? display.getIndexOf(stack) : 0; + info.configuration.setTo(stack.getConfiguration()); + + ArrayList<TaskRecord> tasks = stack.getAllTasks(); + final int numTasks = tasks.size(); + int[] taskIds = new int[numTasks]; + String[] taskNames = new String[numTasks]; + Rect[] taskBounds = new Rect[numTasks]; + int[] taskUserIds = new int[numTasks]; + for (int i = 0; i < numTasks; ++i) { + final TaskRecord task = tasks.get(i); + taskIds[i] = task.taskId; + taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString() + : task.realActivity != null ? task.realActivity.flattenToString() + : task.getTopActivity() != null ? task.getTopActivity().packageName + : "unknown"; + taskBounds[i] = new Rect(); + task.getWindowContainerBounds(taskBounds[i]); + taskUserIds[i] = task.userId; + } + info.taskIds = taskIds; + info.taskNames = taskNames; + info.taskBounds = taskBounds; + info.taskUserIds = taskUserIds; + + final ActivityRecord top = stack.topRunningActivityLocked(); + info.topActivity = top != null ? top.intent.getComponent() : null; + return info; + } + + ActivityManager.StackInfo getStackInfo(int stackId) { + ActivityStack stack = getStack(stackId); + if (stack != null) { + return getStackInfo(stack); + } + return null; + } + + ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType) { + final ActivityStack stack = getStack(windowingMode, activityType); + return (stack != null) ? getStackInfo(stack) : null; + } + + ArrayList<ActivityManager.StackInfo> getAllStackInfos() { + ArrayList<ActivityManager.StackInfo> list = new ArrayList<>(); + for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + list.add(getStackInfo(stack)); + } + } + return list; + } + + void deferUpdateBounds(int activityType) { + final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType); + if (stack != null) { + stack.deferUpdateBounds(); + } + } + + void continueUpdateBounds(int activityType) { + final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType); + if (stack != null) { + stack.continueUpdateBounds(); + } + } + + @Override + public void onDisplayAdded(int displayId) { + if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId); + synchronized (mService.mGlobalLock) { + getActivityDisplayOrCreate(displayId); + // Do not start home before booting, or it may accidentally finish booting before it + // starts. Instead, we expect home activities to be launched when the system is ready + // (ActivityManagerService#systemReady). + if (mService.isBooted() || mService.isBooting()) { + startHomeOnDisplay(mCurrentUser, "displayAdded", displayId); + } + } + } + + @Override + public void onDisplayRemoved(int displayId) { + if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId); + if (displayId == DEFAULT_DISPLAY) { + throw new IllegalArgumentException("Can't remove the primary display."); + } + + synchronized (mService.mGlobalLock) { + final ActivityDisplay activityDisplay = getActivityDisplay(displayId); + if (activityDisplay == null) { + return; + } + + activityDisplay.remove(); + } + } + + @Override + public void onDisplayChanged(int displayId) { + if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId); + synchronized (mService.mGlobalLock) { + final ActivityDisplay activityDisplay = getActivityDisplay(displayId); + if (activityDisplay != null) { + activityDisplay.onDisplayChanged(); + } + } + } + + /** Update lists of UIDs that are present on displays and have access to them. */ + void updateUIDsPresentOnDisplay() { + mDisplayAccessUIDs.clear(); + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx); + // Only bother calculating the whitelist for private displays + if (activityDisplay.isPrivate()) { + mDisplayAccessUIDs.append( + activityDisplay.mDisplayId, activityDisplay.getPresentUIDs()); + } + } + // Store updated lists in DisplayManager. Callers from outside of AM should get them there. + mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs); + } + + ActivityStack findStackBehind(ActivityStack stack) { + final ActivityDisplay display = getActivityDisplay(stack.mDisplayId); + if (display != null) { + for (int i = display.getChildCount() - 1; i >= 0; i--) { + if (display.getChildAt(i) == stack && i > 0) { + return display.getChildAt(i - 1); + } + } + } + throw new IllegalStateException("Failed to find a stack behind stack=" + stack + + " in=" + display); + } + + @Override + protected int getChildCount() { + return mActivityDisplays.size(); + } + + @Override + protected ActivityDisplay getChildAt(int index) { + return mActivityDisplays.get(index); + } + + @Override + protected ConfigurationContainer getParent() { + return null; + } + + // TODO: remove after object merge with RootWindowContainer + void onChildPositionChanged(DisplayWindowController childController, int position) { + // Assume AM lock is held from positionChildAt of controller in each hierarchy. + final ActivityDisplay display = getActivityDisplay(childController.getDisplayId()); + if (display != null) { + positionChildAt(display, position); + } + } + + /** Change the z-order of the given display. */ + private void positionChildAt(ActivityDisplay display, int position) { + if (position >= mActivityDisplays.size()) { + position = mActivityDisplays.size() - 1; + } else if (position < 0) { + position = 0; + } + + if (mActivityDisplays.isEmpty()) { + mActivityDisplays.add(display); + } else if (mActivityDisplays.get(position) != display) { + mActivityDisplays.remove(display); + mActivityDisplays.add(position, display); + } + } + + @VisibleForTesting + void addChild(ActivityDisplay activityDisplay, int position) { + positionChildAt(activityDisplay, position); + mRootWindowContainer.positionChildAt(position, + activityDisplay.getWindowContainerController().mContainer); + } + + void removeChild(ActivityDisplay activityDisplay) { + // The caller must tell the controller of {@link ActivityDisplay} to release its container + // {@link DisplayContent}. That is done in {@link ActivityDisplay#releaseSelfIfNeeded}). + mActivityDisplays.remove(activityDisplay); + } + + Configuration getDisplayOverrideConfiguration(int displayId) { + final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId); + if (activityDisplay == null) { + throw new IllegalArgumentException("No display found with id: " + displayId); + } + + return activityDisplay.getOverrideConfiguration(); + } + + void setDisplayOverrideConfiguration(Configuration overrideConfiguration, int displayId) { + final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId); + if (activityDisplay == null) { + throw new IllegalArgumentException("No display found with id: " + displayId); + } + + activityDisplay.onOverrideConfigurationChanged(overrideConfiguration); + } + + void prepareForShutdown() { + for (int i = 0; i < mActivityDisplays.size(); i++) { + createSleepToken("shutdown", mActivityDisplays.get(i).mDisplayId); + } + } + + ActivityTaskManagerInternal.SleepToken createSleepToken(String tag, int displayId) { + final ActivityDisplay display = getActivityDisplay(displayId); + if (display == null) { + throw new IllegalArgumentException("Invalid display: " + displayId); + } + + final SleepTokenImpl token = new SleepTokenImpl(tag, displayId); + mSleepTokens.add(token); + display.mAllSleepTokens.add(token); + return token; + } + + private void removeSleepToken(SleepTokenImpl token) { + mSleepTokens.remove(token); + + final ActivityDisplay display = getActivityDisplay(token.mDisplayId); + if (display != null) { + display.mAllSleepTokens.remove(token); + if (display.mAllSleepTokens.isEmpty()) { + mService.updateSleepIfNeededLocked(); + } + } + } + + void addStartingWindowsForVisibleActivities(boolean taskSwitch) { + 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); + stack.addStartingWindowsForVisibleActivities(taskSwitch); + } + } + } + + void invalidateTaskLayers() { + mTaskLayersChanged = true; + } + + void rankTaskLayersIfNeeded() { + if (!mTaskLayersChanged) { + return; + } + mTaskLayersChanged = false; + for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + int baseLayer = 0; + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + baseLayer += stack.rankTaskLayers(baseLayer); + } + } + } + + void clearOtherAppTimeTrackers(AppTimeTracker except) { + 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); + stack.clearOtherAppTimeTrackers(except); + } + } + } + + void scheduleDestroyAllActivities(WindowProcessController app, String reason) { + 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); + stack.scheduleDestroyActivities(app, reason); + } + } + } + + void releaseSomeActivitiesLocked(WindowProcessController app, String reason) { + // Tasks is non-null only if two or more tasks are found. + ArraySet<TaskRecord> tasks = app.getReleaseSomeActivitiesTasks(); + if (tasks == null) { + if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release"); + return; + } + // If we have activities in multiple tasks that are in a position to be destroyed, + // let's iterate through the tasks and release the oldest one. + final int numDisplays = mActivityDisplays.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + final int stackCount = display.getChildCount(); + // Step through all stacks starting from behind, to hit the oldest things first. + for (int stackNdx = 0; stackNdx < stackCount; stackNdx++) { + final ActivityStack stack = display.getChildAt(stackNdx); + // Try to release activities in this stack; if we manage to, we are done. + if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) { + return; + } + } + } + } + + // Tries to put all activity stacks to sleep. Returns true if all stacks were + // successfully put to sleep. + boolean putStacksToSleep(boolean allowDelay, boolean shuttingDown) { + boolean allSleep = true; + 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 (allowDelay) { + allSleep &= stack.goToSleepIfPossible(shuttingDown); + } else { + stack.goToSleep(); + } + } + } + return allSleep; + } + + void handleAppCrash(WindowProcessController app) { + 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); + stack.handleAppCrash(app); + } + } + } + + ActivityRecord findActivity(Intent intent, ActivityInfo info, boolean compareIntentFilters) { + 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); + final ActivityRecord ar = stack.findActivityLocked( + intent, info, compareIntentFilters); + if (ar != null) { + return ar; + } + } + } + return null; + } + + boolean hasAwakeDisplay() { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + if (!display.shouldSleep()) { + return true; + } + } + return false; + } + + <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r, + @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) { + return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */); + } + + /** + * Returns the right stack to use for launching factoring in all the input parameters. + * + * @param r The activity we are trying to launch. Can be null. + * @param options The activity options used to the launch. Can be null. + * @param candidateTask The possible task the activity might be launched in. Can be null. + * @params launchParams The resolved launch params to use. + * + * @return The stack to use for the launch or INVALID_STACK_ID. + */ + <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r, + @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop, + @Nullable LaunchParamsController.LaunchParams launchParams) { + int taskId = INVALID_TASK_ID; + int displayId = INVALID_DISPLAY; + //Rect bounds = null; + + // 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 + // associated with that if possible. + if (taskId != INVALID_TASK_ID) { + // Temporarily set the task id to invalid in case in re-entry. + options.setLaunchTaskId(INVALID_TASK_ID); + final TaskRecord task = anyTaskForId(taskId, + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options, onTop); + options.setLaunchTaskId(taskId); + if (task != null) { + return task.getStack(); + } + } + + final int activityType = resolveActivityType(r, options, candidateTask); + T stack; + + // Next preference for stack goes to the display Id set the candidate display. + if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) { + displayId = launchParams.mPreferredDisplayId; + } + if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) { + if (r != null) { + stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options, + launchParams); + if (stack != null) { + return stack; + } + } + final ActivityDisplay display = getActivityDisplayOrCreate(displayId); + if (display != null) { + stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop); + if (stack != null) { + return stack; + } + } + } + + // Give preference to the stack and display of the input task and activity if they match the + // mode we want to launch into. + stack = null; + ActivityDisplay display = null; + if (candidateTask != null) { + stack = candidateTask.getStack(); + } + if (stack == null && r != null) { + stack = r.getStack(); + } + if (stack != null) { + display = stack.getDisplay(); + if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) { + int windowingMode = launchParams != null ? launchParams.mWindowingMode + : WindowConfiguration.WINDOWING_MODE_UNDEFINED; + if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) { + windowingMode = display.resolveWindowingMode(r, options, candidateTask, + activityType); + } + if (stack.isCompatible(windowingMode, activityType)) { + return stack; + } + if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY + && display.getSplitScreenPrimaryStack() == stack + && candidateTask == stack.topTask()) { + // This is a special case when we try to launch an activity that is currently on + // top of split-screen primary stack, but is targeting split-screen secondary. + // In this case we don't want to move it to another stack. + // TODO(b/78788972): Remove after differentiating between preferred and required + // launch options. + return stack; + } + } + } + + if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) { + display = getDefaultDisplay(); + } + + return display.getOrCreateStack(r, options, candidateTask, activityType, onTop); + } + + /** @return true if activity record is null or can be launched on provided display. */ + private boolean canLaunchOnDisplay(ActivityRecord r, int displayId) { + if (r == null) { + return true; + } + return r.canBeLaunchedOnDisplay(displayId); + } + + /** + * Get a topmost stack on the display, that is a valid launch stack for specified activity. + * If there is no such stack, new dynamic stack can be created. + * @param displayId Target display. + * @param r Activity that should be launched there. + * @param candidateTask The possible task the activity might be put in. + * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null. + */ + private ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r, + @Nullable TaskRecord candidateTask, @Nullable ActivityOptions options, + @Nullable LaunchParamsController.LaunchParams launchParams) { + final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId); + if (activityDisplay == null) { + throw new IllegalArgumentException( + "Display with displayId=" + displayId + " not found."); + } + + if (!r.canBeLaunchedOnDisplay(displayId)) { + return null; + } + + // If {@code r} is already in target display and its task is the same as the candidate task, + // the intention should be getting a launch stack for the reusable activity, so we can use + // the existing stack. + if (r.getDisplayId() == displayId && r.getTask() == candidateTask) { + return candidateTask.getStack(); + } + + // Return the topmost valid stack on the display. + for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) { + final ActivityStack stack = activityDisplay.getChildAt(i); + if (isValidLaunchStack(stack, r)) { + return stack; + } + } + + // If there is no valid stack on the external display - check if new dynamic stack will do. + if (displayId != DEFAULT_DISPLAY) { + final int windowingMode; + if (launchParams != null) { + // When launch params is not null, we always defer to its windowing mode. Sometimes + // it could be unspecified, which indicates it should inherit windowing mode from + // display. + windowingMode = launchParams.mWindowingMode; + } else { + windowingMode = options != null ? options.getLaunchWindowingMode() + : r.getWindowingMode(); + } + 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); + return null; + } + + ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r, + @Nullable ActivityOptions options, + @Nullable LaunchParamsController.LaunchParams launchParams) { + return getValidLaunchStackOnDisplay(displayId, r, null /* candidateTask */, options, + launchParams); + } + + // TODO: Can probably be consolidated into getLaunchStack()... + private boolean isValidLaunchStack(ActivityStack stack, ActivityRecord r) { + switch (stack.getActivityType()) { + case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome(); + case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents(); + case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant(); + } + // There is a 1-to-1 relationship between stack and task when not in + // primary split-windowing mode. + if (stack.getWindowingMode() != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + return false; + } else { + return r.supportsSplitScreenWindowingMode(); + } + } + + int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options, + @Nullable TaskRecord task) { + // Preference is given to the activity type for the activity then the task since the type + // once set shouldn't change. + int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED; + if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) { + activityType = task.getActivityType(); + } + if (activityType != ACTIVITY_TYPE_UNDEFINED) { + return activityType; + } + if (options != null) { + activityType = options.getLaunchActivityType(); + } + return activityType != ACTIVITY_TYPE_UNDEFINED ? activityType : ACTIVITY_TYPE_STANDARD; + } + + /** + * Get next focusable stack in the system. This will search through the stack on the same + * display as the current focused stack, looking for a focusable and visible stack, different + * from the target stack. If no valid candidates will be found, it will then go through all + * displays and stacks in last-focused order. + * + * @param currentFocus The stack that previously had focus. + * @param ignoreCurrent If we should ignore {@param currentFocus} when searching for next + * candidate. + * @return Next focusable {@link ActivityStack}, {@code null} if not found. + */ + ActivityStack getNextFocusableStack(@NonNull ActivityStack currentFocus, + boolean ignoreCurrent) { + // First look for next focusable stack on the same display + final ActivityDisplay preferredDisplay = currentFocus.getDisplay(); + final ActivityStack preferredFocusableStack = preferredDisplay.getNextFocusableStack( + currentFocus, ignoreCurrent); + if (preferredFocusableStack != null) { + return preferredFocusableStack; + } + if (preferredDisplay.supportsSystemDecorations()) { + // Stop looking for focusable stack on other displays because the preferred display + // supports system decorations. Home activity would be launched on the same display if + // no focusable stack found. + return null; + } + + // Now look through all displays + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityDisplay display = mActivityDisplays.get(i); + if (display == preferredDisplay) { + // We've already checked this one + continue; + } + final ActivityStack nextFocusableStack = display.getNextFocusableStack(currentFocus, + ignoreCurrent); + if (nextFocusableStack != null) { + return nextFocusableStack; + } + } + + return null; + } + + /** + * Get next valid stack for launching provided activity in the system. This will search across + * displays and stacks in last-focused order for a focusable and visible stack, except those + * that are on a currently focused display. + * + * @param r The activity that is being launched. + * @param currentFocus The display that previously had focus and thus needs to be ignored when + * searching for the next candidate. + * @return Next valid {@link ActivityStack}, null if not found. + */ + ActivityStack getNextValidLaunchStack(@NonNull ActivityRecord r, int currentFocus) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityDisplay display = mActivityDisplays.get(i); + if (display.mDisplayId == currentFocus) { + continue; + } + final ActivityStack stack = getValidLaunchStackOnDisplay(display.mDisplayId, r, + null /* options */, null /* launchParams */); + if (stack != null) { + return stack; + } + } + return null; + } + + boolean handleAppDied(WindowProcessController app) { + boolean hasVisibleActivities = 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); + hasVisibleActivities |= stack.handleAppDiedLocked(app); + } + } + return hasVisibleActivities; + } + + void closeSystemDialogs() { + 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); + stack.closeSystemDialogsLocked(); + } + } + } + + /** @return true if some activity was finished (or would have finished if doit were true). */ + boolean finishDisabledPackageActivities(String packageName, Set<String> filterByClasses, + boolean doit, boolean evenPersistent, int userId) { + 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 (stack.finishDisabledPackageActivitiesLocked( + packageName, filterByClasses, doit, evenPersistent, userId)) { + didSomething = true; + } + } + } + return didSomething; + } + + void updateActivityApplicationInfo(ApplicationInfo aInfo) { + 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); + stack.updateActivityApplicationInfoLocked(aInfo); + } + } + } + + void finishVoiceTask(IVoiceInteractionSession session) { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + final int numStacks = display.getChildCount(); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + stack.finishVoiceTask(session); + } + } + } + + /** + * Removes stacks in the input windowing modes from the system if they are of activity type + * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED + */ + void removeStacksInWindowingModes(int... windowingModes) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + mActivityDisplays.get(i).removeStacksInWindowingModes(windowingModes); + } + } + + void removeStacksWithActivityTypes(int... activityTypes) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + mActivityDisplays.get(i).removeStacksWithActivityTypes(activityTypes); + } + } + + ActivityRecord topRunningActivity() { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity(); + if (topActivity != null) { + return topActivity; + } + } + return null; + } + + boolean allResumedActivitiesIdle() { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + // TODO(b/117135575): Check resumed activities on all visible stacks. + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + if (display.isSleeping()) { + // No resumed activities while display is sleeping. + continue; + } + + // If the focused stack is not null or not empty, there should have some activities + // resuming or resumed. Make sure these activities are idle. + final ActivityStack stack = display.getFocusedStack(); + if (stack == null || stack.numActivities() == 0) { + continue; + } + final ActivityRecord resumedActivity = stack.getResumedActivity(); + if (resumedActivity == null || !resumedActivity.idle) { + if (DEBUG_STATES) { + Slog.d(TAG_STATES, "allResumedActivitiesIdle: stack=" + + stack.mStackId + " " + resumedActivity + " not idle"); + } + return false; + } + } + // Send launch end powerhint when idle + sendPowerHintForLaunchEndIfNeeded(); + return true; + } + + boolean allResumedActivitiesVisible() { + boolean foundResumed = 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); + final ActivityRecord r = stack.getResumedActivity(); + if (r != null) { + if (!r.nowVisible + || mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(r)) { + return false; + } + foundResumed = true; + } + } + } + return foundResumed; + } + + boolean allPausedActivitiesComplete() { + boolean pausing = true; + 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); + final ActivityRecord r = stack.mPausingActivity; + if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) { + if (DEBUG_STATES) { + Slog.d(TAG_STATES, + "allPausedActivitiesComplete: r=" + r + " state=" + r.getState()); + pausing = false; + } else { + return false; + } + } + } + } + return pausing; + } + + /** + * Find all visible task stacks containing {@param userId} and intercept them with an activity + * to block out the contents and possibly start a credential-confirming intent. + * + * @param userId user handle for the locked managed profile. + */ + void lockAllProfileTasks(@UserIdInt int userId) { + mWindowManager.deferSurfaceLayout(); + try { + 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); + final List<TaskRecord> tasks = stack.getAllTasks(); + for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) { + final TaskRecord task = tasks.get(taskNdx); + + // Check the task for a top activity belonging to userId, or returning a + // result to an activity belonging to userId. Example case: a document + // picker for personal files, opened by a work app, should still get locked. + if (taskTopActivityIsUser(task, userId)) { + mService.getTaskChangeNotificationController().notifyTaskProfileLocked( + task.taskId, userId); + } + } + } + } + } finally { + mWindowManager.continueSurfaceLayout(); + } + } + + /** + * Detects whether we should show a lock screen in front of this task for a locked user. + * <p> + * We'll do this if either of the following holds: + * <ul> + * <li>The top activity explicitly belongs to {@param userId}.</li> + * <li>The top activity returns a result to an activity belonging to {@param userId}.</li> + * </ul> + * + * @return {@code true} if the top activity looks like it belongs to {@param userId}. + */ + private boolean taskTopActivityIsUser(TaskRecord task, @UserIdInt int userId) { + // To handle the case that work app is in the task but just is not the top one. + final ActivityRecord activityRecord = task.getTopActivity(); + final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null); + + return (activityRecord != null && activityRecord.userId == userId) + || (resultTo != null && resultTo.userId == userId); + } + + void cancelInitializingActivities() { + 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); + stack.cancelInitializingActivities(); + } + } + } + + TaskRecord anyTaskForId(int id) { + return anyTaskForId(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE); + } + + TaskRecord anyTaskForId(int id, @AnyTaskForIdMatchTaskMode int matchMode) { + return anyTaskForId(id, matchMode, null, !ON_TOP); + } + + /** + * Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise. + * @param id Id of the task we would like returned. + * @param matchMode The mode to match the given task id in. + * @param aOptions The activity options to use for restoration. Can be null. + * @param onTop If the stack for the task should be the topmost on the display. + */ + TaskRecord anyTaskForId(int id, @AnyTaskForIdMatchTaskMode int matchMode, + @Nullable ActivityOptions aOptions, boolean onTop) { + // If options are set, ensure that we are attempting to actually restore a task + if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) { + throw new IllegalArgumentException("Should not specify activity options for non-restore" + + " lookup"); + } + + int numDisplays = mActivityDisplays.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + final TaskRecord task = stack.taskForIdLocked(id); + if (task == null) { + continue; + } + if (aOptions != null) { + // Resolve the stack the task should be placed in now based on options + // and reparent if needed. + final ActivityStack launchStack = + getLaunchStack(null, aOptions, task, onTop); + if (launchStack != null && stack != launchStack) { + final int reparentMode = onTop + ? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE; + task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME, + "anyTaskForId"); + } + } + return task; + } + } + + // If we are matching stack tasks only, return now + if (matchMode == MATCH_TASK_IN_STACKS_ONLY) { + return null; + } + + // Otherwise, check the recent tasks and return if we find it there and we are not restoring + // the task from recents + if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents"); + final TaskRecord task = mStackSupervisor.mRecentTasks.getTask(id); + + if (task == null) { + if (DEBUG_RECENTS) { + Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents"); + } + + return null; + } + + if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) { + return task; + } + + // Implicitly, this case is MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE + if (!mStackSupervisor.restoreRecentTaskLocked(task, aOptions, onTop)) { + if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, + "Couldn't restore task id=" + id + " found in recents"); + return null; + } + if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Restored task id=" + id + " from in recents"); + return task; + } + + ActivityRecord isInAnyStack(IBinder token) { + int numDisplays = mActivityDisplays.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + final ActivityRecord r = stack.isInStackLocked(token); + if (r != null) { + return r; + } + } + } + return null; + } + + @VisibleForTesting + void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list, + @WindowConfiguration.ActivityType int ignoreActivityType, + @WindowConfiguration.WindowingMode int ignoreWindowingMode, int callingUid, + boolean allowed) { + mStackSupervisor.mRunningTasks.getTasks(maxNum, list, ignoreActivityType, + ignoreWindowingMode, mActivityDisplays, callingUid, allowed); + } + + void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) { + boolean sendHint = forceSend; + + if (!sendHint) { + // Send power hint if we don't know what we're launching yet + sendHint = targetActivity == null || targetActivity.app == null; + } + + if (!sendHint) { // targetActivity != null + // Send power hint when the activity's process is different than the current resumed + // activity on all displays, or if there are no resumed activities in the system. + boolean noResumedActivities = true; + boolean allFocusedProcessesDiffer = true; + for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) { + final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx); + final ActivityRecord resumedActivity = activityDisplay.getResumedActivity(); + final WindowProcessController resumedActivityProcess = + resumedActivity == null ? null : resumedActivity.app; + + noResumedActivities &= resumedActivityProcess == null; + if (resumedActivityProcess != null) { + allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app); + } + } + sendHint = noResumedActivities || allFocusedProcessesDiffer; + } + + if (sendHint && mService.mPowerManagerInternal != null) { + mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 1); + mPowerHintSent = true; + } + } + + void sendPowerHintForLaunchEndIfNeeded() { + // Trigger launch power hint if activity is launched + if (mPowerHintSent && mService.mPowerManagerInternal != null) { + mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 0); + mPowerHintSent = false; + } + } + + private void calculateDefaultMinimalSizeOfResizeableTasks() { + final Resources res = mService.mContext.getResources(); + final float minimalSize = res.getDimension( + com.android.internal.R.dimen.default_minimal_size_resizable_task); + final DisplayMetrics dm = res.getDisplayMetrics(); + + mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density); + } + + /** + * Dumps the activities matching the given {@param name} in the either the focused stack + * or all visible stacks if {@param dumpVisibleStacks} is true. + */ + ArrayList<ActivityRecord> getDumpActivities(String name, boolean dumpVisibleStacksOnly, + boolean dumpFocusedStackOnly) { + if (dumpFocusedStackOnly) { + return getTopDisplayFocusedStack().getDumpActivitiesLocked(name); + } else { + ArrayList<ActivityRecord> activities = new ArrayList<>(); + int numDisplays = mActivityDisplays.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) { + activities.addAll(stack.getDumpActivitiesLocked(name)); + } + } + } + return activities; + } + } + + public void dump(PrintWriter pw, String prefix) { + pw.print(prefix); + pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack()); + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityDisplay display = mActivityDisplays.get(i); + display.dump(pw, prefix); + } + } + + /** + * Dump all connected displays' configurations. + * @param prefix Prefix to apply to each line of the dump. + */ + void dumpDisplayConfigs(PrintWriter pw, String prefix) { + pw.print(prefix); pw.println("Display override configurations:"); + final int displayCount = mActivityDisplays.size(); + for (int i = 0; i < displayCount; i++) { + final ActivityDisplay activityDisplay = mActivityDisplays.get(i); + pw.print(prefix); pw.print(" "); pw.print(activityDisplay.mDisplayId); pw.print(": "); + pw.println(activityDisplay.getOverrideConfiguration()); + } + } + + public void dumpDisplays(PrintWriter pw) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityDisplay display = mActivityDisplays.get(i); + pw.print("[id:" + display.mDisplayId + " stacks:"); + display.dumpStacks(pw); + pw.print("]"); + } + } + + boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient, + String dumpPackage) { + boolean printed = false; + boolean needSep = false; + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx); + pw.print("Display #"); pw.print(activityDisplay.mDisplayId); + pw.println(" (activities from top to bottom):"); + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + pw.println(); + pw.println(" Stack #" + stack.mStackId + + ": type=" + activityTypeToString(stack.getActivityType()) + + " mode=" + windowingModeToString(stack.getWindowingMode())); + pw.println(" isSleeping=" + stack.shouldSleepActivities()); + pw.println(" mBounds=" + stack.getOverrideBounds()); + + printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage, + needSep); + + printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, " ", "Run", false, + !dumpAll, false, dumpPackage, true, + " Running activities (most recent first):", null); + + needSep = printed; + boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep, + " mPausingActivity: "); + if (pr) { + printed = true; + needSep = false; + } + pr = printThisActivity(pw, stack.getResumedActivity(), dumpPackage, needSep, + " mResumedActivity: "); + if (pr) { + printed = true; + needSep = false; + } + if (dumpAll) { + pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep, + " mLastPausedActivity: "); + if (pr) { + printed = true; + needSep = true; + } + printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage, + needSep, " mLastNoHistoryActivity: "); + } + needSep = printed; + } + printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep, + " ResumedActivity:"); + } + + printed |= dumpHistoryList(fd, pw, mStackSupervisor.mFinishingActivities, " ", + "Fin", false, !dumpAll, + false, dumpPackage, true, " Activities waiting to finish:", null); + printed |= dumpHistoryList(fd, pw, mStackSupervisor.mStoppingActivities, " ", + "Stop", false, !dumpAll, + false, dumpPackage, true, " Activities waiting to stop:", null); + printed |= dumpHistoryList(fd, pw, + mStackSupervisor.mActivitiesWaitingForVisibleActivity, " ", "Wait", + false, !dumpAll, false, dumpPackage, true, + " Activities waiting for another to become visible:", null); + printed |= dumpHistoryList(fd, pw, mStackSupervisor.mGoingToSleepActivities, + " ", "Sleep", false, !dumpAll, + false, dumpPackage, true, " Activities waiting to sleep:", null); + + return printed; + } + + void writeToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */); + for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) { + final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx); + activityDisplay.writeToProto(proto, DISPLAYS); + } + mStackSupervisor.getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER); + // TODO(b/111541062): Update tests to look for resumed activities on all displays + final ActivityStack focusedStack = getTopDisplayFocusedStack(); + if (focusedStack != null) { + proto.write(FOCUSED_STACK_ID, focusedStack.mStackId); + final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity(); + if (focusedActivity != null) { + focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY); + } + } else { + proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID); + } + proto.write(IS_HOME_RECENTS_COMPONENT, + mStackSupervisor.mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser)); + mService.getActivityStartController().writeToProto(proto, PENDING_ACTIVITIES); + proto.end(token); + } + + private final class SleepTokenImpl extends ActivityTaskManagerInternal.SleepToken { + private final String mTag; + private final long mAcquireTime; + private final int mDisplayId; + + public SleepTokenImpl(String tag, int displayId) { + mTag = tag; + mDisplayId = displayId; + mAcquireTime = SystemClock.uptimeMillis(); + } + + @Override + public void release() { + synchronized (mService.mGlobalLock) { + removeSleepToken(this); + } + } + + @Override + public String toString() { + return "{\"" + mTag + "\", display " + mDisplayId + + ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}"; + } + } +} diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index fd9120a6c12e..80d1368427a3 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -82,12 +82,16 @@ import java.util.ArrayList; import java.util.function.Consumer; /** Root {@link WindowContainer} for the device. */ -class RootWindowContainer extends WindowContainer<DisplayContent> { +class RootWindowContainer extends WindowContainer<DisplayContent> + implements ConfigurationContainerListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "RootWindowContainer" : TAG_WM; private static final int SET_SCREEN_BRIGHTNESS_OVERRIDE = 1; private static final int SET_USER_ACTIVITY_TIMEOUT = 2; + // TODO: Remove after object merge with RootActivityContainer. + private RootActivityContainer mRootActivityContainer; + private Object mLastWindowFreezeSource = null; private Session mHoldScreen = null; private float mScreenBrightness = -1; @@ -145,6 +149,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { mHandler = new MyHandler(service.mH.getLooper()); } + void setRootActivityContainer(RootActivityContainer container) { + mRootActivityContainer = container; + if (container != null) { + container.registerConfigurationChangeListener(this); + } + } + boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) { boolean changed = false; int topFocusedDisplayId = INVALID_DISPLAY; @@ -222,6 +233,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { final DisplayContent existing = getDisplayContent(displayId); if (existing != null) { + initializeDisplayOverrideConfiguration(controller, existing); existing.setController(controller); return existing; } @@ -231,6 +243,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display); mService.mDisplayWindowSettings.applySettingsToDisplayLocked(dc); + initializeDisplayOverrideConfiguration(controller, dc); if (mService.mDisplayManagerInternal != null) { mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager( @@ -243,6 +256,19 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { return dc; } + /** + * The display content may have configuration set from {@link #DisplayWindowSettings}. This + * callback let the owner of container know there is existing configuration to prevent the + * values from being replaced by the initializing {@link #ActivityDisplay}. + */ + private void initializeDisplayOverrideConfiguration(DisplayWindowController controller, + DisplayContent displayContent) { + if (controller != null && controller.mListener != null) { + controller.mListener.onInitializeOverrideConfiguration( + displayContent.getOverrideConfiguration()); + } + } + boolean isLayoutNeeded() { final int numDisplays = mChildren.size(); for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { @@ -495,9 +521,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(winAnimator.mWin, "RECOVER DESTROY", false); winAnimator.destroySurface(); - if (winAnimator.mWin.mAppToken != null - && winAnimator.mWin.mAppToken.getController() != null) { - winAnimator.mWin.mAppToken.getController().removeStartingWindow(); + if (winAnimator.mWin.mAppToken != null) { + winAnimator.mWin.mAppToken.removeStartingWindow(); } } @@ -1013,9 +1038,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { @Override void positionChildAt(int position, DisplayContent child, boolean includingParents) { super.positionChildAt(position, child, includingParents); - final RootWindowContainerController controller = getController(); - if (controller != null) { - controller.onChildPositionChanged(child, position); + if (mRootActivityContainer != null) { + mRootActivityContainer.onChildPositionChanged(child.getController(), position); } } @@ -1025,11 +1049,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { } @Override - RootWindowContainerController getController() { - return (RootWindowContainerController) super.getController(); - } - - @Override void scheduleAnimation() { mService.scheduleAnimationLocked(); } diff --git a/services/core/java/com/android/server/wm/RootWindowContainerController.java b/services/core/java/com/android/server/wm/RootWindowContainerController.java deleted file mode 100644 index 11762201852b..000000000000 --- a/services/core/java/com/android/server/wm/RootWindowContainerController.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -/** - * Controller for the root container. This is created by activity manager to link activity - * stack supervisor to the root window container they use in window manager. - */ -public class RootWindowContainerController - extends WindowContainerController<RootWindowContainer, RootWindowContainerListener> { - - public RootWindowContainerController(RootWindowContainerListener listener) { - super(listener, WindowManagerService.getInstance()); - synchronized (mGlobalLock) { - mRoot.setController(this); - } - } - - void onChildPositionChanged(DisplayContent child, int position) { - // This callback invokes to AM directly so here assumes AM lock is held. If there is another - // path called only with WM lock, it should change to use handler to post or move outside of - // WM lock with adding AM lock. - mListener.onChildPositionChanged(child.getController(), position); - } - - /** Move the display to the given position. */ - public void positionChildAt(DisplayWindowController child, int position) { - synchronized (mGlobalLock) { - mContainer.positionChildAt(position, child.mContainer); - } - } -} diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index df97027da64f..3947bd47b588 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -222,7 +222,7 @@ class ScreenRotationAnimation { } public ScreenRotationAnimation(Context context, DisplayContent displayContent, - boolean forceDefaultOrientation, boolean isSecure, WindowManagerService service) { + boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) { mService = service; mContext = context; mDisplayContent = displayContent; @@ -234,7 +234,7 @@ class ScreenRotationAnimation { final int originalWidth; final int originalHeight; DisplayInfo displayInfo = displayContent.getDisplayInfo(); - if (forceDefaultOrientation) { + if (fixedToUserRotation) { // Emulated orientation. mForceDefaultOrientation = true; originalWidth = displayContent.mBaseDisplayWidth; @@ -261,7 +261,7 @@ class ScreenRotationAnimation { try { mSurfaceControl = displayContent.makeOverlay() .setName("ScreenshotSurface") - .setSize(mWidth, mHeight) + .setBufferSize(mWidth, mHeight) .setSecure(isSecure) .build(); diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 6838c55100e8..37b5a7c30218 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -50,6 +50,7 @@ import android.view.InputChannel; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceSession; +import android.view.InsetsState; import android.view.WindowManager; import com.android.internal.os.logging.MetricsLoggerWrapper; @@ -153,17 +154,21 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, - DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) { + DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel, + InsetsState outInsetsState) { return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame, - outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel); + outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel, + outInsetsState); } @Override public int addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs, - int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets) { + int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, + InsetsState outInsetsState) { return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, new Rect() /* outFrame */, outContentInsets, outStableInsets, null /* outOutsets */, - new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */); + new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */, + outInsetsState); } @Override @@ -182,7 +187,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Rect outBackdropFrame, DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration, - Surface outSurface) { + Surface outSurface, InsetsState outInsetsState) { if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from " + Binder.getCallingPid()); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag); @@ -190,7 +195,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { requestedWidth, requestedHeight, viewFlags, flags, frameNumber, outFrame, outOverscanInsets, outContentInsets, outVisibleInsets, outStableInsets, outsets, outBackdropFrame, cutout, - mergedConfiguration, outSurface); + mergedConfiguration, outSurface, outInsetsState); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to " + Binder.getCallingPid()); diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java index e97b36683362..82f2ad89d407 100644 --- a/services/core/java/com/android/server/wm/StrictModeFlash.java +++ b/services/core/java/com/android/server/wm/StrictModeFlash.java @@ -16,7 +16,6 @@ package com.android.server.wm; - import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -24,12 +23,9 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PixelFormat; import android.graphics.Rect; -import android.graphics.Region; -import android.view.Display; -import android.view.Surface.OutOfResourcesException; import android.view.Surface; +import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl; -import android.view.SurfaceSession; class StrictModeFlash { private static final String TAG = TAG_WITH_CLASS_NAME ? "StrictModeFlash" : TAG_WM; @@ -46,7 +42,7 @@ class StrictModeFlash { try { ctrl = dc.makeOverlay() .setName("StrictModeFlash") - .setSize(1, 1) + .setBufferSize(1, 1) .setFormat(PixelFormat.TRANSLUCENT) .build(); ctrl.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101); // one more than Watermark? arbitrary. @@ -122,7 +118,7 @@ class StrictModeFlash { } mLastDW = dw; mLastDH = dh; - mSurfaceControl.setSize(dw, dh); + mSurfaceControl.setBufferSize(dw, dh); mDrawNeeded = true; } diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index 31c0c7f588c3..11068ce8bace 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -302,8 +302,7 @@ class SurfaceAnimator { if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash"); final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash() .setParent(mAnimatable.getAnimationLeashParent()) - .setName(surface + " - animation-leash") - .setSize(width, height); + .setName(surface + " - animation-leash"); final SurfaceControl leash = builder.build(); t.setWindowCrop(leash, width, height); if (!hidden) { diff --git a/services/core/java/com/android/server/wm/TEST_MAPPING b/services/core/java/com/android/server/wm/TEST_MAPPING index 0c9a14be964f..bbe542458b87 100644 --- a/services/core/java/com/android/server/wm/TEST_MAPPING +++ b/services/core/java/com/android/server/wm/TEST_MAPPING @@ -1,17 +1,6 @@ { "presubmit": [ { - "name": "CtsWindowManagerDeviceTestCases", - "options": [ - { - "include-annotation": "android.platform.test.annotations.Presubmit" - }, - { - "exclude-annotation": "android.support.test.filters.FlakyTest" - } - ] - }, - { "name": "FrameworksServicesTests", "options": [ { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index c9800f85cb22..6904ef58dc24 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -302,7 +302,6 @@ class Task extends WindowContainer<AppWindowToken> { @Override void onDisplayChanged(DisplayContent dc) { - updateSurfaceSize(dc); adjustBoundsForDisplayChangeIfNeeded(dc); super.onDisplayChanged(dc); } diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 117984af67e7..4ae2a79e2697 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -138,7 +138,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { // STEP 1: Determine the display to launch the activity/task. final int displayId = getPreferredLaunchDisplay(task, options, source, currentParams); outParams.mPreferredDisplayId = displayId; - ActivityDisplay display = mSupervisor.getActivityDisplay(displayId); + ActivityDisplay display = mSupervisor.mRootActivityContainer.getActivityDisplay(displayId); if (DEBUG) { appendLog("display-id=" + outParams.mPreferredDisplayId + " display-windowing-mode=" + display.getWindowingMode()); @@ -300,12 +300,14 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { displayId = stack.mDisplayId; } - if (displayId != INVALID_DISPLAY && mSupervisor.getActivityDisplay(displayId) == null) { + if (displayId != INVALID_DISPLAY + && mSupervisor.mRootActivityContainer.getActivityDisplay(displayId) == null) { displayId = currentParams.mPreferredDisplayId; } displayId = (displayId == INVALID_DISPLAY) ? currentParams.mPreferredDisplayId : displayId; - return (displayId != INVALID_DISPLAY && mSupervisor.getActivityDisplay(displayId) != null) + return (displayId != INVALID_DISPLAY + && mSupervisor.mRootActivityContainer.getActivityDisplay(displayId) != null) ? displayId : DEFAULT_DISPLAY; } @@ -606,7 +608,8 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { || displayBounds.height() < inOutBounds.height()) { // There is no way for us to fit the bounds in the display without changing width // or height. Just move the start to align with the display. - final int layoutDirection = mSupervisor.getConfiguration().getLayoutDirection(); + final int layoutDirection = + mSupervisor.mRootActivityContainer.getConfiguration().getLayoutDirection(); final int left = layoutDirection == View.LAYOUT_DIRECTION_RTL ? displayBounds.width() - inOutBounds.width() : 0; diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java index 8120dec7e48f..d50af385865e 100644 --- a/services/core/java/com/android/server/wm/TaskPersister.java +++ b/services/core/java/com/android/server/wm/TaskPersister.java @@ -16,7 +16,7 @@ package com.android.server.wm; -import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; +import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; import android.annotation.NonNull; import android.graphics.Bitmap; @@ -330,7 +330,7 @@ public class TaskPersister implements PersisterQueue.Listener { // mWriteQueue.add(new TaskWriteQueueItem(task)); final int taskId = task.taskId; - if (mStackSupervisor.anyTaskForIdLocked(taskId, + if (mService.mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) { // Should not happen. Slog.wtf(TAG, "Existing task with taskId " + taskId + "found"); diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java index 28bc039d6ee4..5a70325fbd87 100644 --- a/services/core/java/com/android/server/wm/TaskPositioningController.java +++ b/services/core/java/com/android/server/wm/TaskPositioningController.java @@ -82,7 +82,7 @@ class TaskPositioningController { if (mInputSurface == null) { mInputSurface = mService.makeSurfaceBuilder(dc.getSession()) .setContainerLayer(true) - .setName("Drag and Drop Input Consumer").setSize(1, 1).build(); + .setName("Drag and Drop Input Consumer").build(); } final InputWindowHandle h = getDragWindowHandleLocked(); diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index eec10aba5df2..8a3dbada8d3f 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -472,8 +472,8 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont } mResizeMode = resizeMode; mWindowContainerController.setResizeable(resizeMode); - mService.mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); - mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); + mService.mRootActivityContainer.resumeFocusedStacksTopActivities(); } void setTaskDockedResizing(boolean resizing) { @@ -544,10 +544,9 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont // this won't cause tons of irrelevant windows being preserved because only // activities in this task may experience a bounds change. Configs for other // activities stay the same. - mService.mStackSupervisor.ensureActivitiesVisibleLocked(r, 0, - preserveWindow); + mService.mRootActivityContainer.ensureActivitiesVisible(r, 0, preserveWindow); if (!kept) { - mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mService.mRootActivityContainer.resumeFocusedStacksTopActivities(); } } } @@ -623,6 +622,7 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume, boolean schedulePictureInPictureModeChange, String reason) { final ActivityStackSupervisor supervisor = mService.mStackSupervisor; + final RootActivityContainer root = mService.mRootActivityContainer; final WindowManagerService windowManager = mService.mWindowManager; final ActivityStack sourceStack = getStack(); final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStack, @@ -655,7 +655,7 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont boolean kept = true; try { final ActivityRecord r = topRunningActivityLocked(); - final boolean wasFocused = r != null && supervisor.isTopDisplayFocusedStack(sourceStack) + final boolean wasFocused = r != null && root.isTopDisplayFocusedStack(sourceStack) && (topRunningActivityLocked() == r); final boolean wasResumed = r != null && sourceStack.getResumedActivity() == r; final boolean wasPaused = r != null && sourceStack.mPausingActivity == r; @@ -748,8 +748,8 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont if (!deferResume) { // The task might have already been running and its visibility needs to be synchronized // with the visibility of the stack / windows. - supervisor.ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow); - supervisor.resumeFocusedStacksTopActivitiesLocked(); + root.ensureActivitiesVisible(null, 0, !mightReplaceWindow); + root.resumeFocusedStacksTopActivities(); } // TODO: Handle incorrect request to move before the actual move, not after. @@ -982,7 +982,7 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont @Override protected void onParentChanged() { super.onParentChanged(); - mService.mStackSupervisor.updateUIDsPresentOnDisplay(); + mService.mRootActivityContainer.updateUIDsPresentOnDisplay(); } // Close up recents linked list. @@ -1143,7 +1143,7 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont } boolean okToShowLocked() { - // NOTE: If {@link TaskRecord#topRunningActivityLocked} return is not null then it is + // NOTE: If {@link TaskRecord#topRunningActivity} return is not null then it is // okay to show the activity when locked. return mService.mStackSupervisor.isCurrentProfileLocked(userId) || topRunningActivityLocked() != null; @@ -1182,7 +1182,7 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont mActivities.add(newTop); // Make sure window manager is aware of the position change. - mWindowContainerController.positionChildAtTop(newTop.mWindowContainerController); + mWindowContainerController.positionChildAtTop(newTop.mAppWindowToken); updateEffectiveIntent(); setFrontOfTask(); @@ -1264,17 +1264,15 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont mService.notifyTaskPersisterLocked(this, false); } - // Sync. with window manager - final AppWindowContainerController appController = r.getWindowContainerController(); - if (appController != null) { + if (r.mAppWindowToken != null) { // Only attempt to move in WM if the child has a controller. It is possible we haven't // created controller for the activity we are starting yet. - mWindowContainerController.positionChildAt(appController, index); + mWindowContainerController.positionChildAt(r.mAppWindowToken, index); } // Make sure the list of display UID whitelists is updated // now that this record is in a new task. - mService.mStackSupervisor.updateUIDsPresentOnDisplay(); + mService.mRootActivityContainer.updateUIDsPresentOnDisplay(); } /** @@ -1683,9 +1681,9 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont // to do this for the pinned stack as the bounds are controlled by the system. if (!inPinnedWindowingMode()) { final int defaultMinSizeDp = - mService.mStackSupervisor.mDefaultMinSizeOfResizeableTaskDp; + mService.mRootActivityContainer.mDefaultMinSizeOfResizeableTaskDp; final ActivityDisplay display = - mService.mStackSupervisor.getActivityDisplay(mStack.mDisplayId); + mService.mRootActivityContainer.getActivityDisplay(mStack.mDisplayId); final float density = (float) display.getConfiguration().densityDpi / DisplayMetrics.DENSITY_DEFAULT; final int defaultMinSize = (int) (defaultMinSizeDp * density); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index a7b0272d4b0d..9a56606aee7f 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -32,6 +32,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; + import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES; import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES; import static com.android.internal.policy.DecorView.getColorViewLeftInset; @@ -65,6 +66,7 @@ import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.View; import android.view.ViewGroup.LayoutParams; +import android.view.InsetsState; import android.view.WindowManager; import android.view.WindowManagerGlobal; @@ -141,6 +143,7 @@ class TaskSnapshotSurface implements StartingSurface { final Rect taskBounds; final Rect tmpContentInsets = new Rect(); final Rect tmpStableInsets = new Rect(); + final InsetsState mTmpInsetsState = new InsetsState(); final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration(); int backgroundColor = WHITE; int statusBarColor = 0; @@ -201,7 +204,7 @@ class TaskSnapshotSurface implements StartingSurface { try { final int res = session.addToDisplay(window, window.mSeq, layoutParams, View.GONE, token.getDisplayContent().getDisplayId(), tmpFrame, tmpRect, tmpRect, - tmpRect, tmpCutout, null); + tmpRect, tmpCutout, null, mTmpInsetsState); if (res < 0) { Slog.w(TAG, "Failed to add snapshot starting window res=" + res); return null; @@ -217,7 +220,7 @@ class TaskSnapshotSurface implements StartingSurface { try { session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1, tmpFrame, tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect, - tmpCutout, tmpMergedConfiguration, surface); + tmpCutout, tmpMergedConfiguration, surface, mTmpInsetsState); } catch (RemoteException e) { // Local call. } @@ -312,7 +315,7 @@ class TaskSnapshotSurface implements StartingSurface { // Keep a reference to it such that it doesn't get destroyed when finalized. mChildSurfaceControl = new SurfaceControl.Builder(session) .setName(mTitle + " - task-snapshot-surface") - .setSize(buffer.getWidth(), buffer.getHeight()) + .setBufferSize(buffer.getWidth(), buffer.getHeight()) .setFormat(buffer.getFormat()) .build(); Surface surface = new Surface(); diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 64f4ba5e24ab..5deb4f1ab7d4 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -248,7 +248,6 @@ public class TaskStack extends WindowContainer<Task> implements getRawBounds(mTmpRect); final Rect stackBounds = getBounds(); getPendingTransaction() - .setSize(mAnimationBackgroundSurface, mTmpRect.width(), mTmpRect.height()) .setWindowCrop(mAnimationBackgroundSurface, mTmpRect.width(), mTmpRect.height()) .setPosition(mAnimationBackgroundSurface, mTmpRect.left - stackBounds.left, mTmpRect.top - stackBounds.top); @@ -751,7 +750,6 @@ public class TaskStack extends WindowContainer<Task> implements if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) { return; } - transaction.setSize(mSurfaceControl, width, height); transaction.setWindowCrop(mSurfaceControl, width, height); mLastSurfaceSize.set(width, height); } diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java index 59b2055193ae..ec64d2e82895 100644 --- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java +++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java @@ -16,6 +16,14 @@ package com.android.server.wm; +import static com.android.server.EventLogTags.WM_TASK_CREATED; +import static com.android.server.wm.ConfigurationContainer.BOUNDS_CHANGE_NONE; +import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; +import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; +import static com.android.server.wm.WindowContainer.POSITION_TOP; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + import android.app.ActivityManager.TaskDescription; import android.app.ActivityManager.TaskSnapshot; import android.graphics.Rect; @@ -24,18 +32,11 @@ import android.os.Looper; import android.os.Message; import android.util.EventLog; import android.util.Slog; + import com.android.internal.annotations.VisibleForTesting; import java.lang.ref.WeakReference; -import static com.android.server.EventLogTags.WM_TASK_CREATED; -import static com.android.server.wm.ConfigurationContainer.BOUNDS_CHANGE_NONE; -import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; -import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; -import static com.android.server.wm.WindowContainer.POSITION_TOP; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; - /** * Controller for the task container. This is created by activity manager to link task records to * the task container they use in window manager. @@ -103,16 +104,15 @@ public class TaskWindowContainerController } } - public void positionChildAtTop(AppWindowContainerController childController) { - positionChildAt(childController, POSITION_TOP); + void positionChildAtTop(AppWindowToken aToken) { + positionChildAt(aToken, POSITION_TOP); } - public void positionChildAt(AppWindowContainerController childController, int position) { + void positionChildAt(AppWindowToken aToken, int position) { synchronized (mService.mGlobalLock) { - final AppWindowToken aToken = childController.mContainer; if (aToken == null) { Slog.w(TAG_WM, - "Attempted to position of non-existing app : " + childController); + "Attempted to position of non-existing app"); return; } diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java index 9216b66e5088..e6ac059d5e02 100644 --- a/services/core/java/com/android/server/wm/Watermark.java +++ b/services/core/java/com/android/server/wm/Watermark.java @@ -20,19 +20,18 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.Paint.FontMetricsInt; import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Typeface; -import android.graphics.Paint.FontMetricsInt; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; import android.view.Display; -import android.view.Surface.OutOfResourcesException; import android.view.Surface; +import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl; -import android.view.SurfaceSession; /** * Displays a watermark on top of the window manager's windows. @@ -116,7 +115,7 @@ class Watermark { try { ctrl = dc.makeOverlay() .setName("WatermarkSurface") - .setSize(1, 1) + .setBufferSize(1, 1) .setFormat(PixelFormat.TRANSLUCENT) .build(); ctrl.setLayerStack(mDisplay.getLayerStack()); @@ -133,7 +132,7 @@ class Watermark { if (mLastDW != dw || mLastDH != dh) { mLastDW = dw; mLastDH = dh; - mSurfaceControl.setSize(dw, dh); + mSurfaceControl.setBufferSize(dw, dh); mDrawNeeded = true; } } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 266006db9987..7e4c62935e05 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -515,24 +515,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } } - /** - * Update the surface size when display changed in order to avoid children being bound by the - * old display size. - * - * Note that we don't want to apply this to all layers, but only limiting this to layers that - * don't set their own size ({@link Task}, {@link WindowState} and {@link WindowToken}). - */ - void updateSurfaceSize(DisplayContent dc) { - if (mSurfaceControl == null) { - return; - } - - final int newSurfaceSize = dc.getSurfaceSize(); - if (mSurfaceControl.getWidth() != newSurfaceSize) { - getPendingTransaction().setSize(mSurfaceControl, newSurfaceSize, newSurfaceSize); - } - } - void setWaitingForDrawnIfResizingChanged() { for (int i = mChildren.size() - 1; i >= 0; --i) { final WindowContainer wc = mChildren.get(i); diff --git a/services/core/java/com/android/server/wm/WindowContainerListener.java b/services/core/java/com/android/server/wm/WindowContainerListener.java index 4b3cd36040c6..3d3d2e02693c 100644 --- a/services/core/java/com/android/server/wm/WindowContainerListener.java +++ b/services/core/java/com/android/server/wm/WindowContainerListener.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import android.content.res.Configuration; + /** * Interface used by the owner/creator of the container to listen to changes with the container. * @see WindowContainerController @@ -23,4 +25,5 @@ package com.android.server.wm; public interface WindowContainerListener { void registerConfigurationChangeListener(ConfigurationContainerListener listener); void unregisterConfigurationChangeListener(ConfigurationContainerListener listener); + default void onInitializeOverrideConfiguration(Configuration config) {} } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 5df34517956a..4085f3d8062a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -218,6 +218,7 @@ import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.View; import android.view.WindowContentFrameStats; +import android.view.InsetsState; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.view.WindowManager.RemoveContentMode; @@ -1111,7 +1112,8 @@ public class WindowManagerService extends IWindowManager.Stub public int addWindow(Session session, IWindow client, int seq, LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, - DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) { + DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel, + InsetsState outInsetsState) { int[] appOp = new int[1]; int res = mPolicy.checkAddPermission(attrs, appOp); if (res != WindowManagerGlobal.ADD_OKAY) { @@ -1459,6 +1461,7 @@ public class WindowManagerService extends IWindowManager.Stub outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) { res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR; } + outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win)); if (mInTouchMode) { res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE; @@ -1856,7 +1859,7 @@ public class WindowManagerService extends IWindowManager.Stub long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame, DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration, - Surface outSurface) { + Surface outSurface, InsetsState outInsetsState) { int result = 0; boolean configChanged; final boolean hasStatusBarPermission = @@ -2157,6 +2160,7 @@ public class WindowManagerService extends IWindowManager.Stub outStableInsets, outOutsets); outCutout.set(win.getWmDisplayCutout().getDisplayCutout()); outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw())); + outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win)); if (localLOGV) Slog.v( TAG_WM, "Relayout given client " + client.asBinder() + ", requestedWidth=" + requestedWidth @@ -3587,6 +3591,17 @@ public class WindowManagerService extends IWindowManager.Stub } } + void setRotateForApp(int displayId, boolean enabled) { + synchronized (mGlobalLock) { + final DisplayContent display = mRoot.getDisplayContent(displayId); + if (display == null) { + Slog.w(TAG, "Trying to set rotate for app for a missing display."); + return; + } + display.getDisplayRotation().setFixedToUserRotation(enabled); + } + } + @Override public void freezeRotation(int rotation) { freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation); @@ -5393,7 +5408,7 @@ public class WindowManagerService extends IWindowManager.Stub displayContent.updateDisplayInfo(); screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent, - displayContent.getDisplayRotation().isDefaultOrientationForced(), isSecure, + displayContent.getDisplayRotation().isFixedToUserRotation(), isSecure, this); mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId, screenRotationAnimation); diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index bf77ba86075d..6865ce305442 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -76,6 +76,8 @@ public class WindowManagerShellCommand extends ShellCommand { getNextArgRequired()); case "set-user-rotation": return runSetDisplayUserRotation(pw); + case "set-fix-to-user-rotation": + return runSetFixToUserRotation(pw); default: return handleDefaultCommands(cmd); } @@ -297,6 +299,32 @@ public class WindowManagerShellCommand extends ShellCommand { } } + private int runSetFixToUserRotation(PrintWriter pw) { + int displayId = Display.DEFAULT_DISPLAY; + String arg = getNextArgRequired(); + if ("-d".equals(arg)) { + displayId = Integer.parseInt(getNextArgRequired()); + arg = getNextArgRequired(); + } + + final boolean enabled; + switch (arg) { + case "enabled": + enabled = true; + break; + case "disabled": + enabled = false; + break; + default: + getErrPrintWriter().println("Error: expecting enabled or disabled, but we get " + + arg); + return -1; + } + + mInternal.setRotateForApp(displayId, enabled); + return 0; + } + @Override public void onHelp() { PrintWriter pw = getOutPrintWriter(); @@ -316,6 +344,8 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" Dismiss the keyguard, prompting user for auth if necessary."); pw.println(" set-user-rotation [free|lock] [-d DISPLAY_ID] [rotation]"); pw.println(" Set user rotation mode and user rotation."); + pw.println(" set-fix-to-user-rotation [-d DISPLAY_ID] [enabled|disabled]"); + pw.println(" Enable or disable rotating display for app requested orientation."); if (!IS_USER) { pw.println(" tracing (start | stop)"); pw.println(" Start or stop window tracing."); diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 484bd8c30462..578af2eebe88 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -755,9 +755,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return; } final ActivityDisplay activityDisplay = - mAtm.mStackSupervisor.getActivityDisplay(mDisplayId); + mAtm.mRootActivityContainer.getActivityDisplay(mDisplayId); if (activityDisplay != null) { - mAtm.mStackSupervisor.getActivityDisplay( + mAtm.mRootActivityContainer.getActivityDisplay( mDisplayId).unregisterConfigurationChangeListener(this); } mDisplayId = INVALID_DISPLAY; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 9efaefefb192..cfd1f86cdeaa 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -143,6 +143,7 @@ import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER; import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES; import android.annotation.CallSuper; +import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.Context; import android.content.res.Configuration; @@ -194,6 +195,7 @@ import android.view.animation.Interpolator; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ToBooleanFunction; import com.android.server.policy.WindowManagerPolicy; +import com.android.server.policy.WindowManagerPolicy.DisplayContentInfo; import com.android.server.wm.LocalAnimationAdapter.AnimationSpec; import com.android.server.wm.utils.InsetUtils; import com.android.server.wm.utils.WmDisplayCutout; @@ -578,6 +580,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ private boolean mIsDimming = false; + private @Nullable InsetsSourceProvider mInsetProvider; + private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f; void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation, @@ -1261,7 +1265,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override void onDisplayChanged(DisplayContent dc) { - updateSurfaceSize(dc); super.onDisplayChanged(dc); // Window was not laid out for this display yet, so make sure mLayoutSeq does not match. if (dc != null) { @@ -2956,6 +2959,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } + /** + * Called when the insets state changed. + */ + void notifyInsetsChanged() { + try { + mClient.insetsChanged( + getDisplayContent().getInsetsStateController().getInsetsForDispatch(this)); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to deliver inset state change", e); + } + } + Rect getBackdropFrame(Rect frame) { // When the task is docked, we send fullscreen sized backDropFrame as soon as resizing // start even if we haven't received the relayout window, so that the client requests @@ -4777,6 +4792,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWindowFrames.setContentChanged(false); } + void setInsetProvider(InsetsSourceProvider insetProvider) { + mInsetProvider = insetProvider; + } + + InsetsSourceProvider getInsetProvider() { + return mInsetProvider; + } + private final class MoveAnimationSpec implements AnimationSpec { private final long mDuration; diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index e090cc5177ba..78a3fe5ac4ca 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -861,7 +861,7 @@ class WindowStateAnimator { // to find the surface size changed underneath it. final boolean relayout = !w.mRelayoutCalled || w.mInRelayout; if (relayout) { - mSurfaceResized = mSurfaceController.setSizeInTransaction( + mSurfaceResized = mSurfaceController.setBufferSizeInTransaction( mTmpSize.width(), mTmpSize.height(), recoveringMemory); } else { mSurfaceResized = false; diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index 6821e9486a78..ce627e23e6ee 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -19,34 +19,28 @@ package com.android.server.wm; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Surface.SCALING_MODE_SCALE_TO_WINDOW; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; +import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; -import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; 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.WindowSurfaceControllerProto.LAYER; import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN; -import android.graphics.Point; -import android.graphics.PointF; import android.graphics.Rect; import android.graphics.Region; -import android.os.IBinder; import android.os.Debug; +import android.os.IBinder; import android.os.Trace; +import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.WindowContentFrameStats; -import android.view.Surface.OutOfResourcesException; - -import android.util.Slog; -import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.ArrayList; class WindowSurfaceController { static final String TAG = TAG_WITH_CLASS_NAME ? "WindowSurfaceController" : TAG_WM; @@ -106,7 +100,7 @@ class WindowSurfaceController { final SurfaceControl.Builder b = win.makeSurface() .setParent(win.getSurfaceControl()) .setName(name) - .setSize(w, h) + .setBufferSize(w, h) .setFormat(format) .setFlags(flags) .setMetadata(windowType, ownerUid); @@ -303,7 +297,7 @@ class WindowSurfaceController { } } - boolean setSizeInTransaction(int width, int height, boolean recoveringMemory) { + boolean setBufferSizeInTransaction(int width, int height, boolean recoveringMemory) { final boolean surfaceResized = mSurfaceW != width || mSurfaceH != height; if (surfaceResized) { mSurfaceW = width; @@ -312,7 +306,7 @@ class WindowSurfaceController { try { if (SHOW_TRANSACTIONS) logSurface( "SIZE " + width + "x" + height, null); - mSurfaceControl.setSize(width, height); + mSurfaceControl.setBufferSize(width, height); } catch (RuntimeException e) { // If something goes wrong with the surface (such // as running out of memory), don't take down the diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 0cf79b63e9dc..d8242f8a6daa 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -265,7 +265,6 @@ class WindowToken extends WindowContainer<WindowState> { // to another display before the window behind // it is ready. - updateSurfaceSize(dc); super.onDisplayChanged(dc); } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index bf83ac13fd93..8b873e3f9ad7 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -108,6 +108,7 @@ cc_defaults { "android.hardware.contexthub@1.0", "android.hardware.gnss@1.0", "android.hardware.gnss@1.1", + "android.hardware.gnss@2.0", "android.hardware.ir@1.0", "android.hardware.light@2.0", "android.hardware.power@1.0", diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index fcd9335874e1..b36a8a7cdf19 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -32,6 +32,7 @@ #include <atomic> #include <cinttypes> #include <limits.h> +#include <android-base/parseint.h> #include <android-base/stringprintf.h> #include <android_runtime/AndroidRuntime.h> #include <android_runtime/Log.h> @@ -71,6 +72,7 @@ #define INDENT " " +using android::base::ParseUint; using android::base::StringPrintf; namespace android { @@ -81,6 +83,7 @@ namespace android { static const float POINTER_SPEED_EXPONENT = 1.0f / 4; static struct { + jclass clazz; jmethodID notifyConfigurationChanged; jmethodID notifyInputDevicesChanged; jmethodID notifySwitch; @@ -95,6 +98,7 @@ static struct { jmethodID checkInjectEventsPermission; jmethodID getVirtualKeyQuietTimeMillis; jmethodID getExcludedDeviceNames; + jmethodID getInputPortAssociations; jmethodID getKeyRepeatTimeout; jmethodID getKeyRepeatDelay; jmethodID getHoverTapTimeout; @@ -183,6 +187,13 @@ enum { WM_ACTION_PASS_TO_USER = 1, }; +static std::string getStringElementFromJavaArray(JNIEnv* env, jobjectArray array, jsize index) { + jstring item = jstring(env->GetObjectArrayElement(array, index)); + ScopedUtfChars chars(env, item); + std::string result(chars.c_str()); + return result; +} + // --- NativeInputManager --- @@ -452,20 +463,44 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon } outConfig->excludedDeviceNames.clear(); - jobjectArray excludedDeviceNames = jobjectArray(env->CallObjectMethod(mServiceObj, - gServiceClassInfo.getExcludedDeviceNames)); + jobjectArray excludedDeviceNames = jobjectArray(env->CallStaticObjectMethod( + gServiceClassInfo.clazz, gServiceClassInfo.getExcludedDeviceNames)); if (!checkAndClearExceptionFromCallback(env, "getExcludedDeviceNames") && excludedDeviceNames) { jsize length = env->GetArrayLength(excludedDeviceNames); for (jsize i = 0; i < length; i++) { - jstring item = jstring(env->GetObjectArrayElement(excludedDeviceNames, i)); - const char* deviceNameChars = env->GetStringUTFChars(item, nullptr); - outConfig->excludedDeviceNames.push_back(deviceNameChars); - env->ReleaseStringUTFChars(item, deviceNameChars); - env->DeleteLocalRef(item); + std::string deviceName = getStringElementFromJavaArray(env, excludedDeviceNames, i); + outConfig->excludedDeviceNames.push_back(deviceName); } env->DeleteLocalRef(excludedDeviceNames); } + // Associations between input ports and display ports + // The java method packs the information in the following manner: + // Original data: [{'inputPort1': '1'}, {'inputPort2': '2'}] + // Received data: ['inputPort1', '1', 'inputPort2', '2'] + // So we unpack accordingly here. + outConfig->portAssociations.clear(); + jobjectArray portAssociations = jobjectArray(env->CallStaticObjectMethod( + gServiceClassInfo.clazz, gServiceClassInfo.getInputPortAssociations)); + if (!checkAndClearExceptionFromCallback(env, "getInputPortAssociations") && portAssociations) { + jsize length = env->GetArrayLength(portAssociations); + for (jsize i = 0; i < length / 2; i++) { + std::string inputPort = getStringElementFromJavaArray(env, portAssociations, 2 * i); + std::string displayPortStr = + getStringElementFromJavaArray(env, portAssociations, 2 * i + 1); + uint8_t displayPort; + // Should already have been validated earlier, but do it here for safety. + bool success = ParseUint(displayPortStr, &displayPort); + if (!success) { + ALOGE("Could not parse entry in port configuration file, received: %s", + displayPortStr.c_str()); + continue; + } + outConfig->portAssociations.insert({inputPort, displayPort}); + } + env->DeleteLocalRef(portAssociations); + } + jint hoverTapTimeout = env->CallIntMethod(mServiceObj, gServiceClassInfo.getHoverTapTimeout); if (!checkAndClearExceptionFromCallback(env, "getHoverTapTimeout")) { @@ -1697,6 +1732,10 @@ static const JNINativeMethod gInputManagerMethods[] = { var = env->GetMethodID(clazz, methodName, methodDescriptor); \ LOG_FATAL_IF(! (var), "Unable to find method " methodName); +#define GET_STATIC_METHOD_ID(var, clazz, methodName, methodDescriptor) \ + var = env->GetStaticMethodID(clazz, methodName, methodDescriptor); \ + LOG_FATAL_IF(! (var), "Unable to find static method " methodName); + #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ LOG_FATAL_IF(! (var), "Unable to find field " fieldName); @@ -1711,6 +1750,7 @@ int register_android_server_InputManager(JNIEnv* env) { jclass clazz; FIND_CLASS(clazz, "com/android/server/input/InputManagerService"); + gServiceClassInfo.clazz = clazz; GET_METHOD_ID(gServiceClassInfo.notifyConfigurationChanged, clazz, "notifyConfigurationChanged", "(J)V"); @@ -1754,9 +1794,12 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gServiceClassInfo.getVirtualKeyQuietTimeMillis, clazz, "getVirtualKeyQuietTimeMillis", "()I"); - GET_METHOD_ID(gServiceClassInfo.getExcludedDeviceNames, clazz, + GET_STATIC_METHOD_ID(gServiceClassInfo.getExcludedDeviceNames, clazz, "getExcludedDeviceNames", "()[Ljava/lang/String;"); + GET_STATIC_METHOD_ID(gServiceClassInfo.getInputPortAssociations, clazz, + "getInputPortAssociations", "()[Ljava/lang/String;"); + GET_METHOD_ID(gServiceClassInfo.getKeyRepeatTimeout, clazz, "getKeyRepeatTimeout", "()I"); diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 92160053804d..4d0556c7507a 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -20,9 +20,11 @@ #include <android/hardware/gnss/1.0/IGnss.h> #include <android/hardware/gnss/1.1/IGnss.h> +#include <android/hardware/gnss/2.0/IGnss.h> #include <android/hardware/gnss/1.0/IGnssMeasurement.h> #include <android/hardware/gnss/1.1/IGnssMeasurement.h> +#include <android/hardware/gnss/2.0/IGnssMeasurement.h> #include <nativehelper/JNIHelp.h> #include "jni.h" #include "hardware_legacy/power.h" @@ -110,13 +112,15 @@ using android::hidl::base::V1_0::IBase; using IGnss_V1_0 = android::hardware::gnss::V1_0::IGnss; using IGnss_V1_1 = android::hardware::gnss::V1_1::IGnss; +using IGnss_V2_0 = android::hardware::gnss::V2_0::IGnss; using IGnssConfiguration_V1_0 = android::hardware::gnss::V1_0::IGnssConfiguration; using IGnssConfiguration_V1_1 = android::hardware::gnss::V1_1::IGnssConfiguration; using IGnssMeasurement_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurement; using IGnssMeasurement_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurement; +using IGnssMeasurement_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurement; using IGnssMeasurementCallback_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurementCallback; using IGnssMeasurementCallback_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurementCallback; - +using IGnssMeasurementCallback_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurementCallback; struct GnssDeathRecipient : virtual public hidl_death_recipient { @@ -135,6 +139,7 @@ static const uint32_t ADR_STATE_HALF_CYCLE_REPORTED = (1<<4); sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr; sp<IGnss_V1_0> gnssHal = nullptr; sp<IGnss_V1_1> gnssHal_V1_1 = nullptr; +sp<IGnss_V2_0> gnssHal_V2_0 = nullptr; sp<IGnssXtra> gnssXtraIface = nullptr; sp<IAGnssRil> agnssRilIface = nullptr; sp<IGnssGeofencing> gnssGeofencingIface = nullptr; @@ -146,6 +151,7 @@ sp<IGnssConfiguration_V1_1> gnssConfigurationIface_V1_1 = nullptr; sp<IGnssNi> gnssNiIface = nullptr; sp<IGnssMeasurement_V1_0> gnssMeasurementIface = nullptr; sp<IGnssMeasurement_V1_1> gnssMeasurementIface_V1_1 = nullptr; +sp<IGnssMeasurement_V2_0> gnssMeasurementIface_V2_0 = nullptr; sp<IGnssNavigationMessage> gnssNavigationMessageIface = nullptr; #define WAKE_LOCK_NAME "GPS" @@ -744,7 +750,9 @@ Return<void> GnssNavigationMessageCallback::gnssNavigationMessageCb( * GnssMeasurementCallback implements the callback methods required for the * GnssMeasurement interface. */ -struct GnssMeasurementCallback : public IGnssMeasurementCallback_V1_1 { +struct GnssMeasurementCallback : public IGnssMeasurementCallback_V2_0 { + Return<void> gnssMeasurementCb_2_0(const IGnssMeasurementCallback_V2_0::GnssData& data) + override; Return<void> gnssMeasurementCb(const IGnssMeasurementCallback_V1_1::GnssData& data) override; Return<void> GnssMeasurementCb(const IGnssMeasurementCallback_V1_0::GnssData& data) override; private: @@ -761,6 +769,11 @@ struct GnssMeasurementCallback : public IGnssMeasurementCallback_V1_1 { void setMeasurementData(JNIEnv* env, jobject clock, jobjectArray measurementArray); }; +Return<void> GnssMeasurementCallback::gnssMeasurementCb_2_0( + const IGnssMeasurementCallback_V2_0::GnssData& data) { + // TODO(b/119571122): implement gnssMeasurementCb_2_0 + return Void(); +} Return<void> GnssMeasurementCallback::gnssMeasurementCb( const IGnssMeasurementCallback_V1_1::GnssData& data) { @@ -1126,13 +1139,22 @@ Return<void> GnssBatchingCallback::gnssLocationBatchCb(const hidl_vec<GnssLocati } static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, jclass clazz) { + gnssHal_V2_0 = IGnss_V2_0::getService(); + if (gnssHal_V2_0 != nullptr) { + gnssHal = gnssHal_V2_0; + gnssHal_V1_1 = gnssHal_V2_0; + return; + } + + ALOGD("gnssHal 2.0 was null, trying 1.1"); gnssHal_V1_1 = IGnss_V1_1::getService(); - if (gnssHal_V1_1 == nullptr) { - ALOGD("gnssHal 1.1 was null, trying 1.0"); - gnssHal = IGnss_V1_0::getService(); - } else { + if (gnssHal_V1_1 != nullptr) { gnssHal = gnssHal_V1_1; + return; } + + ALOGD("gnssHal 1.1 was null, trying 1.0"); + gnssHal = IGnss_V1_0::getService(); } static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass clazz) { @@ -1187,110 +1209,120 @@ static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass LOG_ALWAYS_FATAL("Unable to get Java VM. Error: %d", jvmStatus); } - if (gnssHal != nullptr) { - gnssHalDeathRecipient = new GnssDeathRecipient(); - hardware::Return<bool> linked = gnssHal->linkToDeath( - gnssHalDeathRecipient, /*cookie*/ 0); - if (!linked.isOk()) { - ALOGE("Transaction error in linking to GnssHAL death: %s", - linked.description().c_str()); - } else if (!linked) { - ALOGW("Unable to link to GnssHal death notifications"); - } else { - ALOGD("Link to death notification successful"); - } + if (gnssHal == nullptr) { + ALOGE("Unable to get GPS service\n"); + return; + } - auto gnssXtra = gnssHal->getExtensionXtra(); - if (!gnssXtra.isOk()) { - ALOGD("Unable to get a handle to Xtra"); - } else { - gnssXtraIface = gnssXtra; - } + gnssHalDeathRecipient = new GnssDeathRecipient(); + hardware::Return<bool> linked = gnssHal->linkToDeath(gnssHalDeathRecipient, /*cookie*/ 0); + if (!linked.isOk()) { + ALOGE("Transaction error in linking to GnssHAL death: %s", + linked.description().c_str()); + } else if (!linked) { + ALOGW("Unable to link to GnssHal death notifications"); + } else { + ALOGD("Link to death notification successful"); + } - auto gnssRil = gnssHal->getExtensionAGnssRil(); - if (!gnssRil.isOk()) { - ALOGD("Unable to get a handle to AGnssRil"); - } else { - agnssRilIface = gnssRil; - } + auto gnssXtra = gnssHal->getExtensionXtra(); + if (!gnssXtra.isOk()) { + ALOGD("Unable to get a handle to Xtra"); + } else { + gnssXtraIface = gnssXtra; + } - auto gnssAgnss = gnssHal->getExtensionAGnss(); - if (!gnssAgnss.isOk()) { - ALOGD("Unable to get a handle to AGnss"); - } else { - agnssIface = gnssAgnss; - } + auto gnssRil = gnssHal->getExtensionAGnssRil(); + if (!gnssRil.isOk()) { + ALOGD("Unable to get a handle to AGnssRil"); + } else { + agnssRilIface = gnssRil; + } - auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage(); - if (!gnssNavigationMessage.isOk()) { - ALOGD("Unable to get a handle to GnssNavigationMessage"); - } else { - gnssNavigationMessageIface = gnssNavigationMessage; - } + auto gnssAgnss = gnssHal->getExtensionAGnss(); + if (!gnssAgnss.isOk()) { + ALOGD("Unable to get a handle to AGnss"); + } else { + agnssIface = gnssAgnss; + } - if (gnssHal_V1_1 != nullptr) { - auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1(); - if (!gnssMeasurement.isOk()) { - ALOGD("Unable to get a handle to GnssMeasurement"); - } else { - gnssMeasurementIface_V1_1 = gnssMeasurement; - gnssMeasurementIface = gnssMeasurementIface_V1_1; - } - } else { - auto gnssMeasurement_V1_0 = gnssHal->getExtensionGnssMeasurement(); - if (!gnssMeasurement_V1_0.isOk()) { - ALOGD("Unable to get a handle to GnssMeasurement"); - } else { - gnssMeasurementIface = gnssMeasurement_V1_0; - } - } + auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage(); + if (!gnssNavigationMessage.isOk()) { + ALOGD("Unable to get a handle to GnssNavigationMessage"); + } else { + gnssNavigationMessageIface = gnssNavigationMessage; + } - auto gnssDebug = gnssHal->getExtensionGnssDebug(); - if (!gnssDebug.isOk()) { - ALOGD("Unable to get a handle to GnssDebug"); + if (gnssHal_V2_0 != nullptr) { + // TODO(b/119638366): getExtensionGnssMeasurement_1_1 from gnssHal_V2_0 + auto gnssMeasurement = gnssHal_V2_0->getExtensionGnssMeasurement_2_0(); + if (!gnssMeasurement.isOk()) { + ALOGD("Unable to get a handle to GnssMeasurement_V2_0"); } else { - gnssDebugIface = gnssDebug; + gnssMeasurementIface_V2_0 = gnssMeasurement; + gnssMeasurementIface_V1_1 = gnssMeasurementIface_V2_0; + gnssMeasurementIface = gnssMeasurementIface_V2_0; } + } else if (gnssHal_V1_1 != nullptr) { + auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1(); + if (!gnssMeasurement.isOk()) { + ALOGD("Unable to get a handle to GnssMeasurement_V1_1"); + } else { + gnssMeasurementIface_V1_1 = gnssMeasurement; + gnssMeasurementIface = gnssMeasurementIface_V1_1; + } + } else { + auto gnssMeasurement_V1_0 = gnssHal->getExtensionGnssMeasurement(); + if (!gnssMeasurement_V1_0.isOk()) { + ALOGD("Unable to get a handle to GnssMeasurement"); + } else { + gnssMeasurementIface = gnssMeasurement_V1_0; + } + } - auto gnssNi = gnssHal->getExtensionGnssNi(); - if (!gnssNi.isOk()) { - ALOGD("Unable to get a handle to GnssNi"); - } else { - gnssNiIface = gnssNi; - } + auto gnssDebug = gnssHal->getExtensionGnssDebug(); + if (!gnssDebug.isOk()) { + ALOGD("Unable to get a handle to GnssDebug"); + } else { + gnssDebugIface = gnssDebug; + } - if (gnssHal_V1_1 != nullptr) { - auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1(); - if (!gnssConfiguration.isOk()) { - ALOGD("Unable to get a handle to GnssConfiguration"); - } else { - gnssConfigurationIface_V1_1 = gnssConfiguration; - gnssConfigurationIface = gnssConfigurationIface_V1_1; - } - } else { - auto gnssConfiguration_V1_0 = gnssHal->getExtensionGnssConfiguration(); - if (!gnssConfiguration_V1_0.isOk()) { - ALOGD("Unable to get a handle to GnssConfiguration"); - } else { - gnssConfigurationIface = gnssConfiguration_V1_0; - } - } + auto gnssNi = gnssHal->getExtensionGnssNi(); + if (!gnssNi.isOk()) { + ALOGD("Unable to get a handle to GnssNi"); + } else { + gnssNiIface = gnssNi; + } - auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing(); - if (!gnssGeofencing.isOk()) { - ALOGD("Unable to get a handle to GnssGeofencing"); + if (gnssHal_V1_1 != nullptr) { + auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1(); + if (!gnssConfiguration.isOk()) { + ALOGD("Unable to get a handle to GnssConfiguration"); } else { - gnssGeofencingIface = gnssGeofencing; + gnssConfigurationIface_V1_1 = gnssConfiguration; + gnssConfigurationIface = gnssConfigurationIface_V1_1; } - - auto gnssBatching = gnssHal->getExtensionGnssBatching(); - if (!gnssBatching.isOk()) { - ALOGD("Unable to get a handle to gnssBatching"); + } else { + auto gnssConfiguration_V1_0 = gnssHal->getExtensionGnssConfiguration(); + if (!gnssConfiguration_V1_0.isOk()) { + ALOGD("Unable to get a handle to GnssConfiguration"); } else { - gnssBatchingIface = gnssBatching; + gnssConfigurationIface = gnssConfiguration_V1_0; } + } + + auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing(); + if (!gnssGeofencing.isOk()) { + ALOGD("Unable to get a handle to GnssGeofencing"); + } else { + gnssGeofencingIface = gnssGeofencing; + } + + auto gnssBatching = gnssHal->getExtensionGnssBatching(); + if (!gnssBatching.isOk()) { + ALOGD("Unable to get a handle to gnssBatching"); } else { - ALOGE("Unable to get GPS service\n"); + gnssBatchingIface = gnssBatching; } } @@ -1820,10 +1852,11 @@ static jboolean android_location_GnssMeasurementsProvider_start_measurement_coll sp<GnssMeasurementCallback> cbIface = new GnssMeasurementCallback(); IGnssMeasurement_V1_0::GnssMeasurementStatus result = - IGnssMeasurement_V1_0::GnssMeasurementStatus::ERROR_GENERIC;; - if (gnssMeasurementIface_V1_1 != nullptr) { - result = gnssMeasurementIface_V1_1->setCallback_1_1(cbIface, - enableFullTracking); + IGnssMeasurement_V1_0::GnssMeasurementStatus::ERROR_GENERIC; + if (gnssMeasurementIface_V2_0 != nullptr) { + result = gnssMeasurementIface_V2_0->setCallback_2_0(cbIface, enableFullTracking); + } else if (gnssMeasurementIface_V1_1 != nullptr) { + result = gnssMeasurementIface_V1_1->setCallback_1_1(cbIface, enableFullTracking); } else { if (enableFullTracking == JNI_TRUE) { // full tracking mode not supported in 1.0 HAL @@ -1837,7 +1870,7 @@ static jboolean android_location_GnssMeasurementsProvider_start_measurement_coll static_cast<int32_t>(result)); return JNI_FALSE; } else { - ALOGD("gnss measurement infc has been enabled"); + ALOGD("gnss measurement infc has been enabled"); } return JNI_TRUE; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 6462d163cc9d..b9dabb9ee7ec 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -23,6 +23,9 @@ import android.os.ParcelFileDescriptor; import com.android.server.SystemService; +import java.util.Collections; +import java.util.List; + /** * Defines the required interface for IDevicePolicyManager implemenation. * @@ -97,4 +100,24 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { @Override public void installUpdateFromFile(ComponentName admin, ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback listener) {} + + @Override + public void addCrossProfileCalendarPackage(ComponentName admin, String packageName) { + } + + @Override + public boolean removeCrossProfileCalendarPackage(ComponentName admin, String packageName) { + return false; + } + + @Override + public List<String> getCrossProfileCalendarPackages(ComponentName admin) { + return Collections.emptyList(); + } + + @Override + public boolean isPackageAllowedToAccessCalendarForUser(String packageName, + int userHandle) { + return false; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 7751b4a44b88..6fbb850e4e40 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -74,8 +74,10 @@ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent .PROVISIONING_ENTRY_POINT_ADB; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker .STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; -import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER; -import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER; +import static com.android.server.devicepolicy.TransferOwnershipMetadataManager + .ADMIN_TYPE_DEVICE_OWNER; +import static com.android.server.devicepolicy.TransferOwnershipMetadataManager + .ADMIN_TYPE_PROFILE_OWNER; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; @@ -908,8 +910,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final String TAG_IS_LOGOUT_ENABLED = "is_logout_enabled"; private static final String TAG_START_USER_SESSION_MESSAGE = "start_user_session_message"; private static final String TAG_END_USER_SESSION_MESSAGE = "end_user_session_message"; - private static final String TAG_METERED_DATA_DISABLED_PACKAGES - = "metered_data_disabled_packages"; + private static final String TAG_METERED_DATA_DISABLED_PACKAGES = + "metered_data_disabled_packages"; + private static final String TAG_CROSS_PROFILE_CALENDAR_PACKAGES = + "cross-profile-calendar-packages"; + private static final String TAG_PACKAGE = "package"; + DeviceAdminInfo info; @@ -1030,6 +1036,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { String startUserSessionMessage = null; String endUserSessionMessage = null; + // The whitelist of packages that can access cross profile calendar APIs. + final Set<String> mCrossProfileCalendarPackages = new ArraySet<>(); + ActiveAdmin(DeviceAdminInfo _info, boolean parent) { info = _info; isParent = parent; @@ -1299,6 +1308,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { out.text(endUserSessionMessage); out.endTag(null, TAG_END_USER_SESSION_MESSAGE); } + if (!mCrossProfileCalendarPackages.isEmpty()) { + out.startTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES); + writeAttributeValuesToXml( + out, TAG_PACKAGE, mCrossProfileCalendarPackages); + out.endTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES); + } } void writePackageListToXml(XmlSerializer out, String outerTag, @@ -1491,6 +1506,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } else { Log.w(LOG_TAG, "Missing text when loading end session message"); } + } else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES.equals(tag)) { + readAttributeValues( + parser, TAG_PACKAGE, mCrossProfileCalendarPackages); } else { Slog.w(LOG_TAG, "Unknown admin tag: " + tag); XmlUtils.skipCurrentTag(parser); @@ -1706,6 +1724,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { pw.print(prefix); pw.println("parentAdmin:"); parentAdmin.dump(prefix + " ", pw); } + pw.print(prefix); pw.print("mCrossProfileCalendarPackages="); + pw.println(mCrossProfileCalendarPackages); } } @@ -13339,9 +13359,77 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private boolean isDeviceAB() { return "true".equalsIgnoreCase(android.os.SystemProperties .get(AB_DEVICE_KEY, "")); } + + @Override + public void addCrossProfileCalendarPackage(ComponentName who, String packageName) { + if (!mHasFeature) { + return; + } + Preconditions.checkNotNull(who, "ComponentName is null"); + Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty"); + + synchronized (getLockObject()) { + final ActiveAdmin admin = getActiveAdminForCallerLocked( + who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + if (admin.mCrossProfileCalendarPackages.add(packageName)) { + saveSettingsLocked(mInjector.userHandleGetCallingUserId()); + } + } + } + + @Override + public boolean removeCrossProfileCalendarPackage(ComponentName who, String packageName) { + if (!mHasFeature) { + return false; + } + Preconditions.checkNotNull(who, "ComponentName is null"); + Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty"); + + boolean isRemoved = false; + synchronized (getLockObject()) { + final ActiveAdmin admin = getActiveAdminForCallerLocked( + who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + isRemoved = admin.mCrossProfileCalendarPackages.remove(packageName); + if (isRemoved) { + saveSettingsLocked(mInjector.userHandleGetCallingUserId()); + } + } + return isRemoved; + } + + @Override + public List<String> getCrossProfileCalendarPackages(ComponentName who) { + if (!mHasFeature) { + return Collections.emptyList(); + } + Preconditions.checkNotNull(who, "ComponentName is null"); + + synchronized (getLockObject()) { + final ActiveAdmin admin = getActiveAdminForCallerLocked( + who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + return new ArrayList<String>(admin.mCrossProfileCalendarPackages); + } + } + + @Override + public boolean isPackageAllowedToAccessCalendarForUser(String packageName, + int userHandle) { + if (!mHasFeature) { + return false; + } + Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty"); + + enforceCrossUsersPermission(userHandle); + synchronized (getLockObject()) { + final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle); + if (admin != null && admin.mCrossProfileCalendarPackages != null) { + return admin.mCrossProfileCalendarPackages.contains(packageName); + } + } + return false; + } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index d6e62087f277..56f7cff565af 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -177,7 +177,7 @@ public final class SystemServer { * them from the build system somehow. */ private static final String BACKUP_MANAGER_SERVICE_CLASS = - "com.android.server.backup.GlobalBackupManagerService$Lifecycle"; + "com.android.server.backup.BackupManagerService$Lifecycle"; private static final String APPWIDGET_SERVICE_CLASS = "com.android.server.appwidget.AppWidgetService"; private static final String VOICE_RECOGNITION_MANAGER_SERVICE_CLASS = diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java index dc55179bdc9e..c9b9f3e6bd48 100644 --- a/services/print/java/com/android/server/print/PrintManagerService.java +++ b/services/print/java/com/android/server/print/PrintManagerService.java @@ -146,13 +146,14 @@ public final class PrintManagerService extends SystemService { final long identity = Binder.clearCallingIdentity(); try { disabledMessage = dpmi.getPrintingDisabledReasonForUser(callingUserId); + + if (disabledMessage != null) { + Toast.makeText(mContext, Looper.getMainLooper(), disabledMessage, + Toast.LENGTH_LONG).show(); + } } finally { Binder.restoreCallingIdentity(identity); } - if (disabledMessage != null) { - Toast.makeText(mContext, Looper.getMainLooper(), disabledMessage, - Toast.LENGTH_LONG).show(); - } try { adapter.start(); } catch (RemoteException re) { diff --git a/services/robotests/src/com/android/server/backup/GlobalBackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java index 3108c804b6f4..ba4caf44024b 100644 --- a/services/robotests/src/com/android/server/backup/GlobalBackupManagerServiceTest.java +++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java @@ -49,43 +49,43 @@ import java.io.File; import java.io.FileDescriptor; import java.io.PrintWriter; -/** Tests for the user-aware backup/restore system service {@link GlobalBackupManagerService}. */ +/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */ @RunWith(RobolectricTestRunner.class) @Presubmit -public class GlobalBackupManagerServiceTest { +public class BackupManagerServiceTest { private static final String TEST_PACKAGE = "package"; private static final String TEST_TRANSPORT = "transport"; @Mock private UserBackupManagerService mUserBackupManagerService; @Mock private TransportManager mTransportManager; - private GlobalBackupManagerService mGlobalBackupManagerService; + private BackupManagerService mBackupManagerService; private Context mContext; - /** Initialize {@link GlobalBackupManagerService}. */ + /** Initialize {@link BackupManagerService}. */ @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); Application application = RuntimeEnvironment.application; mContext = application; - mGlobalBackupManagerService = - new GlobalBackupManagerService( + mBackupManagerService = + new BackupManagerService( application, new Trampoline(application), BackupManagerServiceTestUtils.startBackupThread(null), new File(application.getCacheDir(), "base_state"), new File(application.getCacheDir(), "data"), mTransportManager); - mGlobalBackupManagerService.setUserBackupManagerService(mUserBackupManagerService); + mBackupManagerService.setUserBackupManagerService(mUserBackupManagerService); } /** - * Test verifying that {@link GlobalBackupManagerService#MORE_DEBUG} is set to {@code false}. + * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. * This is specifically to prevent overloading the logs in production. */ @Test public void testMoreDebug_isFalse() throws Exception { - boolean moreDebug = GlobalBackupManagerService.MORE_DEBUG; + boolean moreDebug = BackupManagerService.MORE_DEBUG; assertThat(moreDebug).isFalse(); } @@ -101,7 +101,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testDataChanged_callsDataChangedForUser() throws Exception { - mGlobalBackupManagerService.dataChanged(TEST_PACKAGE); + mBackupManagerService.dataChanged(TEST_PACKAGE); verify(mUserBackupManagerService).dataChanged(TEST_PACKAGE); } @@ -111,7 +111,7 @@ public class GlobalBackupManagerServiceTest { public void testAgentConnected_callsAgentConnectedForUser() throws Exception { IBinder agentBinder = mock(IBinder.class); - mGlobalBackupManagerService.agentConnected(TEST_PACKAGE, agentBinder); + mBackupManagerService.agentConnected(TEST_PACKAGE, agentBinder); verify(mUserBackupManagerService).agentConnected(TEST_PACKAGE, agentBinder); } @@ -119,7 +119,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testAgentDisconnected_callsAgentDisconnectedForUser() throws Exception { - mGlobalBackupManagerService.agentDisconnected(TEST_PACKAGE); + mBackupManagerService.agentDisconnected(TEST_PACKAGE); verify(mUserBackupManagerService).agentDisconnected(TEST_PACKAGE); } @@ -127,7 +127,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testOpComplete_callsOpCompleteForUser() throws Exception { - mGlobalBackupManagerService.opComplete(/* token */ 0, /* result */ 0L); + mBackupManagerService.opComplete(/* token */ 0, /* result */ 0L); verify(mUserBackupManagerService).opComplete(/* token */ 0, /* result */ 0L); } @@ -141,7 +141,7 @@ public class GlobalBackupManagerServiceTest { public void testInitializeTransports_callsInitializeTransportsForUser() throws Exception { String[] transports = {TEST_TRANSPORT}; - mGlobalBackupManagerService.initializeTransports(transports, /* observer */ null); + mBackupManagerService.initializeTransports(transports, /* observer */ null); verify(mUserBackupManagerService).initializeTransports(transports, /* observer */ null); } @@ -149,7 +149,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testClearBackupData_callsClearBackupDataForUser() throws Exception { - mGlobalBackupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE); + mBackupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE); verify(mUserBackupManagerService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE); } @@ -157,7 +157,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testGetCurrentTransport_callsGetCurrentTransportForUser() throws Exception { - mGlobalBackupManagerService.getCurrentTransport(); + mBackupManagerService.getCurrentTransport(); verify(mUserBackupManagerService).getCurrentTransport(); } @@ -166,7 +166,7 @@ public class GlobalBackupManagerServiceTest { @Test public void testGetCurrentTransportComponent_callsGetCurrentTransportComponentForUser() throws Exception { - mGlobalBackupManagerService.getCurrentTransportComponent(); + mBackupManagerService.getCurrentTransportComponent(); verify(mUserBackupManagerService).getCurrentTransportComponent(); } @@ -174,7 +174,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testListAllTransports_callsListAllTransportsForUser() throws Exception { - mGlobalBackupManagerService.listAllTransports(); + mBackupManagerService.listAllTransports(); verify(mUserBackupManagerService).listAllTransports(); } @@ -183,7 +183,7 @@ public class GlobalBackupManagerServiceTest { @Test public void testListAllTransportComponents_callsListAllTransportComponentsForUser() throws Exception { - mGlobalBackupManagerService.listAllTransportComponents(); + mBackupManagerService.listAllTransportComponents(); verify(mUserBackupManagerService).listAllTransportComponents(); } @@ -191,7 +191,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testGetTransportWhitelist_callsGetTransportWhitelistForUser() throws Exception { - mGlobalBackupManagerService.getTransportWhitelist(); + mBackupManagerService.getTransportWhitelist(); verify(mUserBackupManagerService).getTransportWhitelist(); } @@ -204,7 +204,7 @@ public class GlobalBackupManagerServiceTest { Intent configurationIntent = new Intent(); Intent dataManagementIntent = new Intent(); - mGlobalBackupManagerService.updateTransportAttributes( + mBackupManagerService.updateTransportAttributes( transport.getTransportComponent(), transport.transportName, configurationIntent, @@ -225,7 +225,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testSelectBackupTransport_callsSelectBackupTransportForUser() throws Exception { - mGlobalBackupManagerService.selectBackupTransport(TEST_TRANSPORT); + mBackupManagerService.selectBackupTransport(TEST_TRANSPORT); verify(mUserBackupManagerService).selectBackupTransport(TEST_TRANSPORT); } @@ -236,7 +236,7 @@ public class GlobalBackupManagerServiceTest { TransportData transport = backupTransport(); ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class); - mGlobalBackupManagerService.selectBackupTransportAsync( + mBackupManagerService.selectBackupTransportAsync( transport.getTransportComponent(), callback); verify(mUserBackupManagerService) @@ -246,7 +246,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testGetConfigurationIntent_callsGetConfigurationIntentForUser() throws Exception { - mGlobalBackupManagerService.getConfigurationIntent(TEST_TRANSPORT); + mBackupManagerService.getConfigurationIntent(TEST_TRANSPORT); verify(mUserBackupManagerService).getConfigurationIntent(TEST_TRANSPORT); } @@ -254,7 +254,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testGetDestinationString_callsGetDestinationStringForUser() throws Exception { - mGlobalBackupManagerService.getDestinationString(TEST_TRANSPORT); + mBackupManagerService.getDestinationString(TEST_TRANSPORT); verify(mUserBackupManagerService).getDestinationString(TEST_TRANSPORT); } @@ -262,7 +262,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testGetDataManagementIntent_callsGetDataManagementIntentForUser() throws Exception { - mGlobalBackupManagerService.getDataManagementIntent(TEST_TRANSPORT); + mBackupManagerService.getDataManagementIntent(TEST_TRANSPORT); verify(mUserBackupManagerService).getDataManagementIntent(TEST_TRANSPORT); } @@ -270,7 +270,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testGetDataManagementLabel_callsGetDataManagementLabelForUser() throws Exception { - mGlobalBackupManagerService.getDataManagementLabel(TEST_TRANSPORT); + mBackupManagerService.getDataManagementLabel(TEST_TRANSPORT); verify(mUserBackupManagerService).getDataManagementLabel(TEST_TRANSPORT); } @@ -282,7 +282,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void setBackupEnabled_callsSetBackupEnabledForUser() throws Exception { - mGlobalBackupManagerService.setBackupEnabled(true); + mBackupManagerService.setBackupEnabled(true); verify(mUserBackupManagerService).setBackupEnabled(true); } @@ -290,7 +290,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void setAutoRestore_callsSetAutoRestoreForUser() throws Exception { - mGlobalBackupManagerService.setAutoRestore(true); + mBackupManagerService.setAutoRestore(true); verify(mUserBackupManagerService).setAutoRestore(true); } @@ -298,7 +298,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testSetBackupProvisioned_callsSetBackupProvisionedForUser() throws Exception { - mGlobalBackupManagerService.setBackupProvisioned(true); + mBackupManagerService.setBackupProvisioned(true); verify(mUserBackupManagerService).setBackupProvisioned(true); } @@ -306,7 +306,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testIsBackupEnabled_callsIsBackupEnabledForUser() throws Exception { - mGlobalBackupManagerService.isBackupEnabled(); + mBackupManagerService.isBackupEnabled(); verify(mUserBackupManagerService).isBackupEnabled(); } @@ -318,7 +318,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testIsAppEligibleForBackup_callsIsAppEligibleForBackupForUser() throws Exception { - mGlobalBackupManagerService.isAppEligibleForBackup(TEST_PACKAGE); + mBackupManagerService.isAppEligibleForBackup(TEST_PACKAGE); verify(mUserBackupManagerService).isAppEligibleForBackup(TEST_PACKAGE); } @@ -329,7 +329,7 @@ public class GlobalBackupManagerServiceTest { throws Exception { String[] packages = {TEST_PACKAGE}; - mGlobalBackupManagerService.filterAppsEligibleForBackup(packages); + mBackupManagerService.filterAppsEligibleForBackup(packages); verify(mUserBackupManagerService).filterAppsEligibleForBackup(packages); } @@ -337,7 +337,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testBackupNow_callsBackupNowForUser() throws Exception { - mGlobalBackupManagerService.backupNow(); + mBackupManagerService.backupNow(); verify(mUserBackupManagerService).backupNow(); } @@ -349,7 +349,7 @@ public class GlobalBackupManagerServiceTest { IBackupObserver observer = mock(IBackupObserver.class); IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class); - mGlobalBackupManagerService.requestBackup(packages, observer, monitor, /* flags */ 0); + mBackupManagerService.requestBackup(packages, observer, monitor, /* flags */ 0); verify(mUserBackupManagerService).requestBackup(packages, observer, monitor, /* flags */ 0); } @@ -357,7 +357,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testCancelBackups_callsCancelBackupsForUser() throws Exception { - mGlobalBackupManagerService.cancelBackups(); + mBackupManagerService.cancelBackups(); verify(mUserBackupManagerService).cancelBackups(); } @@ -367,7 +367,7 @@ public class GlobalBackupManagerServiceTest { public void testBeginFullBackup_callsBeginFullBackupForUser() throws Exception { FullBackupJob job = new FullBackupJob(); - mGlobalBackupManagerService.beginFullBackup(job); + mBackupManagerService.beginFullBackup(job); verify(mUserBackupManagerService).beginFullBackup(job); } @@ -375,7 +375,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testEndFullBackup_callsEndFullBackupForUser() throws Exception { - mGlobalBackupManagerService.endFullBackup(); + mBackupManagerService.endFullBackup(); verify(mUserBackupManagerService).endFullBackup(); } @@ -385,7 +385,7 @@ public class GlobalBackupManagerServiceTest { public void testFullTransportBackup_callsFullTransportBackupForUser() throws Exception { String[] packages = {TEST_PACKAGE}; - mGlobalBackupManagerService.fullTransportBackup(packages); + mBackupManagerService.fullTransportBackup(packages); verify(mUserBackupManagerService).fullTransportBackup(packages); } @@ -397,7 +397,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testRestoreAtInstall_callsRestoreAtInstallForUser() throws Exception { - mGlobalBackupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0); + mBackupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0); verify(mUserBackupManagerService).restoreAtInstall(TEST_PACKAGE, /* token */ 0); } @@ -405,7 +405,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testBeginRestoreSession_callsBeginRestoreSessionForUser() throws Exception { - mGlobalBackupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); + mBackupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); verify(mUserBackupManagerService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); } @@ -414,7 +414,7 @@ public class GlobalBackupManagerServiceTest { @Test public void testGetAvailableRestoreToken_callsGetAvailableRestoreTokenForUser() throws Exception { - mGlobalBackupManagerService.getAvailableRestoreToken(TEST_PACKAGE); + mBackupManagerService.getAvailableRestoreToken(TEST_PACKAGE); verify(mUserBackupManagerService).getAvailableRestoreToken(TEST_PACKAGE); } @@ -426,7 +426,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testSetBackupPassword_callsSetBackupPasswordForUser() throws Exception { - mGlobalBackupManagerService.setBackupPassword("currentPassword", "newPassword"); + mBackupManagerService.setBackupPassword("currentPassword", "newPassword"); verify(mUserBackupManagerService).setBackupPassword("currentPassword", "newPassword"); } @@ -434,7 +434,7 @@ public class GlobalBackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testHasBackupPassword_callsHasBackupPasswordForUser() throws Exception { - mGlobalBackupManagerService.hasBackupPassword(); + mBackupManagerService.hasBackupPassword(); verify(mUserBackupManagerService).hasBackupPassword(); } @@ -448,7 +448,7 @@ public class GlobalBackupManagerServiceTest { ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE); String[] packages = {TEST_PACKAGE}; - mGlobalBackupManagerService.adbBackup( + mBackupManagerService.adbBackup( parcelFileDescriptor, /* includeApks */ true, /* includeObbs */ true, @@ -482,7 +482,7 @@ public class GlobalBackupManagerServiceTest { ParcelFileDescriptor parcelFileDescriptor = ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE); - mGlobalBackupManagerService.adbRestore(parcelFileDescriptor); + mBackupManagerService.adbRestore(parcelFileDescriptor); verify(mUserBackupManagerService).adbRestore(parcelFileDescriptor); } @@ -493,7 +493,7 @@ public class GlobalBackupManagerServiceTest { throws Exception { IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class); - mGlobalBackupManagerService.acknowledgeAdbBackupOrRestore( + mBackupManagerService.acknowledgeAdbBackupOrRestore( /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer); verify(mUserBackupManagerService) @@ -518,7 +518,7 @@ public class GlobalBackupManagerServiceTest { PrintWriter printWriter = new PrintWriter(testFile); String[] args = {"1", "2"}; - mGlobalBackupManagerService.dump(fileDescriptor, printWriter, args); + mBackupManagerService.dump(fileDescriptor, printWriter, args); verify(mUserBackupManagerService).dump(fileDescriptor, printWriter, args); } diff --git a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java index 8ec0759e52db..a14cc51a3ab6 100644 --- a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java +++ b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java @@ -44,7 +44,7 @@ import android.platform.test.annotations.Presubmit; import android.util.Log; import com.android.internal.backup.IBackupTransport; -import com.android.server.backup.GlobalBackupManagerService; +import com.android.server.backup.BackupManagerService; import com.android.server.backup.TransportManager; import com.android.server.backup.UserBackupManagerService; import com.android.server.backup.testing.TransportData; @@ -212,7 +212,7 @@ public class PerformInitializeTaskTest { performInitializeTask.run(); assertLogcatContains( - GlobalBackupManagerService.TAG, + BackupManagerService.TAG, log -> log.msg.contains("finishBackup()") && log.type >= Log.ERROR); } @@ -225,7 +225,7 @@ public class PerformInitializeTaskTest { performInitializeTask.run(); assertLogcatContains( - GlobalBackupManagerService.TAG, + BackupManagerService.TAG, log -> log.msg.contains("initializeDevice()") && log.type >= Log.ERROR); } diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index fa17b6125983..746c4530a5e1 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -143,9 +143,6 @@ <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2" /> <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3" /> - <activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity" - android:showWhenLocked="true"/> - <activity android:name="com.android.server.pm.ShortcutTestActivity" android:enabled="true" android:exported="true" /> @@ -206,12 +203,6 @@ </intent-filter> </activity-alias> - <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityA" /> - <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityB" /> - <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" /> - <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskChangeCallbacks" /> - <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskDescriptionChange" /> - <receiver android:name="com.android.server.appwidget.DummyAppWidget"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> diff --git a/services/tests/servicestests/res/raw/input_port_associations.xml b/services/tests/servicestests/res/raw/input_port_associations.xml new file mode 100644 index 000000000000..b10d541f942c --- /dev/null +++ b/services/tests/servicestests/res/raw/input_port_associations.xml @@ -0,0 +1,4 @@ +<ports> + <port display="0" input="USB1" /> + <port display="1" input="USB2" /> +</ports>
\ No newline at end of file diff --git a/services/tests/servicestests/res/raw/input_port_associations_bad_displayport.xml b/services/tests/servicestests/res/raw/input_port_associations_bad_displayport.xml new file mode 100644 index 000000000000..8eeb1f58ef9e --- /dev/null +++ b/services/tests/servicestests/res/raw/input_port_associations_bad_displayport.xml @@ -0,0 +1,3 @@ +<ports> + <port display="a" input="USB1" /> +</ports>
\ No newline at end of file diff --git a/services/tests/servicestests/res/raw/input_port_associations_bad_xml.xml b/services/tests/servicestests/res/raw/input_port_associations_bad_xml.xml new file mode 100644 index 000000000000..cf6e12486239 --- /dev/null +++ b/services/tests/servicestests/res/raw/input_port_associations_bad_xml.xml @@ -0,0 +1,3 @@ +<ports> + <port Garbage data inside xml> +</ports>
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java index c99e2a850679..7c002995a769 100644 --- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java @@ -83,7 +83,7 @@ public class TrampolineTest { }; private final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1; - @Mock private GlobalBackupManagerService mBackupManagerServiceMock; + @Mock private BackupManagerService mBackupManagerServiceMock; @Mock private Context mContextMock; @Mock private File mSuppressFileMock; @Mock private File mSuppressFileParentMock; @@ -864,7 +864,7 @@ public class TrampolineTest { static boolean sBackupDisabled = false; static File sSuppressFile = null; static int sCallingUid = -1; - static GlobalBackupManagerService sBackupManagerServiceMock = null; + static BackupManagerService sBackupManagerServiceMock = null; private int mCreateServiceCallsCount = 0; TrampolineTestable(Context context) { @@ -887,7 +887,7 @@ public class TrampolineTest { } @Override - protected GlobalBackupManagerService createBackupManagerService() { + protected BackupManagerService createBackupManagerService() { mCreateServiceCallsCount++; return sBackupManagerServiceMock; } diff --git a/services/tests/servicestests/src/com/android/server/input/ConfigurationProcessorTest.java b/services/tests/servicestests/src/com/android/server/input/ConfigurationProcessorTest.java new file mode 100644 index 000000000000..636aa375a84c --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/input/ConfigurationProcessorTest.java @@ -0,0 +1,93 @@ +/* + * 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.input; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.content.Context; +import android.util.Pair; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.InputStream; +import java.util.List; + +/** + * Build/Install/Run: + * atest ConfigurationProcessorTest + */ +@RunWith(AndroidJUnit4.class) +public class ConfigurationProcessorTest { + + private Context mContext; + + @Before + public void setUp() throws Exception { + mContext = InstrumentationRegistry.getContext(); + } + + @Test + public void testGetInputPortAssociations() { + final int res = com.android.frameworks.servicestests.R.raw.input_port_associations; + InputStream xml = mContext.getResources().openRawResource(res); + List<Pair<String, String>> associations = null; + try { + associations = ConfigurationProcessor.processInputPortAssociations(xml); + } catch (Exception e) { + fail("Could not process xml file for input associations"); + } + assertNotNull(associations); + assertEquals(2, associations.size()); + assertTrue(associations.contains(Pair.create("USB1", "0"))); + assertTrue(associations.contains(Pair.create("USB2", "1"))); + } + + @Test + public void testGetInputPortAssociationsBadDisplayport() { + final int res = + com.android.frameworks.servicestests.R.raw.input_port_associations_bad_displayport; + InputStream xml = mContext.getResources().openRawResource(res); + List<Pair<String, String>> associations = null; + try { + associations = ConfigurationProcessor.processInputPortAssociations(xml); + } catch (Exception e) { + fail("Could not process xml file for input associations"); + } + assertNotNull(associations); + assertEquals(0, associations.size()); + } + + @Test + public void testGetInputPortAssociationsEmptyConfig() { + final int res = com.android.frameworks.servicestests.R.raw.input_port_associations_bad_xml; + InputStream xml = mContext.getResources().openRawResource(res); + try { + ConfigurationProcessor.processInputPortAssociations(xml); + fail("Parsing should fail, because xml contains bad data"); + } catch (Exception e) { + // This is expected + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SP800DeriveTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SP800DeriveTests.java new file mode 100644 index 000000000000..fc2dcb9cc83b --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/locksettings/SP800DeriveTests.java @@ -0,0 +1,40 @@ +/* + * 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.locksettings; + +import android.test.AndroidTestCase; + +import com.android.internal.util.HexDump; + +public class SP800DeriveTests extends AndroidTestCase { + public void testFixedInput() throws Exception { + // CAVP: https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/key-derivation + byte[] keyBytes = HexDump.hexStringToByteArray( + "e204d6d466aad507ffaf6d6dab0a5b26" + + "152c9e21e764370464e360c8fbc765c6"); + SP800Derive sk = new SP800Derive(keyBytes); + byte[] fixedInput = HexDump.hexStringToByteArray( + "7b03b98d9f94b899e591f3ef264b71b1" + + "93fba7043c7e953cde23bc5384bc1a62" + + "93580115fae3495fd845dadbd02bd645" + + "5cf48d0f62b33e62364a3a80"); + byte[] res = sk.fixedInput(fixedInput); + assertEquals(( + "770dfab6a6a4a4bee0257ff335213f78" + + "d8287b4fd537d5c1fffa956910e7c779").toUpperCase(), HexDump.toHexString(res)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index 113ee2df768e..99b827c11853 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.net; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.TYPE_WIFI; @@ -72,7 +72,6 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; @@ -142,12 +141,14 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.telephony.PhoneConstants; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent; -import com.android.server.net.NetworkPolicyManagerInternal; -import com.android.server.net.NetworkPolicyManagerService; -import com.android.server.net.NetworkStatsManagerInternal; +import com.android.server.DeviceIdleController; +import com.android.server.LocalServices; import com.google.common.util.concurrent.AbstractFuture; +import libcore.io.IoUtils; +import libcore.io.Streams; + import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -162,9 +163,6 @@ import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; -import libcore.io.IoUtils; -import libcore.io.Streams; - import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; @@ -195,15 +193,6 @@ import java.util.stream.Collectors; /** * Tests for {@link NetworkPolicyManagerService}. - * - * <p>Typical usage: - * - * <pre><code> - m -j32 FrameworksServicesTests && adb install -r -g \ - ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && \ - adb shell am instrument -e class "com.android.server.NetworkPolicyManagerServiceTest" -w \ - "com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner" - * </code></pre> */ @RunWith(AndroidJUnit4.class) @MediumTest @@ -376,7 +365,7 @@ public class NetworkPolicyManagerServiceTest { return null; } }).when(mActivityManager).registerUidObserver(any(), anyInt(), - eq(NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE), isNull(String.class)); + eq(NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE), any(String.class)); mFutureIntent = newRestrictBackgroundChangedFuture(); mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager, @@ -425,7 +414,7 @@ public class NetworkPolicyManagerServiceTest { // catch INetworkManagementEventObserver during systemReady() final ArgumentCaptor<INetworkManagementEventObserver> networkObserver = - ArgumentCaptor.forClass(INetworkManagementEventObserver.class); + ArgumentCaptor.forClass(INetworkManagementEventObserver.class); verify(mNetworkManager).registerObserver(networkObserver.capture()); mNetworkObserver = networkObserver.getValue(); @@ -843,6 +832,18 @@ public class NetworkPolicyManagerServiceTest { assertTrue(mService.isUidForeground(UID_B)); } + @Test + public void testAppIdleTempWhitelisting() throws Exception { + mService.setAppIdleWhitelist(UID_A, true); + mService.setAppIdleWhitelist(UID_B, false); + int[] whitelistedIds = mService.getAppIdleWhitelist(); + assertTrue(Arrays.binarySearch(whitelistedIds, UID_A) >= 0); + assertTrue(Arrays.binarySearch(whitelistedIds, UID_B) < 0); + assertFalse(mService.isUidIdle(UID_A)); + // Can't currently guarantee UID_B's app idle state. + // TODO: expand with multiple app idle states. + } + private static long computeLastCycleBoundary(long currentTime, NetworkPolicy policy) { RecurrenceRule.sClock = Clock.fixed(Instant.ofEpochMilli(currentTime), ZoneId.systemDefault()); @@ -1770,7 +1771,7 @@ public class NetworkPolicyManagerServiceTest { } private static NetworkPolicy buildFakeMobilePolicy(int cycleDay, long warningBytes, - long limitBytes, boolean inferred){ + long limitBytes, boolean inferred) { final NetworkTemplate template = buildTemplateMobileAll(FAKE_SUBSCRIBER_ID); return new NetworkPolicy(template, cycleDay, new Time().timezone, warningBytes, limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, inferred); @@ -1868,7 +1869,7 @@ public class NetworkPolicyManagerServiceTest { } private static void assertNotificationType(int expected, String actualTag) { - assertEquals("notification type mismatch for '" + actualTag +"'", + assertEquals("notification type mismatch for '" + actualTag + "'", Integer.toString(expected), actualTag.substring(actualTag.lastIndexOf(':') + 1)); } @@ -1902,7 +1903,8 @@ public class NetworkPolicyManagerServiceTest { final String action = ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED; final Intent intent = future.get(5, TimeUnit.SECONDS); assertNotNull("Didn't get a " + action + "intent in 5 seconds"); - assertEquals("Wrong package on " + action + " intent", expectedPackage, intent.getPackage()); + assertEquals("Wrong package on " + action + " intent", + expectedPackage, intent.getPackage()); } // TODO: replace by Truth, Hamcrest, or a similar tool. @@ -1923,7 +1925,7 @@ public class NetworkPolicyManagerServiceTest { } if (errors.length() > 0) { fail("assertContainsInAnyOrder(expected=" + Arrays.toString(expected) - + ", actual=" + Arrays.toString(actual) +") failed: \n" + errors); + + ", actual=" + Arrays.toString(actual) + ") failed: \n" + errors); } } @@ -1986,7 +1988,7 @@ public class NetworkPolicyManagerServiceTest { @Override public Void answer(InvocationOnMock invocation) throws Throwable { - Log.d(TAG,"counting down on answer: " + invocation); + Log.d(TAG, "counting down on answer: " + invocation); latch.countDown(); return null; } @@ -2024,8 +2026,8 @@ public class NetworkPolicyManagerServiceTest { final String assetPath = NETPOLICY_DIR + "/" + mNetpolicyXml; final File netConfigFile = new File(mPolicyDir, "netpolicy.xml"); Log.d(TAG, "Creating " + netConfigFile + " from asset " + assetPath); - try (final InputStream in = context.getResources().getAssets().open(assetPath); - final OutputStream out = new FileOutputStream(netConfigFile)) { + try (InputStream in = context.getResources().getAssets().open(assetPath); + OutputStream out = new FileOutputStream(netConfigFile)) { Streams.copy(in, out); } } @@ -2037,9 +2039,7 @@ public class NetworkPolicyManagerServiceTest { @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface NetPolicyXml { - - public String value() default ""; - + String value() default ""; } /** diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index d7988656c9de..ce59e6ec9760 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -31,6 +31,7 @@ import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; import android.content.pm.SharedLibraryInfo; import android.content.pm.Signature; +import android.content.pm.UsesPermissionInfo; import android.os.Bundle; import android.os.Parcel; import android.platform.test.annotations.Presubmit; @@ -464,6 +465,7 @@ public class PackageParserTest { pkg.services.add(new PackageParser.Service(dummy, new ServiceInfo())); pkg.instrumentation.add(new PackageParser.Instrumentation(dummy, new InstrumentationInfo())); pkg.requestedPermissions.add("foo7"); + pkg.usesPermissionInfos.add(new UsesPermissionInfo("foo7")); pkg.implicitPermissions.add("foo25"); pkg.protectedBroadcasts = new ArrayList<>(); diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java deleted file mode 100644 index 415b5d93d90e..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.wm; - -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.content.res.Configuration.EMPTY; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.SmallTest; - -import com.android.server.wm.WindowTestUtils.TestTaskWindowContainerController; - -import org.junit.Test; - -/** - * Test class for {@link AppWindowContainerController}. - * - * atest FrameworksServicesTests:AppWindowContainerControllerTests - */ -@FlakyTest(bugId = 74078662) -@SmallTest -@Presubmit -public class AppWindowContainerControllerTests extends WindowTestsBase { - - private final String mPackageName = getInstrumentation().getTargetContext().getPackageName(); - - @Test - public void testRemoveContainer() { - final WindowTestUtils.TestAppWindowContainerController controller = - createAppWindowController(); - - // Assert token was added to display. - assertNotNull(mDisplayContent.getWindowToken(controller.mToken.asBinder())); - // Assert that the container was created and linked. - assertNotNull(controller.mContainer); - - controller.removeContainer(mDisplayContent.getDisplayId()); - - // Assert token was remove from display. - assertNull(mDisplayContent.getWindowToken(controller.mToken.asBinder())); - // Assert that the container was removed. - assertNull(controller.mContainer); - } - - @Test - public void testSetOrientation() { - final WindowTestUtils.TestAppWindowContainerController controller = - createAppWindowController(); - - // Assert orientation is unspecified to start. - assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation()); - - controller.setOrientation(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getDisplayId(), - EMPTY /* displayConfig */, false /* freezeScreenIfNeeded */); - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, controller.getOrientation()); - - controller.removeContainer(mDisplayContent.getDisplayId()); - // Assert orientation is unspecified to after container is removed. - assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation()); - - // Reset display frozen state - mWm.mDisplayFrozen = false; - } - - private void assertHasStartingWindow(AppWindowToken atoken) { - assertNotNull(atoken.startingSurface); - assertNotNull(atoken.startingData); - assertNotNull(atoken.startingWindow); - } - - private void assertNoStartingWindow(AppWindowToken atoken) { - assertNull(atoken.startingSurface); - assertNull(atoken.startingWindow); - assertNull(atoken.startingData); - atoken.forAllWindows(windowState -> { - assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING); - }, true); - } - - @Test - public void testCreateRemoveStartingWindow() { - final WindowTestUtils.TestAppWindowContainerController controller = - createAppWindowController(); - controller.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); - waitUntilHandlersIdle(); - final AppWindowToken atoken = controller.getAppWindowToken(mDisplayContent); - assertHasStartingWindow(atoken); - controller.removeStartingWindow(); - waitUntilHandlersIdle(); - assertNoStartingWindow(atoken); - } - - @Test - public void testAddRemoveRace() { - // There was once a race condition between adding and removing starting windows - for (int i = 0; i < 1000; i++) { - final WindowTestUtils.TestAppWindowContainerController controller = - createAppWindowController(); - controller.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); - controller.removeStartingWindow(); - waitUntilHandlersIdle(); - assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent)); - - controller.getAppWindowToken( - mDisplayContent).getParent().getParent().removeImmediately(); - } - } - - @Test - public void testTransferStartingWindow() { - final WindowTestUtils.TestAppWindowContainerController controller1 = - createAppWindowController(); - final WindowTestUtils.TestAppWindowContainerController controller2 = - createAppWindowController(); - controller1.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); - waitUntilHandlersIdle(); - controller2.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(), - true, true, false, true, false, false); - waitUntilHandlersIdle(); - assertNoStartingWindow(controller1.getAppWindowToken(mDisplayContent)); - assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent)); - } - - @Test - public void testTransferStartingWindowWhileCreating() { - final WindowTestUtils.TestAppWindowContainerController controller1 = - createAppWindowController(); - final WindowTestUtils.TestAppWindowContainerController controller2 = - createAppWindowController(); - ((TestWindowManagerPolicy) mWm.mPolicy).setRunnableWhenAddingSplashScreen(() -> { - - // Surprise, ...! Transfer window in the middle of the creation flow. - controller2.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(), - true, true, false, true, false, false); - }); - controller1.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); - waitUntilHandlersIdle(); - assertNoStartingWindow(controller1.getAppWindowToken(mDisplayContent)); - assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent)); - } - - @Test - public void testTryTransferStartingWindowFromHiddenAboveToken() { - - // Add two tasks on top of each other. - TestTaskWindowContainerController taskController = - new WindowTestUtils.TestTaskWindowContainerController(this); - final WindowTestUtils.TestAppWindowContainerController controllerTop = - createAppWindowController(taskController); - final WindowTestUtils.TestAppWindowContainerController controllerBottom = - createAppWindowController(taskController); - - // Add a starting window. - controllerTop.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); - waitUntilHandlersIdle(); - - // Make the top one invisible, and try transfering the starting window from the top to the - // bottom one. - controllerTop.setVisibility(false, false); - controllerBottom.mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded(); - - // Assert that the bottom window now has the starting window. - assertNoStartingWindow(controllerTop.getAppWindowToken(mDisplayContent)); - assertHasStartingWindow(controllerBottom.getAppWindowToken(mDisplayContent)); - } - - @Test - public void testReparent() { - final StackWindowController stackController = - createStackControllerOnDisplay(mDisplayContent); - final WindowTestUtils.TestTaskWindowContainerController taskController1 = - new WindowTestUtils.TestTaskWindowContainerController(stackController); - final WindowTestUtils.TestAppWindowContainerController appWindowController1 = - createAppWindowController(taskController1); - final WindowTestUtils.TestTaskWindowContainerController taskController2 = - new WindowTestUtils.TestTaskWindowContainerController(stackController); - final WindowTestUtils.TestAppWindowContainerController appWindowController2 = - createAppWindowController(taskController2); - final WindowTestUtils.TestTaskWindowContainerController taskController3 = - new WindowTestUtils.TestTaskWindowContainerController(stackController); - - try { - appWindowController1.reparent(taskController1, 0); - fail("Should not be able to reparent to the same parent"); - } catch (IllegalArgumentException e) { - // Expected - } - - try { - taskController3.setContainer(null); - appWindowController1.reparent(taskController3, 0); - fail("Should not be able to reparent to a task that doesn't have a container"); - } catch (IllegalArgumentException e) { - // Expected - } - - // Reparent the app window and ensure that it is moved - appWindowController1.reparent(taskController2, 0); - assertEquals(taskController2.mContainer, appWindowController1.mContainer.getParent()); - assertEquals(0, ((WindowTestUtils.TestAppWindowToken) appWindowController1.mContainer) - .positionInParent()); - assertEquals(1, ((WindowTestUtils.TestAppWindowToken) appWindowController2.mContainer) - .positionInParent()); - } - - private WindowTestUtils.TestAppWindowContainerController createAppWindowController() { - return createAppWindowController( - new WindowTestUtils.TestTaskWindowContainerController(this)); - } - - private WindowTestUtils.TestAppWindowContainerController createAppWindowController( - WindowTestUtils.TestTaskWindowContainerController taskController) { - return new WindowTestUtils.TestAppWindowContainerController(taskController); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java deleted file mode 100644 index 60c045991d69..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static android.app.AppOpsManager.OP_NONE; -import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - -import static com.android.server.wm.WindowContainer.POSITION_TOP; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -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 android.app.ActivityManager; -import android.content.ComponentName; -import android.content.res.Configuration; -import android.graphics.Rect; -import android.os.Binder; -import android.os.IBinder; -import android.view.IApplicationToken; -import android.view.IWindow; -import android.view.SurfaceControl.Transaction; -import android.view.WindowManager; - -import org.mockito.invocation.InvocationOnMock; - -/** - * A collection of static functions that can be referenced by other test packages to provide access - * to WindowManager related test functionality. - */ -public class WindowTestUtils { - private static int sNextTaskId = 0; - - /** - * Creates a mock instance of {@link StackWindowController}. - */ - public static StackWindowController createMockStackWindowContainerController() { - StackWindowController controller = mock(StackWindowController.class); - controller.mContainer = mock(TestTaskStack.class); - - // many components rely on the {@link StackWindowController#adjustConfigurationForBounds} - // to properly set bounds values in the configuration. We must mimick those actions here. - doAnswer((InvocationOnMock invocationOnMock) -> { - final Configuration config = invocationOnMock.<Configuration>getArgument(7); - final Rect bounds = invocationOnMock.<Rect>getArgument(0); - config.windowConfiguration.setBounds(bounds); - return null; - }).when(controller).adjustConfigurationForBounds(any(), any(), any(), any(), - anyBoolean(), anyBoolean(), anyFloat(), any(), any(), anyInt()); - - return controller; - } - - /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ - public static Task createTaskInStack(WindowManagerService service, TaskStack stack, - int userId) { - synchronized (service.mGlobalLock) { - final Task newTask = new Task(sNextTaskId++, stack, userId, service, 0, false, - new ActivityManager.TaskDescription(), null); - stack.addTask(newTask, POSITION_TOP); - return newTask; - } - } - - /** - * An extension of {@link TestTaskStack}, which overrides package scoped methods that would not - * normally be mocked out. - */ - public static class TestTaskStack extends TaskStack { - TestTaskStack(WindowManagerService service, int stackId) { - super(service, stackId, null); - } - - @Override - void addTask(Task task, int position, boolean showForAllUsers, boolean moveParents) { - // Do nothing. - } - } - - static TestAppWindowToken createTestAppWindowToken(DisplayContent dc) { - synchronized (dc.mService.mGlobalLock) { - return new TestAppWindowToken(dc); - } - } - - /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */ - public static class TestAppWindowToken extends AppWindowToken { - boolean mOnTop = false; - private Transaction mPendingTransactionOverride; - - private TestAppWindowToken(DisplayContent dc) { - super(dc.mService, new IApplicationToken.Stub() { - public String getName() {return null;} - }, new ComponentName("", ""), false, dc, true /* fillsParent */); - } - - TestAppWindowToken(WindowManagerService service, IApplicationToken token, - ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc, - long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, - int targetSdk, int orientation, int rotationAnimationHint, int configChanges, - boolean launchTaskBehind, boolean alwaysFocusable, - AppWindowContainerController controller) { - super(service, token, activityComponent, voiceInteraction, dc, - inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, - orientation, rotationAnimationHint, configChanges, launchTaskBehind, - alwaysFocusable, controller); - } - - int getWindowsCount() { - return mChildren.size(); - } - - boolean hasWindow(WindowState w) { - return mChildren.contains(w); - } - - WindowState getFirstChild() { - return mChildren.peekFirst(); - } - - WindowState getLastChild() { - return mChildren.peekLast(); - } - - int positionInParent() { - return getParent().mChildren.indexOf(this); - } - - void setIsOnTop(boolean onTop) { - mOnTop = onTop; - } - - @Override - boolean isOnTop() { - return mOnTop; - } - - void setPendingTransaction(Transaction transaction) { - mPendingTransactionOverride = transaction; - } - - @Override - public Transaction getPendingTransaction() { - return mPendingTransactionOverride == null - ? super.getPendingTransaction() - : mPendingTransactionOverride; - } - } - - static TestWindowToken createTestWindowToken(int type, DisplayContent dc) { - return createTestWindowToken(type, dc, false /* persistOnEmpty */); - } - - static TestWindowToken createTestWindowToken(int type, DisplayContent dc, - boolean persistOnEmpty) { - synchronized (dc.mService.mGlobalLock) { - return new TestWindowToken(type, dc, persistOnEmpty); - } - } - - /* Used so we can gain access to some protected members of the {@link WindowToken} class */ - public static class TestWindowToken extends WindowToken { - - private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) { - super(dc.mService, mock(IBinder.class), type, persistOnEmpty, dc, - false /* ownerCanManageAppTokens */); - } - - int getWindowsCount() { - return mChildren.size(); - } - - boolean hasWindow(WindowState w) { - return mChildren.contains(w); - } - } - - /* Used so we can gain access to some protected members of the {@link Task} class */ - public static class TestTask extends Task { - boolean mShouldDeferRemoval = false; - boolean mOnDisplayChangedCalled = false; - private boolean mIsAnimating = false; - - TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, - int resizeMode, boolean supportsPictureInPicture, - TaskWindowContainerController controller) { - super(taskId, stack, userId, service, resizeMode, supportsPictureInPicture, - new ActivityManager.TaskDescription(), controller); - } - - boolean shouldDeferRemoval() { - return mShouldDeferRemoval; - } - - int positionInParent() { - return getParent().mChildren.indexOf(this); - } - - @Override - void onDisplayChanged(DisplayContent dc) { - super.onDisplayChanged(dc); - mOnDisplayChangedCalled = true; - } - - @Override - boolean isSelfAnimating() { - return mIsAnimating; - } - - void setLocalIsAnimating(boolean isAnimating) { - mIsAnimating = isAnimating; - } - } - - /** - * Used so we can gain access to some protected members of {@link TaskWindowContainerController} - * class. - */ - public static class TestTaskWindowContainerController extends TaskWindowContainerController { - - static final TaskWindowContainerListener NOP_LISTENER = new TaskWindowContainerListener() { - @Override - public void registerConfigurationChangeListener( - ConfigurationContainerListener listener) { - } - - @Override - public void unregisterConfigurationChangeListener( - ConfigurationContainerListener listener) { - } - - @Override - public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) { - } - - @Override - public void requestResize(Rect bounds, int resizeMode) { - } - }; - - TestTaskWindowContainerController(WindowTestsBase testsBase) { - this(testsBase.createStackControllerOnDisplay(testsBase.mDisplayContent)); - } - - TestTaskWindowContainerController(StackWindowController stackController) { - super(sNextTaskId++, NOP_LISTENER, stackController, 0 /* userId */, null /* bounds */, - RESIZE_MODE_UNRESIZEABLE, false /* supportsPictureInPicture */, true /* toTop*/, - true /* showForAllUsers */, new ActivityManager.TaskDescription(), - stackController.mService); - } - - @Override - TestTask createTask(int taskId, TaskStack stack, int userId, int resizeMode, - boolean supportsPictureInPicture, ActivityManager.TaskDescription taskDescription) { - return new TestTask(taskId, stack, userId, mService, resizeMode, - supportsPictureInPicture, this); - } - } - - public static class TestAppWindowContainerController extends AppWindowContainerController { - - final IApplicationToken mToken; - - TestAppWindowContainerController(TestTaskWindowContainerController taskController) { - this(taskController, new TestIApplicationToken()); - } - - TestAppWindowContainerController(TestTaskWindowContainerController taskController, - IApplicationToken token) { - super(taskController, token, new ComponentName("", "") /* activityComponent */, - null /* listener */, 0 /* index */, SCREEN_ORIENTATION_UNSPECIFIED, - true /* fullscreen */, true /* showForAllUsers */, 0 /* configChanges */, - false /* voiceInteraction */, false /* launchTaskBehind */, - false /* alwaysFocusable */, 0 /* targetSdkVersion */, - 0 /* rotationAnimationHint */, 0 /* inputDispatchingTimeoutNanos */, - taskController.mService); - mToken = token; - } - - @Override - AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token, - ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc, - long inputDispatchingTimeoutNanos, - boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, - int rotationAnimationHint, int configChanges, boolean launchTaskBehind, - boolean alwaysFocusable, AppWindowContainerController controller) { - return new TestAppWindowToken(service, token, activityComponent, voiceInteraction, dc, - inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, - orientation, - rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, - controller); - } - - AppWindowToken getAppWindowToken(DisplayContent dc) { - return (AppWindowToken) dc.getWindowToken(mToken.asBinder()); - } - } - - public static class TestIApplicationToken implements IApplicationToken { - - private final Binder mBinder = new Binder(); - @Override - public IBinder asBinder() { - return mBinder; - } - @Override - public String getName() { - return null; - } - } - - /** Used to track resize reports. */ - public static class TestWindowState extends WindowState { - boolean resizeReported; - - TestWindowState(WindowManagerService service, Session session, IWindow window, - WindowManager.LayoutParams attrs, WindowToken token) { - super(service, session, window, token, null, OP_NONE, 0, attrs, 0, 0, - false /* ownerCanAddInternalSystemWindow */); - } - - @Override - void reportResized() { - super.reportResized(); - resizeReported = true; - } - - @Override - public boolean isGoneForLayoutLw() { - return false; - } - - @Override - void updateResizingWindowIfNeeded() { - // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive - // the system that it can actually update the window. - boolean hadSurface = mHasSurface; - mHasSurface = true; - - super.updateResizingWindowIfNeeded(); - - mHasSurface = hadSurface; - } - } -} 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 fcd29e13a07b..d950360791d2 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -3711,4 +3711,23 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mAssistants).notifyAssistantSuggestedReplySent( eq(r.sbn), eq(reply), eq(generatedByAssistant)); } + + @Test + public void testOnNotificationActionClick() { + final int actionIndex = 2; + final Notification.Action action = + new Notification.Action.Builder(null, "text", null).build(); + final boolean generatedByAssistant = false; + + NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); + mService.addNotification(r); + + NotificationVisibility notificationVisibility = + NotificationVisibility.obtain(r.getKey(), 1, 2, true); + mService.mNotificationDelegate.onNotificationActionClick( + 10, 10, r.getKey(), actionIndex, action, notificationVisibility, + generatedByAssistant); + verify(mAssistants).notifyAssistantActionClicked( + eq(r.sbn), eq(actionIndex), eq(action), eq(generatedByAssistant)); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 38d8e3990e00..6c7ede3df4db 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -41,6 +41,7 @@ import static org.mockito.Mockito.when; import android.app.AppGlobals; import android.app.AppOpsManager; +import android.app.AutomaticZenRule; import android.app.NotificationManager; import android.app.NotificationManager.Policy; import android.content.ComponentName; @@ -1097,6 +1098,25 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertFalse(Objects.equals(defaultRuleName, ruleAfterUpdating.name)); // update name } + @Test + public void testAddAutomaticZenRule() { + AutomaticZenRule zenRule = new AutomaticZenRule("name", + new ComponentName("android", "ScheduleConditionProvider"), + ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), + NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); + String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test"); + + assertTrue(id != null); + ZenModeConfig.ZenRule ruleInConfig = mZenModeHelperSpy.mConfig.automaticRules.get(id); + assertTrue(ruleInConfig != null); + assertEquals(zenRule.isEnabled(), ruleInConfig.enabled); + assertEquals(zenRule.isModified(), ruleInConfig.modified); + assertEquals(zenRule.getConditionId(), ruleInConfig.conditionId); + assertEquals(NotificationManager.zenModeFromInterruptionFilter( + zenRule.getInterruptionFilter(), -1), ruleInConfig.zenMode); + assertEquals(zenRule.getName(), ruleInConfig.name); + } + private void setupZenConfig() { mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; mZenModeHelperSpy.mConfig.allowAlarms = false; diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index f128b4e2de2b..3f3b99692e9c 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -34,9 +34,13 @@ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> <uses-permission android:name="android.permission.READ_FRAME_BUFFER" /> + <uses-permission android:name="android.permission.WAKE_LOCK" /> + <uses-permission android:name="android.permission.REORDER_TASKS" /> <application android:debuggable="true" android:testOnly="true"> + <uses-library android:name="android.test.mock" android:required="true" /> + <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityA" /> <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityB" /> <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" /> diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java index cb2a8ec8a274..5bf3d2dabe24 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java @@ -62,8 +62,9 @@ public class ActivityDisplayTests extends ActivityTestsBase { @Test public void testLastFocusedStackIsUpdatedWhenMovingStack() { // Create a stack at bottom. - final ActivityDisplay display = mSupervisor.getDefaultDisplay(); - final ActivityStack stack = new StackBuilder(mSupervisor).setOnTop(!ON_TOP).build(); + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); + final ActivityStack stack = + new StackBuilder(mRootActivityContainer).setOnTop(!ON_TOP).build(); final ActivityStack prevFocusedStack = display.getFocusedStack(); stack.moveToFront("moveStackToFront"); @@ -83,7 +84,7 @@ public class ActivityDisplayTests extends ActivityTestsBase { @Test public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() { // Create a pinned stack and move to front. - final ActivityStack pinnedStack = mSupervisor.getDefaultDisplay().createStack( + final ActivityStack pinnedStack = mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP); final TaskRecord pinnedTask = new TaskBuilder(mService.mStackSupervisor) .setStack(pinnedStack).build(); @@ -96,7 +97,7 @@ public class ActivityDisplayTests extends ActivityTestsBase { // Create a fullscreen stack and move to front. final ActivityStack fullscreenStack = createFullscreenStackWithSimpleActivityAt( - mSupervisor.getDefaultDisplay()); + mRootActivityContainer.getDefaultDisplay()); fullscreenStack.moveToFront("moveFullscreenStackToFront"); // The focused stack should be the fullscreen stack. @@ -138,7 +139,7 @@ public class ActivityDisplayTests extends ActivityTestsBase { final ActivityDisplay display = spy(createNewActivityDisplay()); doReturn(false).when(display).shouldDestroyContentOnRemove(); doReturn(true).when(display).supportsSystemDecorations(); - mSupervisor.addChild(display, ActivityDisplay.POSITION_TOP); + mRootActivityContainer.addChild(display, ActivityDisplay.POSITION_TOP); // Put home stack on the display. final ActivityStack homeStack = display.createStack( @@ -175,14 +176,14 @@ public class ActivityDisplayTests extends ActivityTestsBase { */ @Test public void testTopRunningActivity() { - final ActivityDisplay display = mSupervisor.getDefaultDisplay(); + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); final KeyguardController keyguard = mSupervisor.getKeyguardController(); - final ActivityStack stack = new StackBuilder(mSupervisor).build(); + final ActivityStack stack = new StackBuilder(mRootActivityContainer).build(); final ActivityRecord activity = stack.getTopActivity(); // Create empty stack on top. final ActivityStack emptyStack = - new StackBuilder(mSupervisor).setCreateActivity(false).build(); + new StackBuilder(mRootActivityContainer).setCreateActivity(false).build(); // Make sure the top running activity is not affected when keyguard is not locked. assertTopRunningActivity(activity, display); @@ -225,7 +226,7 @@ public class ActivityDisplayTests extends ActivityTestsBase { */ @Test public void testAlwaysOnTopStackLocation() { - final ActivityDisplay display = mSupervisor.getDefaultDisplay(); + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); final ActivityStack alwaysOnTopStack = display.createStack(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true) diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index c7f0521adb7b..cac9cf69ce4d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -24,20 +24,30 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions; +import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.timeout; import android.content.Intent; import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.util.SparseIntArray; +import android.util.proto.ProtoOutputStream; import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; +import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto; + +import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentMatcher; + +import java.util.Arrays; /** * Tests for the {@link ActivityMetricsLaunchObserver} class. @@ -51,6 +61,7 @@ import org.junit.Test; public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { private ActivityMetricsLogger mActivityMetricsLogger; private ActivityMetricsLaunchObserver mLaunchObserver; + private ActivityMetricsLaunchObserverRegistry mLaunchObserverRegistry; private TestActivityStack mStack; private TaskRecord mTask; @@ -61,33 +72,62 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { public void setUpAMLO() throws Exception { setupActivityTaskManagerService(); - mActivityMetricsLogger = - new ActivityMetricsLogger(mSupervisor, mService.mContext, mService.mH.getLooper()); - mLaunchObserver = mock(ActivityMetricsLaunchObserver.class); - // TODO: Use ActivityMetricsLaunchObserverRegistry . - java.lang.reflect.Field f = - mActivityMetricsLogger.getClass().getDeclaredField("mLaunchObserver"); - f.setAccessible(true); - f.set(mActivityMetricsLogger, mLaunchObserver); + // ActivityStackSupervisor always creates its own instance of ActivityMetricsLogger. + mActivityMetricsLogger = mSupervisor.getActivityMetricsLogger(); + + mLaunchObserverRegistry = mActivityMetricsLogger.getLaunchObserverRegistry(); + mLaunchObserverRegistry.registerLaunchObserver(mLaunchObserver); // Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful. // This seems to be the easiest way to create an ActivityRecord. - mStack = mSupervisor.getDefaultDisplay().createStack( + mStack = mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); mTask = new TaskBuilder(mSupervisor).setStack(mStack).build(); mActivityRecord = new ActivityBuilder(mService).setTask(mTask).build(); mActivityRecordTrampoline = new ActivityBuilder(mService).setTask(mTask).build(); } + @After + public void tearDownAMLO() throws Exception { + if (mLaunchObserverRegistry != null) { // Don't NPE if setUp failed. + mLaunchObserverRegistry.unregisterLaunchObserver(mLaunchObserver); + } + } + + static class ActivityRecordMatcher implements ArgumentMatcher</*@ActivityRecordProto*/ byte[]> { + private final @ActivityRecordProto byte[] mExpected; + + public ActivityRecordMatcher(ActivityRecord activityRecord) { + mExpected = activityRecordToProto(activityRecord); + } + + public boolean matches(@ActivityRecordProto byte[] actual) { + return Arrays.equals(mExpected, actual); + } + } + + static @ActivityRecordProto byte[] activityRecordToProto(ActivityRecord record) { + return ActivityMetricsLogger.convertActivityRecordToProto(record); + } + + static @ActivityRecordProto byte[] eqProto(ActivityRecord record) { + return argThat(new ActivityRecordMatcher(record)); + } + + static <T> T verifyAsync(T mock) { + // AMLO callbacks happen on a separate thread than AML calls, so we need to use a timeout. + return verify(mock, timeout(100)); + } + @Test public void testOnIntentStarted() throws Exception { Intent intent = new Intent("action 1"); mActivityMetricsLogger.notifyActivityLaunching(intent); - verify(mLaunchObserver).onIntentStarted(eq(intent)); + verifyAsync(mLaunchObserver).onIntentStarted(eq(intent)); verifyNoMoreInteractions(mLaunchObserver); } @@ -102,7 +142,7 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, activityRecord); - verify(mLaunchObserver).onIntentFailed(); + verifyAsync(mLaunchObserver).onIntentFailed(); verifyNoMoreInteractions(mLaunchObserver); } @@ -113,7 +153,7 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mActivityRecord); - verify(mLaunchObserver).onActivityLaunched(eq(mActivityRecord), anyInt()); + verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mActivityRecord), anyInt()); verifyNoMoreInteractions(mLaunchObserver); } @@ -127,7 +167,7 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecord.getWindowingMode(), SystemClock.uptimeMillis()); - verify(mLaunchObserver).onActivityLaunchFinished(eq(mActivityRecord)); + verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecord)); verifyNoMoreInteractions(mLaunchObserver); } @@ -135,12 +175,12 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { public void testOnActivityLaunchCancelled() throws Exception { testOnActivityLaunched(); - mActivityRecord.nowVisible = true; + mActivityRecord.mDrawn = true; // Cannot time already-visible activities. mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mActivityRecord); - verify(mLaunchObserver).onActivityLaunchCancelled(eq(mActivityRecord)); + verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mActivityRecord)); verifyNoMoreInteractions(mLaunchObserver); } @@ -151,7 +191,7 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mActivityRecord); - verify(mLaunchObserver).onActivityLaunched(eq(mActivityRecord), anyInt()); + verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mActivityRecord), anyInt()); // A second, distinct, activity launch is coalesced into the the current app launch sequence mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, @@ -170,7 +210,7 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecordTrampoline.getWindowingMode(), SystemClock.uptimeMillis()); - verify(mLaunchObserver).onActivityLaunchFinished(eq(mActivityRecordTrampoline)); + verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecordTrampoline)); verifyNoMoreInteractions(mLaunchObserver); } @@ -178,13 +218,26 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { public void testOnActivityLaunchCancelledTrampoline() throws Exception { testOnActivityLaunchedTrampoline(); - mActivityRecordTrampoline.nowVisible = true; + mActivityRecordTrampoline.mDrawn = true; // Cannot time already-visible activities. mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mActivityRecordTrampoline); - verify(mLaunchObserver).onActivityLaunchCancelled(eq(mActivityRecordTrampoline)); + verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mActivityRecordTrampoline)); verifyNoMoreInteractions(mLaunchObserver); } + + @Test + public void testActivityRecordProtoIsNotTooBig() throws Exception { + // The ActivityRecordProto must not be too big, otherwise converting it at runtime + // will become prohibitively expensive. + assertWithMessage("mActivityRecord: %s", mActivityRecord). + that(activityRecordToProto(mActivityRecord).length). + isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE); + + assertWithMessage("mActivityRecordTrampoline: %s", mActivityRecordTrampoline). + that(activityRecordToProto(mActivityRecordTrampoline).length). + isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE); + } } 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 170bd3311de2..b6f181758d3a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -29,10 +29,9 @@ import static com.android.server.wm.ActivityStack.ActivityState.PAUSING; import static com.android.server.wm.ActivityStack.ActivityState.STOPPED; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING; -import static junit.framework.TestCase.assertNotNull; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -65,7 +64,7 @@ public class ActivityRecordTests extends ActivityTestsBase { @Before public void setUp() throws Exception { setupActivityTaskManagerService(); - mStack = new StackBuilder(mSupervisor).build(); + mStack = new StackBuilder(mRootActivityContainer).build(); mTask = mStack.getChildAt(0); mActivity = mTask.getTopActivity(); } @@ -86,7 +85,7 @@ public class ActivityRecordTests extends ActivityTestsBase { public void testStackCleanupOnTaskRemoval() { mStack.removeTask(mTask, null /*reason*/, REMOVE_TASK_MODE_MOVING); // Stack should be gone on task removal. - assertNull(mService.mStackSupervisor.getStack(mStack.mStackId)); + assertNull(mService.mRootActivityContainer.getStack(mStack.mStackId)); } @Test @@ -116,7 +115,7 @@ public class ActivityRecordTests extends ActivityTestsBase { assertFalse(pauseFound.value); // Clear focused stack - final ActivityDisplay display = mActivity.mStackSupervisor.getDefaultDisplay(); + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); when(display.getFocusedStack()).thenReturn(null); // In the unfocused stack, the activity should move to paused. diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java index 8a6d5873927a..78a67d212f24 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java @@ -38,8 +38,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.ActivityDisplay.POSITION_TOP; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; -import static com.android.server.wm.ActivityStackSupervisor - .MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE; +import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE; import static com.google.common.truth.Truth.assertThat; @@ -83,78 +82,11 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { @Before public void setUp() throws Exception { setupActivityTaskManagerService(); - mFullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack( + mFullscreenStack = mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); } /** - * This test ensures that we do not try to restore a task based off an invalid task id. We - * should expect {@code null} to be returned in this case. - */ - @Test - public void testRestoringInvalidTask() { - ((TestActivityDisplay) mSupervisor.getDefaultDisplay()).removeAllTasks(); - TaskRecord task = mSupervisor.anyTaskForIdLocked(0 /*taskId*/, - MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */); - assertNull(task); - } - - /** - * This test ensures that an existing task in the pinned stack is moved to the fullscreen - * activity stack when a new task is added. - */ - @Test - public void testReplacingTaskInPinnedStack() { - final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true) - .setStack(mFullscreenStack).build(); - final TaskRecord firstTask = firstActivity.getTask(); - - final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true) - .setStack(mFullscreenStack).build(); - final TaskRecord secondTask = secondActivity.getTask(); - - mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack"); - - // Ensure full screen stack has both tasks. - ensureStackPlacement(mFullscreenStack, firstTask, secondTask); - - // Move first activity to pinned stack. - final Rect sourceBounds = new Rect(); - mSupervisor.moveActivityToPinnedStackLocked(firstActivity, sourceBounds, - 0f /*aspectRatio*/, "initialMove"); - - final ActivityDisplay display = mFullscreenStack.getDisplay(); - ActivityStack pinnedStack = display.getPinnedStack(); - // Ensure a task has moved over. - ensureStackPlacement(pinnedStack, firstTask); - ensureStackPlacement(mFullscreenStack, secondTask); - - // Move second activity to pinned stack. - mSupervisor.moveActivityToPinnedStackLocked(secondActivity, sourceBounds, - 0f /*aspectRatio*/, "secondMove"); - - // Need to get stacks again as a new instance might have been created. - pinnedStack = display.getPinnedStack(); - mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - // Ensure stacks have swapped tasks. - ensureStackPlacement(pinnedStack, secondTask); - ensureStackPlacement(mFullscreenStack, firstTask); - } - - private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) { - final ArrayList<TaskRecord> stackTasks = stack.getAllTasks(); - assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0); - - if (tasks == null) { - return; - } - - for (TaskRecord task : tasks) { - assertTrue(stackTasks.contains(task)); - } - } - - /** * Ensures that an activity is removed from the stopping activities list once it is resumed. */ @Test @@ -179,7 +111,7 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { // #notifyAll will be called on the ActivityManagerService. we must hold the object lock // when this happens. - synchronized (mSupervisor.mService.mGlobalLock) { + synchronized (mService.mGlobalLock) { final WaitResult taskToFrontWait = new WaitResult(); mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait); mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity, START_TASK_TO_FRONT); @@ -198,334 +130,4 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { assertEquals(deliverToTopWait.who, firstActivity.realActivity); } } - - @Test - public void testApplySleepTokensLocked() { - final ActivityDisplay display = mSupervisor.getDefaultDisplay(); - final KeyguardController keyguard = mSupervisor.getKeyguardController(); - final ActivityStack stack = mock(ActivityStack.class); - display.addChild(stack, 0 /* position */); - - // Make sure we wake and resume in the case the display is turning on and the keyguard is - // not showing. - verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, - false /* displayShouldSleep */, true /* isFocusedStack */, - false /* keyguardShowing */, true /* expectWakeFromSleep */, - true /* expectResumeTopActivity */); - - // Make sure we wake and don't resume when the display is turning on and the keyguard is - // showing. - verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, - false /* displayShouldSleep */, true /* isFocusedStack */, - true /* keyguardShowing */, true /* expectWakeFromSleep */, - false /* expectResumeTopActivity */); - - // Make sure we wake and don't resume when the display is turning on and the keyguard is - // not showing as unfocused. - verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, - false /* displayShouldSleep */, false /* isFocusedStack */, - false /* keyguardShowing */, true /* expectWakeFromSleep */, - false /* expectResumeTopActivity */); - - // Should not do anything if the display state hasn't changed. - verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/, - false /* displayShouldSleep */, true /* isFocusedStack */, - false /* keyguardShowing */, false /* expectWakeFromSleep */, - false /* expectResumeTopActivity */); - } - - private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard, - ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep, - boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep, - boolean expectResumeTopActivity) { - reset(stack); - - doReturn(displayShouldSleep).when(display).shouldSleep(); - doReturn(displaySleeping).when(display).isSleeping(); - doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt()); - - doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay(); - doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack(); - mSupervisor.applySleepTokensLocked(true); - verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked(); - verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked( - null /* target */, null /* targetOptions */); - } - - /** - * Verifies that removal of activity with task and stack is done correctly. - */ - @Test - public void testRemovingStackOnAppCrash() { - final ActivityDisplay defaultDisplay = mService.mStackSupervisor.getDefaultDisplay(); - final int originalStackCount = defaultDisplay.getChildCount(); - final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); - final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true) - .setStack(stack).build(); - - assertEquals(originalStackCount + 1, defaultDisplay.getChildCount()); - - // Let's pretend that the app has crashed. - firstActivity.app.setThread(null); - mService.mStackSupervisor.finishTopCrashedActivitiesLocked(firstActivity.app, "test"); - - // Verify that the stack was removed. - assertEquals(originalStackCount, defaultDisplay.getChildCount()); - } - - @Test - public void testFocusability() { - final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack( - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true) - .setStack(stack).build(); - - // Under split screen primary we should be focusable when not minimized - mService.mStackSupervisor.setDockedStackMinimized(false); - assertTrue(stack.isFocusable()); - assertTrue(activity.isFocusable()); - - // Under split screen primary we should not be focusable when minimized - mService.mStackSupervisor.setDockedStackMinimized(true); - assertFalse(stack.isFocusable()); - assertFalse(activity.isFocusable()); - - final ActivityStack pinnedStack = mService.mStackSupervisor.getDefaultDisplay().createStack( - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true) - .setStack(pinnedStack).build(); - - // We should not be focusable when in pinned mode - assertFalse(pinnedStack.isFocusable()); - assertFalse(pinnedActivity.isFocusable()); - - // Add flag forcing focusability. - pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE; - - // We should not be focusable when in pinned mode - assertTrue(pinnedStack.isFocusable()); - assertTrue(pinnedActivity.isFocusable()); - - // Without the overridding activity, stack should not be focusable. - pinnedStack.removeTask(pinnedActivity.getTask(), "testFocusability", - REMOVE_TASK_MODE_DESTROYING); - assertFalse(pinnedStack.isFocusable()); - } - - /** - * Verify that split-screen primary stack will be chosen if activity is launched that targets - * split-screen secondary, but a matching existing instance is found on top of split-screen - * primary stack. - */ - @Test - public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() { - // Create primary split-screen stack with a task and an activity. - final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay() - .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build(); - final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build(); - - // Find a launch stack for the top activity in split-screen primary, while requesting - // split-screen secondary. - final ActivityOptions options = ActivityOptions.makeBasic(); - options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY); - final ActivityStack result = mSupervisor.getLaunchStack(r, options, task, true /* onTop */); - - // Assert that the primary stack is returned. - assertEquals(primaryStack, result); - } - - /** - * Verify split-screen primary stack & task can resized by - * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect. - */ - @Test - public void testResizeDockedStackForSplitScreenPrimary() { - final Rect taskSize = new Rect(0, 0, 600, 600); - final Rect stackSize = new Rect(0, 0, 300, 300); - - // Create primary split-screen stack with a task. - final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay() - .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build(); - - // Resize dock stack. - mService.resizeDockedStack(stackSize, taskSize, null, null, null); - - // Verify dock stack & its task bounds if is equal as resized result. - assertEquals(primaryStack.getBounds(), stackSize); - assertEquals(task.getBounds(), taskSize); - } - - /** - * Verify that home stack would be moved to front when the top activity is Recents. - */ - @Test - public void testFindTaskToMoveToFrontWhenRecentsOnTop() { - // Create stack/task on default display. - final ActivityDisplay display = mSupervisor.getDefaultDisplay(); - final TestActivityStack targetStack = new StackBuilder(mSupervisor).setOnTop(false).build(); - final TaskRecord targetTask = targetStack.getChildAt(0); - - // Create Recents on top of the display. - final ActivityStack stack = - new StackBuilder(mSupervisor).setActivityType(ACTIVITY_TYPE_RECENTS).build(); - - final String reason = "findTaskToMoveToFront"; - mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, - false); - - verify(display).moveHomeStackToFront(contains(reason)); - } - - /** - * Verify that home stack won't be moved to front if the top activity on other display is - * Recents. - */ - @Test - public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() { - // Create stack/task on default display. - final ActivityDisplay display = mSupervisor.getDefaultDisplay(); - final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, false /* onTop */); - final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build(); - - // Create Recents on secondary display. - final TestActivityDisplay secondDisplay = addNewActivityDisplayAt( - ActivityDisplay.POSITION_TOP); - final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_RECENTS, true /* onTop */); - final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build(); - new ActivityBuilder(mService).setTask(task).build(); - - final String reason = "findTaskToMoveToFront"; - mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, - false); - - verify(display, never()).moveHomeStackToFront(contains(reason)); - } - - /** - * Verify if a stack is not at the topmost position, it should be able to resume its activity if - * the stack is the top focused. - */ - @Test - public void testResumeActivityWhenNonTopmostStackIsTopFocused() { - // Create a stack at bottom. - final ActivityDisplay display = mSupervisor.getDefaultDisplay(); - final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, false /* onTop */)); - final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build(); - final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build(); - display.positionChildAtBottom(targetStack); - - // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it - // is the current top focused stack. - assertFalse(targetStack.isTopStackOnDisplay()); - doReturn(targetStack).when(mSupervisor).getTopDisplayFocusedStack(); - - // Use the stack as target to resume. - mSupervisor.resumeFocusedStacksTopActivitiesLocked( - targetStack, activity, null /* targetOptions */); - - // Verify the target stack should resume its activity. - verify(targetStack, times(1)).resumeTopActivityUncheckedLocked( - eq(activity), eq(null /* targetOptions */)); - } - - /** - * Tests home activities that targeted sdk before Q cannot start on secondary display. - */ - @Test - public void testStartHomeTargetSdkBeforeQ() throws Exception { - final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay()); - mSupervisor.addChild(secondDisplay, POSITION_TOP); - doReturn(true).when(secondDisplay).supportsSystemDecorations(); - - final ActivityInfo info = new ActivityInfo(); - info.launchMode = LAUNCH_MULTIPLE; - info.applicationInfo = new ApplicationInfo(); - info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; - assertTrue(mSupervisor.canStartHomeOnDisplay(info, secondDisplay.mDisplayId, - false /* allowInstrumenting */)); - - info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P; - assertFalse(mSupervisor.canStartHomeOnDisplay(info, secondDisplay.mDisplayId, - false /* allowInstrumenting */)); - } - - /** - * Tests that home activities can be started on the displays that supports system decorations. - */ - @Test - public void testStartHomeOnAllDisplays() { - // Create secondary displays. - final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay()); - mSupervisor.addChild(secondDisplay, POSITION_TOP); - doReturn(true).when(secondDisplay).supportsSystemDecorations(); - - // Create mock tasks and other necessary mocks. - TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false); - final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class); - TaskRecord.setTaskRecordFactory(factory); - doAnswer(i -> taskBuilder.build()).when(factory) - .create(any(), anyInt(), any(), any(), any(), any()); - doReturn(true).when(mService.mStackSupervisor) - .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean()); - doReturn(true).when(mSupervisor).canStartHomeOnDisplay(any(), anyInt(), anyBoolean()); - - mSupervisor.startHomeOnAllDisplays(0, "testStartHome"); - - assertTrue(mSupervisor.getDefaultDisplay().getTopStack().isActivityTypeHome()); - assertNotNull(secondDisplay.getTopStack()); - assertTrue(secondDisplay.getTopStack().isActivityTypeHome()); - } - - /** - * Tests that home activities won't be started before booting when display added. - */ - @Test - public void testNotStartHomeBeforeBoot() { - final int displayId = 1; - final boolean isBooting = mService.mAmInternal.isBooting(); - final boolean isBooted = mService.mAmInternal.isBooted(); - try { - mService.mAmInternal.setBooting(false); - mService.mAmInternal.setBooted(false); - mSupervisor.onDisplayAdded(displayId); - verify(mSupervisor, never()).startHomeOnDisplay(anyInt(), any(), anyInt()); - } finally { - mService.mAmInternal.setBooting(isBooting); - mService.mAmInternal.setBooted(isBooted); - } - } - - /** - * Tests whether home can be started if being instrumented. - */ - @Test - public void testCanStartHomeWhenInstrumented() { - final ActivityInfo info = new ActivityInfo(); - info.applicationInfo = new ApplicationInfo(); - final WindowProcessController app = mock(WindowProcessController.class); - doReturn(app).when(mService).getProcessController(any(), anyInt()); - - // Can not start home if we don't want to start home while home is being instrumented. - doReturn(true).when(app).isInstrumenting(); - assertFalse(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, - false /* allowInstrumenting*/)); - - // Can start home for other cases. - assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, - true /* allowInstrumenting*/)); - - doReturn(false).when(app).isInstrumenting(); - assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, - false /* allowInstrumenting*/)); - assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, - true /* allowInstrumenting*/)); - } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index 2fe45b86df4a..0da0b247b60c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -74,7 +74,7 @@ public class ActivityStackTests extends ActivityTestsBase { @Before public void setUp() throws Exception { setupActivityTaskManagerService(); - mDefaultDisplay = mSupervisor.getDefaultDisplay(); + mDefaultDisplay = mRootActivityContainer.getDefaultDisplay(); mStack = spy(mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */)); mTask = new TaskBuilder(mSupervisor).setStack(mStack).build(); @@ -112,7 +112,7 @@ public class ActivityStackTests extends ActivityTestsBase { r.setState(RESUMED, "testResumedActivityFromTaskReparenting"); assertEquals(r, mStack.getResumedActivity()); - final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack( + final ActivityStack destStack = mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); mTask.reparent(destStack, true /* toTop */, TaskRecord.REPARENT_KEEP_STACK_AT_FRONT, @@ -130,7 +130,7 @@ public class ActivityStackTests extends ActivityTestsBase { r.setState(RESUMED, "testResumedActivityFromActivityReparenting"); assertEquals(r, mStack.getResumedActivity()); - final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack( + final ActivityStack destStack = mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); final TaskRecord destTask = new TaskBuilder(mSupervisor).setStack(destStack).build(); @@ -239,8 +239,8 @@ public class ActivityStackTests extends ActivityTestsBase { .setUid(UserHandle.PER_USER_RANGE * 2).build(); taskOverlay.mTaskOverlay = true; - final ActivityStackSupervisor.FindTaskResult result = - new ActivityStackSupervisor.FindTaskResult(); + final RootActivityContainer.FindTaskResult result = + new RootActivityContainer.FindTaskResult(); mStack.findTaskLocked(r, result); assertEquals(r, task.getTopActivity(false /* includeOverlays */)); @@ -700,7 +700,7 @@ public class ActivityStackTests extends ActivityTestsBase { // should be destroyed immediately with updating configuration to restore original state. final ActivityRecord activity1 = finishCurrentActivity(stack1); assertEquals(DESTROYING, activity1.getState()); - verify(mSupervisor).ensureVisibilityAndConfig(eq(null) /* starting */, + verify(mRootActivityContainer).ensureVisibilityAndConfig(eq(null) /* starting */, eq(display.mDisplayId), anyBoolean(), anyBoolean()); } @@ -778,7 +778,7 @@ public class ActivityStackTests extends ActivityTestsBase { final ActivityDisplay display = mock(ActivityDisplay.class); final KeyguardController keyguardController = mSupervisor.getKeyguardController(); - doReturn(display).when(mSupervisor).getActivityDisplay(anyInt()); + doReturn(display).when(mRootActivityContainer).getActivityDisplay(anyInt()); doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway(); doReturn(displaySleeping).when(display).isSleeping(); doReturn(focusedStack).when(mStack).isFocusedStackOnDisplay(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java index 9d93c85480bc..2ba2fdbcb959 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java @@ -74,7 +74,7 @@ public class ActivityStartControllerTests extends ActivityTestsBase { final ActivityRecord activity = new ActivityBuilder(mService).build(); final ActivityRecord source = new ActivityBuilder(mService).build(); final int startFlags = random.nextInt(); - final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack( + final ActivityStack stack = mService.mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); final WindowProcessController wpc = new WindowProcessController(mService, mService.mContext.getApplicationInfo(), "name", 12345, diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java index 27fa20b91a80..350114c792bf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java @@ -90,6 +90,8 @@ public class ActivityStartInterceptorTest { @Mock private ActivityTaskManagerService mService; @Mock + private RootActivityContainer mRootActivityContainer; + @Mock private ActivityStackSupervisor mSupervisor; @Mock private DevicePolicyManagerInternal mDevicePolicyManager; @@ -111,7 +113,8 @@ public class ActivityStartInterceptorTest { public void setUp() { MockitoAnnotations.initMocks(this); mService.mAmInternal = mAmInternal; - mInterceptor = new ActivityStartInterceptor(mService, mSupervisor, mContext); + mInterceptor = new ActivityStartInterceptor( + mService, mSupervisor, mRootActivityContainer, mContext); mInterceptor.setStates(TEST_USER_ID, TEST_REAL_CALLING_PID, TEST_REAL_CALLING_UID, TEST_START_FLAGS, TEST_CALLING_PACKAGE); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 50aa541a9549..f19e28d0cb79 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.app.ActivityManager.START_ABORTED; import static android.app.ActivityManager.START_CLASS_NOT_FOUND; import static android.app.ActivityManager.START_DELIVERED_TO_TOP; @@ -69,6 +70,7 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageManagerInternal; import android.graphics.Rect; import android.os.IBinder; +import android.os.Process; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.service.voice.IVoiceInteractionSession; @@ -110,6 +112,8 @@ public class ActivityStarterTests extends ActivityTestsBase { private static final int FAKE_CALLING_UID = 666; private static final int FAKE_REAL_CALLING_UID = 667; private static final String FAKE_CALLING_PACKAGE = "com.whatever.dude"; + private static final int UNIMPORTANT_UID = 12345; + private static final int UNIMPORTANT_UID2 = 12346; @Before public void setUp() throws Exception { @@ -125,7 +129,7 @@ public class ActivityStarterTests extends ActivityTestsBase { public void testUpdateLaunchBounds() { // When in a non-resizeable stack, the task bounds should be updated. final TaskRecord task = new TaskBuilder(mService.mStackSupervisor) - .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack( + .setStack(mService.mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */)) .build(); final Rect bounds = new Rect(10, 10, 100, 100); @@ -136,7 +140,7 @@ public class ActivityStarterTests extends ActivityTestsBase { // When in a resizeable stack, the stack bounds should be updated as well. final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor) - .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack( + .setStack(mService.mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */)) .build(); assertThat((Object) task2.getStack()).isInstanceOf(PinnedActivityStack.class); @@ -314,7 +318,7 @@ public class ActivityStarterTests extends ActivityTestsBase { * Creates a {@link ActivityStarter} with default parameters and necessary mocks. * * @param launchFlags The intent flags to launch activity. - * @param mockGetLaunchStack Whether to mock {@link ActivityStackSupervisor#getLaunchStack} for + * @param mockGetLaunchStack Whether to mock {@link RootActivityContainer#getLaunchStack} for * always launching to the testing stack. Set to false when allowing * the activity can be launched to any stack that is decided by real * implementation. @@ -323,14 +327,14 @@ public class ActivityStarterTests extends ActivityTestsBase { private ActivityStarter prepareStarter(@Intent.Flags int launchFlags, boolean mockGetLaunchStack) { // always allow test to start activity. - doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission( + doReturn(true).when(mSupervisor).checkStartAnyActivityPermission( any(), any(), any(), anyInt(), anyInt(), anyInt(), any(), anyBoolean(), anyBoolean(), any(), any(), any()); // instrument the stack and task used. - final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack( + final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final TaskRecord task = new TaskBuilder(mService.mStackSupervisor) + final TaskRecord task = new TaskBuilder(mSupervisor) .setCreateStack(false) .build(); @@ -343,9 +347,9 @@ public class ActivityStarterTests extends ActivityTestsBase { if (mockGetLaunchStack) { // Direct starter to use spy stack. - doReturn(stack).when(mService.mStackSupervisor) + doReturn(stack).when(mRootActivityContainer) .getLaunchStack(any(), any(), any(), anyBoolean()); - doReturn(stack).when(mService.mStackSupervisor) + doReturn(stack).when(mRootActivityContainer) .getLaunchStack(any(), any(), any(), anyBoolean(), any()); } @@ -441,7 +445,7 @@ public class ActivityStarterTests extends ActivityTestsBase { final ActivityStack focusStack = focusActivity.getStack(); focusStack.moveToFront("testSplitScreenDeliverToTop"); - doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt()); + doReturn(reusableActivity).when(mRootActivityContainer).findTask(any(), anyInt()); final int result = starter.setReason("testSplitScreenDeliverToTop").execute(); @@ -473,7 +477,7 @@ public class ActivityStarterTests extends ActivityTestsBase { // Enter split-screen. Primary stack should have focus. focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt()); + doReturn(reusableActivity).when(mRootActivityContainer).findTask(any(), anyInt()); final int result = starter.setReason("testSplitScreenMoveToFront").execute(); @@ -486,7 +490,7 @@ public class ActivityStarterTests extends ActivityTestsBase { */ @Test public void testTaskModeViolation() { - final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay(); + final ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay(); ((TestActivityDisplay) display).removeAllTasks(); assertNoTasks(display); @@ -551,6 +555,123 @@ public class ActivityStarterTests extends ActivityTestsBase { } /** + * This test ensures that unsupported usecases aren't aborted when background starts are + * allowed. + */ + @Test + public void testBackgroundActivityStartsAllowed_noStartsAborted() { + doReturn(true).when(mService).isBackgroundActivityStartsEnabled(); + + runAndVerifyBackgroundActivityStartsSubtest("allowed_noStartsAborted", false, + UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, + UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1, + false, false); + } + + /** + * This test ensures that unsupported usecases are aborted when background starts are + * disallowed. + */ + @Test + public void testBackgroundActivityStartsDisallowed_unsupportedStartsAborted() { + doReturn(false).when(mService).isBackgroundActivityStartsEnabled(); + + runAndVerifyBackgroundActivityStartsSubtest( + "disallowed_unsupportedUsecase_aborted", true, + UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, + UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1, + false, false); + } + + /** + * This test ensures that supported usecases aren't aborted when background starts are + * disallowed. + * The scenarios each have only one condidion that makes them supported. + */ + @Test + public void testBackgroundActivityStartsDisallowed_supportedStartsNotAborted() { + doReturn(false).when(mService).isBackgroundActivityStartsEnabled(); + + runAndVerifyBackgroundActivityStartsSubtest("disallowed_rootUid_notAborted", false, + Process.ROOT_UID, false, PROCESS_STATE_TOP + 1, + UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1, + false, false); + runAndVerifyBackgroundActivityStartsSubtest("disallowed_systemUid_notAborted", false, + Process.SYSTEM_UID, false, PROCESS_STATE_TOP + 1, + UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1, + false, false); + runAndVerifyBackgroundActivityStartsSubtest( + "disallowed_callingUidHasVisibleWindow_notAborted", false, + UNIMPORTANT_UID, true, PROCESS_STATE_TOP + 1, + UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1, + false, false); + runAndVerifyBackgroundActivityStartsSubtest( + "disallowed_callingUidProcessStateTop_notAborted", false, + UNIMPORTANT_UID, false, PROCESS_STATE_TOP, + UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1, + false, false); + runAndVerifyBackgroundActivityStartsSubtest( + "disallowed_realCallingUidHasVisibleWindow_notAborted", false, + UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, + UNIMPORTANT_UID2, true, PROCESS_STATE_TOP + 1, + false, false); + runAndVerifyBackgroundActivityStartsSubtest( + "disallowed_realCallingUidProcessStateTop_notAborted", false, + UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, + UNIMPORTANT_UID2, false, PROCESS_STATE_TOP, + false, false); + runAndVerifyBackgroundActivityStartsSubtest( + "disallowed_hasForegroundActivities_notAborted", false, + UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, + UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1, + true, false); + runAndVerifyBackgroundActivityStartsSubtest( + "disallowed_callerIsRecents_notAborted", false, + UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, + UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1, + false, true); + } + + private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted, + int callingUid, boolean callingUidHasVisibleWindow, int callingUidProcState, + int realCallingUid, boolean realCallingUidHasVisibleWindow, int realCallingUidProcState, + boolean hasForegroundActivities, boolean callerIsRecents) { + // window visibility + doReturn(callingUidHasVisibleWindow).when(mService.mWindowManager).isAnyWindowVisibleForUid( + callingUid); + doReturn(realCallingUidHasVisibleWindow).when(mService.mWindowManager) + .isAnyWindowVisibleForUid(realCallingUid); + // process importance + doReturn(callingUidProcState).when(mService).getUidStateLocked(callingUid); + doReturn(realCallingUidProcState).when(mService).getUidStateLocked(realCallingUid); + // foreground activities + final IApplicationThread caller = mock(IApplicationThread.class); + final ApplicationInfo ai = new ApplicationInfo(); + ai.uid = callingUid; + final WindowProcessController callerApp = + new WindowProcessController(mService, ai, null, callingUid, -1, null, null); + callerApp.setHasForegroundActivities(hasForegroundActivities); + doReturn(callerApp).when(mService).getProcessController(caller); + // caller is recents + RecentTasks recentTasks = mock(RecentTasks.class); + mService.mStackSupervisor.setRecentTasks(recentTasks); + doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid); + + final ActivityOptions options = spy(ActivityOptions.makeBasic()); + ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK) + .setCaller(caller) + .setCallingUid(callingUid) + .setRealCallingUid(realCallingUid) + .setActivityOptions(new SafeActivityOptions(options)); + + final int result = starter.setReason("testBackgroundActivityStarts_" + name).execute(); + + assertEquals(ActivityStarter.getExternalResult( + shouldHaveAborted ? START_ABORTED : START_SUCCESS), result); + verify(options, times(shouldHaveAborted ? 1 : 0)).abort(); + } + + /** * This test ensures that when starting an existing single task activity on secondary display * which is not the top focused display, it should deliver new intent to the activity and not * create a new stack. @@ -562,7 +683,7 @@ public class ActivityStarterTests extends ActivityTestsBase { // Create a secondary display at bottom. final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay()); - mSupervisor.addChild(secondaryDisplay, POSITION_BOTTOM); + mRootActivityContainer.addChild(secondaryDisplay, POSITION_BOTTOM); final ActivityStack stack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); @@ -600,7 +721,7 @@ public class ActivityStarterTests extends ActivityTestsBase { // Create a secondary display with an activity. final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay()); - mSupervisor.addChild(secondaryDisplay, POSITION_TOP); + mRootActivityContainer.addChild(secondaryDisplay, POSITION_TOP); final ActivityRecord singleTaskActivity = createSingleTaskActivityOn( secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */)); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index c2ab3acd4933..ead9731782e8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -94,6 +94,7 @@ class ActivityTestsBase { final TestInjector mTestInjector = new TestInjector(); ActivityTaskManagerService mService; + RootActivityContainer mRootActivityContainer; ActivityStackSupervisor mSupervisor; // Default package name @@ -120,6 +121,7 @@ class ActivityTestsBase { ActivityTaskManagerService createActivityTaskManagerService() { mService = new TestActivityTaskManagerService(mContext); mSupervisor = mService.mStackSupervisor; + mRootActivityContainer = mService.mRootActivityContainer; return mService; } @@ -139,7 +141,7 @@ class ActivityTestsBase { /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */ TestActivityDisplay addNewActivityDisplayAt(int position) { final TestActivityDisplay display = createNewActivityDisplay(); - mSupervisor.addChild(display, position); + mRootActivityContainer.addChild(display, position); return display; } @@ -231,7 +233,9 @@ class ActivityTestsBase { aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */, 0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */, mService.mStackSupervisor, null /* options */, null /* sourceRecord */); - activity.mWindowContainerController = mock(AppWindowContainerController.class); + spyOn(activity); + activity.mAppWindowToken = mock(AppWindowToken.class); + doNothing().when(activity).removeWindowContainer(); if (mTaskRecord != null) { mTaskRecord.addActivityToTop(activity); @@ -317,7 +321,7 @@ class ActivityTestsBase { TaskRecord build() { if (mStack == null && mCreateStack) { - mStack = mSupervisor.getDefaultDisplay().createStack( + mStack = mSupervisor.mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); } @@ -375,6 +379,8 @@ class ActivityTestsBase { // We keep the reference in order to prevent creating it twice. ActivityStackSupervisor mTestStackSupervisor; + ActivityDisplay mDefaultDisplay; + TestActivityTaskManagerService(Context context) { super(context); spyOn(this); @@ -390,18 +396,11 @@ class ActivityTestsBase { final TestActivityManagerService am = new TestActivityManagerService(mTestInjector, this); - // Put a home stack on the default display, so that we'll always have something - // focusable. - final TestActivityStackSupervisor supervisor = - (TestActivityStackSupervisor) mStackSupervisor; - supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); - final TaskRecord task = new TaskBuilder(mStackSupervisor) - .setStack(supervisor.getDefaultDisplay().getHomeStack()).build(); - new ActivityBuilder(this).setTask(task).build(); - spyOn(getLifecycleManager()); spyOn(getLockTaskController()); doReturn(mock(IPackageManager.class)).when(this).getPackageManager(); + // allow background activity starts by default + doReturn(true).when(this).isBackgroundActivityStartsEnabled(); } void setActivityManagerService(IntentFirewall intentFirewall, @@ -409,9 +408,38 @@ class ActivityTestsBase { WindowManagerService wm) { mAmInternal = amInternal; setActivityManagerService(intentFirewall, intentController); + initRootActivityContainerMocks(wm); setWindowManager(wm); } + void initRootActivityContainerMocks(WindowManagerService wm) { + spyOn(mRootActivityContainer); + mRootActivityContainer.setWindowContainer(mock(RootWindowContainer.class)); + mRootActivityContainer.mWindowManager = wm; + mRootActivityContainer.mDisplayManager = + (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); + doNothing().when(mRootActivityContainer).setWindowManager(any()); + // Invoked during {@link ActivityStack} creation. + doNothing().when(mRootActivityContainer).updateUIDsPresentOnDisplay(); + // Always keep things awake. + doReturn(true).when(mRootActivityContainer).hasAwakeDisplay(); + // Called when moving activity to pinned stack. + doNothing().when(mRootActivityContainer).ensureActivitiesVisible(any(), anyInt(), + anyBoolean()); + + // Create a default display and put a home stack on it so that we'll always have + // something focusable. + mDefaultDisplay = TestActivityDisplay.create(mStackSupervisor, DEFAULT_DISPLAY); + spyOn(mDefaultDisplay); + mRootActivityContainer.addChild(mDefaultDisplay, ActivityDisplay.POSITION_TOP); + mDefaultDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); + final TaskRecord task = new TaskBuilder(mStackSupervisor) + .setStack(mDefaultDisplay.getHomeStack()).build(); + new ActivityBuilder(this).setTask(task).build(); + + doReturn(mDefaultDisplay).when(mRootActivityContainer).getDefaultDisplay(); + } + @Override int handleIncomingUser(int callingPid, int callingUid, int userId, String name) { return userId; @@ -508,25 +536,14 @@ class ActivityTestsBase { * setup not available in the test environment. Also specifies an injector for */ protected class TestActivityStackSupervisor extends ActivityStackSupervisor { - private ActivityDisplay mDisplay; private KeyguardController mKeyguardController; TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) { super(service, looper); spyOn(this); - mDisplayManager = - (DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE); mWindowManager = prepareMockWindowManager(); mKeyguardController = mock(KeyguardController.class); - setWindowContainerController(mock(RootWindowContainerController.class)); - // Invoked during {@link ActivityStack} creation. - doNothing().when(this).updateUIDsPresentOnDisplay(); - // Always keep things awake. - doReturn(true).when(this).hasAwakeDisplay(); - // Called when moving activity to pinned stack. - doNothing().when(this).ensureActivitiesVisibleLocked(any(), anyInt(), - anyBoolean()); // Do not schedule idle timeouts doNothing().when(this).scheduleIdleTimeoutLocked(any()); // unit test version does not handle launch wake lock @@ -537,24 +554,11 @@ class ActivityTestsBase { } @Override - public void initialize() { - super.initialize(); - mDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY); - spyOn(mDisplay); - addChild(mDisplay, ActivityDisplay.POSITION_TOP); - } - - @Override public KeyguardController getKeyguardController() { return mKeyguardController; } @Override - ActivityDisplay getDefaultDisplay() { - return mDisplay; - } - - @Override void setWindowManager(WindowManagerService wm) { mWindowManager = wm; } @@ -571,7 +575,7 @@ class ActivityTestsBase { DisplayInfo info) { if (displayId == DEFAULT_DISPLAY) { return new TestActivityDisplay(supervisor, - supervisor.mDisplayManager.getDisplay(displayId)); + supervisor.mRootActivityContainer.mDisplayManager.getDisplay(displayId)); } final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId, info, DEFAULT_DISPLAY_ADJUSTMENTS); @@ -579,7 +583,7 @@ class ActivityTestsBase { } TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) { - super(supervisor, display); + super(supervisor.mService.mRootActivityContainer, display); // Normally this comes from display-properties as exposed by WM. Without that, just // hard-code to FULLSCREEN for tests. setWindowingMode(WINDOWING_MODE_FULLSCREEN); @@ -590,7 +594,7 @@ class ActivityTestsBase { @Override <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType, int stackId, boolean onTop) { - return new StackBuilder(mSupervisor).setDisplay(this) + return new StackBuilder(mSupervisor.mRootActivityContainer).setDisplay(this) .setWindowingMode(windowingMode).setActivityType(activityType) .setStackId(stackId).setOnTop(onTop).setCreateActivity(false).build(); } @@ -732,8 +736,8 @@ class ActivityTestsBase { } } - protected static class StackBuilder { - private final ActivityStackSupervisor mSupervisor; + static class StackBuilder { + private final RootActivityContainer mRootActivityContainer; private ActivityDisplay mDisplay; private int mStackId = -1; private int mWindowingMode = WINDOWING_MODE_FULLSCREEN; @@ -741,9 +745,9 @@ class ActivityTestsBase { private boolean mOnTop = true; private boolean mCreateActivity = true; - StackBuilder(ActivityStackSupervisor supervisor) { - mSupervisor = supervisor; - mDisplay = mSupervisor.getDefaultDisplay(); + StackBuilder(RootActivityContainer root) { + mRootActivityContainer = root; + mDisplay = mRootActivityContainer.getDefaultDisplay(); } StackBuilder setWindowingMode(int windowingMode) { @@ -780,7 +784,8 @@ class ActivityTestsBase { <T extends ActivityStack> T build() { final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId(); if (mWindowingMode == WINDOWING_MODE_PINNED) { - return (T) new PinnedActivityStack(mDisplay, stackId, mSupervisor, mOnTop) { + return (T) new PinnedActivityStack(mDisplay, stackId, + mRootActivityContainer.mStackSupervisor, mOnTop) { @Override Rect getDefaultPictureInPictureBounds(float aspectRatio) { return new Rect(50, 50, 100, 100); @@ -796,7 +801,8 @@ class ActivityTestsBase { } }; } else { - return (T) new TestActivityStack(mDisplay, stackId, mSupervisor, mWindowingMode, + return (T) new TestActivityStack(mDisplay, stackId, + mRootActivityContainer.mStackSupervisor, mWindowingMode, mActivityType, mOnTop, mCreateActivity); } } diff --git a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java index a9071612a725..5556a150cf3a 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java @@ -19,12 +19,12 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions; + import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; - import android.platform.test.annotations.Presubmit; import androidx.test.filters.FlakyTest; diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index 5e12a950c560..5e12a950c560 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index f12619c6e337..577859cf2107 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -24,18 +24,18 @@ import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; + import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.spy; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.view.Display; -import android.view.IApplicationToken; import androidx.test.filters.SmallTest; @@ -111,16 +111,9 @@ public class AppTransitionTests extends WindowTestsBase { final WindowTestUtils.TestAppWindowToken token2 = createTestAppWindowToken(dc2, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - // Set TestAppWindowContainerController & assign first app token state to be good to go. - final WindowTestUtils.TestAppWindowContainerController controller1 = - createAppWindowController(dc1, token1.appToken); - final WindowTestUtils.TestAppWindowContainerController controller2 = - createAppWindowController(dc1, token2.appToken); - controller1.setContainer(token1); token1.allDrawn = true; token1.startingDisplayed = true; token1.startingMoved = true; - controller2.setContainer(token2); // Simulate activity resume / finish flows to prepare app transition & set visibility, // make sure transition is set as expected for each display. @@ -132,8 +125,8 @@ public class AppTransitionTests extends WindowTestsBase { assertEquals(TRANSIT_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransition()); // One activity window is visible for resuming & the other activity window is invisible // for finishing in different display. - controller1.setVisibility(true, false); - controller2.setVisibility(false, false); + token1.setVisibility(true, false); + token2.setVisibility(false, false); // Make sure each display is in animating stage. assertTrue(dc1.mOpeningApps.size() > 0); @@ -174,16 +167,4 @@ public class AppTransitionTests extends WindowTestsBase { assertFalse(dc1.mOpeningApps.contains(token1)); } - private WindowTestUtils.TestAppWindowContainerController createAppWindowController( - DisplayContent dc, IApplicationToken token) { - return createAppWindowController( - new WindowTestUtils.TestTaskWindowContainerController( - createStackControllerOnDisplay(dc)), token); - } - - private WindowTestUtils.TestAppWindowContainerController createAppWindowController( - WindowTestUtils.TestTaskWindowContainerController taskController, - IApplicationToken token) { - return new WindowTestUtils.TestAppWindowContainerController(taskController, token); - } } diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java index 4522494349a3..dcfb8797eaba 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java @@ -20,11 +20,12 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.SurfaceControl.Transaction; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; import android.view.SurfaceControl; diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index 552390d19419..8653bf96d984 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.content.ActivityInfoProto.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; @@ -31,13 +32,17 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_UNSET; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; import android.graphics.Point; import android.graphics.Rect; @@ -66,6 +71,8 @@ public class AppWindowTokenTests extends WindowTestsBase { Task mTask; WindowTestUtils.TestAppWindowToken mToken; + private final String mPackageName = getInstrumentation().getTargetContext().getPackageName(); + @Before public void setUp() throws Exception { mStack = createTaskStackOnDisplay(mDisplayContent); @@ -112,7 +119,8 @@ public class AppWindowTokenTests extends WindowTestsBase { assertEquals(window1, mToken.findMainWindow()); window1.mAnimatingExit = true; assertEquals(window1, mToken.findMainWindow()); - final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mToken, "window2"); + final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mToken, + "window2"); assertEquals(window2, mToken.findMainWindow()); mToken.removeImmediately(); } @@ -150,7 +158,7 @@ public class AppWindowTokenTests extends WindowTestsBase { mWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null, mDisplayContent.getDisplayId()); assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation()); - appWindow.resizeReported = false; + appWindow.mResizeReported = false; // Update the orientation to perform 180 degree rotation and check that resize was reported. mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE); @@ -158,7 +166,7 @@ public class AppWindowTokenTests extends WindowTestsBase { mDisplayContent.getDisplayId()); mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */); assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mDisplayContent.getLastOrientation()); - assertTrue(appWindow.resizeReported); + assertTrue(appWindow.mResizeReported); appWindow.removeImmediately(); } @@ -179,11 +187,11 @@ public class AppWindowTokenTests extends WindowTestsBase { // Set initial orientation and update. performRotation(spiedRotation, Surface.ROTATION_90); - appWindow.resizeReported = false; + appWindow.mResizeReported = false; // Update the rotation to perform 180 degree rotation and check that resize was reported. performRotation(spiedRotation, Surface.ROTATION_270); - assertTrue(appWindow.resizeReported); + assertTrue(appWindow.mResizeReported); appWindow.removeImmediately(); } @@ -215,7 +223,8 @@ public class AppWindowTokenTests extends WindowTestsBase { // Can not specify orientation if app isn't visible even though it fills parent. assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation()); // Can specify orientation if the current orientation candidate is orientation behind. - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation(SCREEN_ORIENTATION_BEHIND)); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, + mToken.getOrientation(SCREEN_ORIENTATION_BEHIND)); } @Test @@ -241,7 +250,8 @@ public class AppWindowTokenTests extends WindowTestsBase { // Finish relaunching and ensure flag is now not reported mToken.finishRelaunching(); - assertFalse(mToken.containsShowWhenLockedWindow() || mToken.containsDismissKeyguardWindow()); + assertFalse( + mToken.containsShowWhenLockedWindow() || mToken.containsDismissKeyguardWindow()); } @Test @@ -251,7 +261,7 @@ public class AppWindowTokenTests extends WindowTestsBase { "closingWindow"); closingWindow.mAnimatingExit = true; closingWindow.mRemoveOnExit = true; - closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET, + closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET, true /* performLayout */, false /* isVoiceInteraction */); // We pretended that we were running an exit animation, but that should have been cleared up @@ -261,6 +271,124 @@ public class AppWindowTokenTests extends WindowTestsBase { } @Test + public void testSetOrientation() { + // Assert orientation is unspecified to start. + assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mToken.getOrientation()); + + mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation()); + + mDisplayContent.removeAppToken(mToken.token); + // Assert orientation is unset to after container is removed. + assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation()); + + // Reset display frozen state + mWm.mDisplayFrozen = false; + } + + @Test + public void testCreateRemoveStartingWindow() { + mToken.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, + false, false); + waitUntilHandlersIdle(); + assertHasStartingWindow(mToken); + mToken.removeStartingWindow(); + waitUntilHandlersIdle(); + assertNoStartingWindow(mToken); + } + + @Test + public void testAddRemoveRace() { + // There was once a race condition between adding and removing starting windows + for (int i = 0; i < 1000; i++) { + final WindowTestUtils.TestAppWindowToken appToken = createIsolatedTestAppWindowToken(); + + appToken.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, + false, false); + appToken.removeStartingWindow(); + waitUntilHandlersIdle(); + assertNoStartingWindow(appToken); + + appToken.getParent().getParent().removeImmediately(); + } + } + + @Test + public void testTransferStartingWindow() { + final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken(); + final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken(); + token1.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, + false, false); + waitUntilHandlersIdle(); + token2.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, token1.appToken.asBinder(), + true, true, false, true, false, false); + waitUntilHandlersIdle(); + assertNoStartingWindow(token1); + assertHasStartingWindow(token2); + } + + @Test + public void testTransferStartingWindowWhileCreating() { + final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken(); + final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken(); + ((TestWindowManagerPolicy) token1.mService.mPolicy).setRunnableWhenAddingSplashScreen( + () -> { + // Surprise, ...! Transfer window in the middle of the creation flow. + token2.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, + token1.appToken.asBinder(), true, true, false, + true, false, false); + }); + token1.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, + false, false); + waitUntilHandlersIdle(); + assertNoStartingWindow(token1); + assertHasStartingWindow(token2); + } + + private WindowTestUtils.TestAppWindowToken createIsolatedTestAppWindowToken() { + final TaskStack taskStack = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(taskStack, 0 /* userId */); + return createTestAppWindowTokenForGivenTask(task); + } + + private WindowTestUtils.TestAppWindowToken createTestAppWindowTokenForGivenTask(Task task) { + final WindowTestUtils.TestAppWindowToken appToken = + WindowTestUtils.createTestAppWindowToken(mDisplayContent); + task.addChild(appToken, 0); + waitUntilHandlersIdle(); + return appToken; + } + + @Test + public void testTryTransferStartingWindowFromHiddenAboveToken() { + // Add two tasks on top of each other. + final WindowTestUtils.TestAppWindowToken tokenTop = createIsolatedTestAppWindowToken(); + final WindowTestUtils.TestAppWindowToken tokenBottom = + createTestAppWindowTokenForGivenTask(tokenTop.getTask()); + + // Add a starting window. + tokenTop.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, + false, false); + waitUntilHandlersIdle(); + + // Make the top one invisible, and try transferring the starting window from the top to the + // bottom one. + tokenTop.setVisibility(false, false); + tokenBottom.transferStartingWindowFromHiddenAboveTokenIfNeeded(); + + // Assert that the bottom window now has the starting window. + assertNoStartingWindow(tokenTop); + assertHasStartingWindow(tokenBottom); + } + + @Test public void testTransitionAnimationPositionAndBounds() { final Rect stackBounds = new Rect( 0/* left */, 0 /* top */, 1000 /* right */, 1000 /* bottom */); @@ -285,4 +413,19 @@ public class AppWindowTokenTests extends WindowTestsBase { assertEquals(expectedY, outPosition.y); assertEquals(expectedBounds, outBounds); } + + private void assertHasStartingWindow(AppWindowToken atoken) { + assertNotNull(atoken.startingSurface); + assertNotNull(atoken.startingData); + assertNotNull(atoken.startingWindow); + } + + private void assertNoStartingWindow(AppWindowToken atoken) { + assertNull(atoken.startingSurface); + assertNull(atoken.startingWindow); + assertNull(atoken.startingData); + atoken.forAllWindows(windowState -> { + assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING); + }, true); + } } diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java index 1c5391ed3a6c..1c5391ed3a6c 100644 --- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java index 991981f62d30..ee1c8dfdd319 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java @@ -16,16 +16,17 @@ package com.android.server.wm; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; @@ -145,7 +146,7 @@ public class DimmerTests extends WindowTestsBase { } @Test - public void testUpdateDimsAppliesSize() { + public void testUpdateDimsAppliesCrop() { mDimmer.dimAbove(mTransaction, 0.8f); int width = 100; @@ -153,7 +154,7 @@ public class DimmerTests extends WindowTestsBase { Rect bounds = new Rect(0, 0, width, height); mDimmer.updateDims(mTransaction, bounds); - verify(mTransaction).setSize(getDimLayer(), width, height); + verify(mTransaction).setWindowCrop(getDimLayer(), width, height); verify(mTransaction).show(getDimLayer()); } @@ -242,13 +243,13 @@ public class DimmerTests extends WindowTestsBase { SurfaceControl dimLayer = getDimLayer(); bounds.set(0, 0, 10, 10); mDimmer.updateDims(mTransaction, bounds); + verify(mTransaction).setWindowCrop(dimLayer, bounds.width(), bounds.height()); verify(mTransaction, times(1)).show(dimLayer); - verify(mTransaction).setSize(dimLayer, bounds.width(), bounds.height()); verify(mTransaction).setPosition(dimLayer, 0, 0); bounds.set(10, 10, 30, 30); mDimmer.updateDims(mTransaction, bounds); - verify(mTransaction).setSize(dimLayer, bounds.width(), bounds.height()); + verify(mTransaction).setWindowCrop(dimLayer, bounds.width(), bounds.height()); verify(mTransaction).setPosition(dimLayer, 10, 10); } diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 43e10f0a5f06..3b8d71dd8176 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -29,6 +29,10 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; @@ -39,10 +43,6 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; import android.annotation.SuppressLint; import android.content.res.Configuration; diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java index 18bd2e45acd4..6767465f838c 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java @@ -28,14 +28,11 @@ import android.platform.test.annotations.Presubmit; import android.view.DisplayInfo; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ErrorCollector; -import org.junit.runner.RunWith; -@RunWith(AndroidJUnit4.class) @SmallTest @Presubmit public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase { diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index a91c5e79ccfc..b94f472965ab 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -46,15 +46,12 @@ 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 { diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index 07d5fea334cd..8349ac7fc62c 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -42,12 +42,9 @@ 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 { diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java index 1d63c57e6cfe..1d63c57e6cfe 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java new file mode 100644 index 000000000000..e9889948c341 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -0,0 +1,823 @@ +/* + * 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.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.atMost; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.same; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.PowerManagerInternal; +import android.os.SystemClock; +import android.platform.test.annotations.Presubmit; +import android.provider.Settings; +import android.view.Surface; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import com.android.internal.util.test.FakeSettingsProvider; +import com.android.server.LocalServices; +import com.android.server.UiThread; +import com.android.server.policy.WindowManagerPolicy; +import com.android.server.statusbar.StatusBarManagerInternal; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Test class for {@link DisplayRotation}. + * + * Build/Install/Run: + * atest WmTests:DisplayRotationTests + */ +@SmallTest +@Presubmit +@FlakyTest(detail = "Confirm stable in post-submit before removing") +public class DisplayRotationTests { + private static final long UI_HANDLER_WAIT_TIMEOUT_MS = 50; + + private StatusBarManagerInternal mPreviousStatusBarManagerInternal; + + private WindowManagerService mMockWm; + private DisplayContent mMockDisplayContent; + private DisplayPolicy mMockDisplayPolicy; + private Context mMockContext; + private Resources mMockRes; + private SensorManager mMockSensorManager; + private Sensor mFakeSensor; + private DisplayWindowSettings mMockDisplayWindowSettings; + private ContentResolver mMockResolver; + private FakeSettingsProvider mFakeSettingsProvider; + private StatusBarManagerInternal mMockStatusBarManagerInternal; + + // Fields below are callbacks captured from test target. + private ContentObserver mShowRotationSuggestionsObserver; + private ContentObserver mAccelerometerRotationObserver; + private ContentObserver mUserRotationObserver; + private SensorEventListener mOrientationSensorListener; + + private DisplayRotationBuilder mBuilder; + + private DisplayRotation mTarget; + + @Before + public void setUp() { + FakeSettingsProvider.clearSettingsProvider(); + + mMockWm = mock(WindowManagerService.class); + mMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class); + + mPreviousStatusBarManagerInternal = LocalServices.getService( + StatusBarManagerInternal.class); + LocalServices.removeServiceForTest(StatusBarManagerInternal.class); + mMockStatusBarManagerInternal = mock(StatusBarManagerInternal.class); + LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal); + + mBuilder = new DisplayRotationBuilder(); + } + + @After + public void tearDown() { + LocalServices.removeServiceForTest(StatusBarManagerInternal.class); + if (mPreviousStatusBarManagerInternal != null) { + LocalServices.addService(StatusBarManagerInternal.class, + mPreviousStatusBarManagerInternal); + mPreviousStatusBarManagerInternal = null; + } + } + + // ================================ + // Display Settings Related Tests + // ================================ + @Test + public void testLocksUserRotation_LockRotation_DefaultDisplay() throws Exception { + mBuilder.build(); + + freezeRotation(Surface.ROTATION_180); + + assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode()); + assertEquals(Surface.ROTATION_180, mTarget.getUserRotation()); + + assertEquals(0, Settings.System.getInt(mMockResolver, + Settings.System.ACCELEROMETER_ROTATION)); + assertEquals(Surface.ROTATION_180, Settings.System.getInt(mMockResolver, + Settings.System.USER_ROTATION)); + } + + @Test + public void testPersistsUserRotation_LockRotation_NonDefaultDisplay() throws Exception { + mBuilder.mIsDefaultDisplay = false; + + mBuilder.build(); + + freezeRotation(Surface.ROTATION_180); + + assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode()); + assertEquals(Surface.ROTATION_180, mTarget.getUserRotation()); + + verify(mMockDisplayWindowSettings).setUserRotation(mMockDisplayContent, + WindowManagerPolicy.USER_ROTATION_LOCKED, Surface.ROTATION_180); + } + + @Test + public void testPersistUserRotation_UnlockRotation_DefaultDisplay() throws Exception { + mBuilder.build(); + + thawRotation(); + + assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, mTarget.getUserRotationMode()); + + assertEquals(1, Settings.System.getInt(mMockResolver, + Settings.System.ACCELEROMETER_ROTATION)); + } + + @Test + public void testPersistsUserRotation_UnlockRotation_NonDefaultDisplay() throws Exception { + mBuilder.mIsDefaultDisplay = false; + + mBuilder.build(); + + thawRotation(); + + assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, mTarget.getUserRotationMode()); + + verify(mMockDisplayWindowSettings).setUserRotation(same(mMockDisplayContent), + eq(WindowManagerPolicy.USER_ROTATION_FREE), anyInt()); + } + + @Test + public void testPersistsFixedToUserRotation() throws Exception { + mBuilder.build(); + + mTarget.setFixedToUserRotation(true); + + verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, true); + + reset(mMockDisplayWindowSettings); + mTarget.setFixedToUserRotation(false); + + verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, false); + } + + // ======================================== + // Tests for User Rotation based Rotation + // ======================================== + @Test + public void testReturnsUserRotation_UserRotationLocked_NoAppRequest() + throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + freezeRotation(Surface.ROTATION_180); + + assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation( + ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90)); + } + + @Test + public void testReturnsUserRotation_UserRotationLocked_CompatibleAppRequest() + throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + freezeRotation(Surface.ROTATION_180); + + assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation( + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90)); + } + + @Test + public void testReturnsSidesays_UserRotationLocked_IncompatibleAppRequest() + throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + freezeRotation(Surface.ROTATION_180); + + final int rotation = mTarget.rotationForOrientation( + ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, Surface.ROTATION_90); + assertTrue("Rotation should be sideways, but it's " + + Surface.rotationToString(rotation), + rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270); + } + + // ================================= + // Tests for Sensor based Rotation + // ================================= + private void verifyOrientationListenerRegistration(int numOfInvocation) { + final ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass( + SensorEventListener.class); + verify(mMockSensorManager, times(numOfInvocation)).registerListener( + listenerCaptor.capture(), + same(mFakeSensor), + anyInt(), + any()); + if (numOfInvocation > 0) { + mOrientationSensorListener = listenerCaptor.getValue(); + } + } + + @Test + public void testNotEnablesSensor_AutoRotationNotSupported() throws Exception { + mBuilder.setSupportAutoRotation(false).build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + thawRotation(); + + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); + when(mMockDisplayPolicy.isAwake()).thenReturn(true); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(0); + } + + @Test + public void testNotEnablesSensor_ScreenNotOn() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + thawRotation(); + + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(false); + when(mMockDisplayPolicy.isAwake()).thenReturn(true); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(0); + } + + @Test + public void testNotEnablesSensor_NotAwake() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + thawRotation(); + + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); + when(mMockDisplayPolicy.isAwake()).thenReturn(false); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(0); + } + + @Test + public void testNotEnablesSensor_KeyguardNotDrawnCompletely() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + thawRotation(); + + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); + when(mMockDisplayPolicy.isAwake()).thenReturn(true); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(false); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(0); + } + + @Test + public void testNotEnablesSensor_WindowManagerNotDrawnCompletely() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + thawRotation(); + + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); + when(mMockDisplayPolicy.isAwake()).thenReturn(true); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(false); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(0); + } + + @Test + public void testNotEnablesSensor_FixedUserRotation() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); + when(mMockDisplayPolicy.isAwake()).thenReturn(true); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); + mTarget.setFixedToUserRotation(true); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(0); + } + + @Test + public void testNotEnablesSensor_ForceDefaultRotation() throws Exception { + mBuilder.build(); + when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation)) + .thenReturn(true); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); + when(mMockDisplayPolicy.isAwake()).thenReturn(true); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(0); + } + + @Test + public void testNotEnablesSensor_ForceDefaultRotation_Car() throws Exception { + mBuilder.build(); + when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation)) + .thenReturn(true); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false); + + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); + when(mMockDisplayPolicy.isAwake()).thenReturn(true); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(0); + } + + @Test + public void testNotEnablesSensor_ForceDefaultRotation_Tv() throws Exception { + mBuilder.build(); + when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation)) + .thenReturn(true); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true); + + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); + when(mMockDisplayPolicy.isAwake()).thenReturn(true); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(0); + } + + private void enableOrientationSensor() { + when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); + when(mMockDisplayPolicy.isAwake()).thenReturn(true); + when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); + when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); + mTarget.updateOrientationListener(); + verifyOrientationListenerRegistration(1); + } + + private SensorEvent createSensorEvent(int rotation) throws Exception { + final Constructor<SensorEvent> constructor = + SensorEvent.class.getDeclaredConstructor(int.class); + constructor.setAccessible(true); + final SensorEvent event = constructor.newInstance(1); + event.sensor = mFakeSensor; + event.values[0] = rotation; + event.timestamp = SystemClock.elapsedRealtimeNanos(); + return event; + } + + @Test + public void testReturnsSensorRotation_RotationThawed() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + thawRotation(); + + enableOrientationSensor(); + + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90)); + + assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation( + SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); + } + + private boolean waitForUiHandler() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + UiThread.getHandler().post(latch::countDown); + return latch.await(UI_HANDLER_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + + @Test + public void testUpdatesRotationWhenSensorUpdates_RotationThawed() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + thawRotation(); + + enableOrientationSensor(); + + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90)); + assertTrue(waitForUiHandler()); + + verify(mMockWm).updateRotation(false, false); + } + + @Test + public void testNotifiesChoiceWhenSensorUpdates_RotationLocked() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + freezeRotation(Surface.ROTATION_270); + + enableOrientationSensor(); + + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90)); + assertTrue(waitForUiHandler()); + + verify(mMockStatusBarManagerInternal).onProposedRotationChanged(Surface.ROTATION_90, true); + } + + @Test + public void testReturnsCompatibleRotation_SensorEnabled_RotationThawed() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + thawRotation(); + + enableOrientationSensor(); + + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180)); + + final int rotation = mTarget.rotationForOrientation(SCREEN_ORIENTATION_LANDSCAPE, + Surface.ROTATION_0); + assertTrue("Rotation should be sideways but it's " + + Surface.rotationToString(rotation), + rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270); + } + + @Test + public void testReturnsUserRotation_SensorEnabled_RotationLocked() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + freezeRotation(Surface.ROTATION_270); + + enableOrientationSensor(); + + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180)); + + assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation( + SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); + } + + // ================================= + // Tests for Policy based Rotation + // ================================= + @Test + public void testReturnsUserRotation_ForceDefaultRotation() throws Exception { + mBuilder.build(); + when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation)) + .thenReturn(true); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT, + Surface.ROTATION_180)); + } + + @Test + public void testReturnsUserRotation_ForceDefaultRotation_Car() throws Exception { + mBuilder.build(); + when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation)) + .thenReturn(true); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false); + + assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT, + Surface.ROTATION_180)); + } + + @Test + public void testReturnsUserRotation_ForceDefaultRotation_Tv() throws Exception { + mBuilder.build(); + when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation)) + .thenReturn(true); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true); + + assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT, + Surface.ROTATION_180)); + } + + @Test + public void testReturnsLidOpenRotation_LidOpen() throws Exception { + mBuilder.setLidOpenRotation(Surface.ROTATION_90).build(); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + when(mMockDisplayPolicy.getLidState()).thenReturn( + WindowManagerPolicy.WindowManagerFuncs.LID_OPEN); + + freezeRotation(Surface.ROTATION_270); + + assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation( + SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); + } + + @Test + public void testReturnsCarDockRotation_CarDockedMode() throws Exception { + mBuilder.setCarDockRotation(Surface.ROTATION_270).build(); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_CAR); + + freezeRotation(Surface.ROTATION_90); + + assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation( + SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90)); + } + + @Test + public void testReturnsDeskDockRotation_DeskDockedMode() throws Exception { + mBuilder.setDeskDockRotation(Surface.ROTATION_270).build(); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_DESK); + + freezeRotation(Surface.ROTATION_90); + + assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation( + SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90)); + } + + @Test + public void testReturnsUserRotation_FixedToUserRotation_IgnoreIncompatibleAppRequest() + throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + mTarget.setFixedToUserRotation(true); + + freezeRotation(Surface.ROTATION_180); + + final int rotation = mTarget.rotationForOrientation( + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90); + assertEquals(Surface.ROTATION_180, rotation); + } + + @Test + public void testReturnsUserRotation_NonDefaultDisplay() throws Exception { + mBuilder.setIsDefaultDisplay(false).build(); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + + freezeRotation(Surface.ROTATION_90); + + assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation( + SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); + } + + /** + * Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget} + * according to given parameters. + */ + private void configureDisplayRotation(int displayOrientation, boolean isCar, boolean isTv) { + final int width; + final int height; + switch (displayOrientation) { + case SCREEN_ORIENTATION_LANDSCAPE: + width = 1920; + height = 1080; + break; + case SCREEN_ORIENTATION_PORTRAIT: + width = 1080; + height = 1920; + break; + default: + throw new IllegalArgumentException("displayOrientation needs to be either landscape" + + " or portrait, but we got " + + ActivityInfo.screenOrientationToString(displayOrientation)); + } + + final PackageManager mockPackageManager = mock(PackageManager.class); + when(mMockContext.getPackageManager()).thenReturn(mockPackageManager); + when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) + .thenReturn(isCar); + when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) + .thenReturn(isTv); + + final int shortSizeDp = (isCar || isTv) ? 540 : 720; + final int longSizeDp = 960; + mTarget.configure(width, height, shortSizeDp, longSizeDp); + } + + private void freezeRotation(int rotation) { + mTarget.freezeRotation(rotation); + + if (mTarget.isDefaultDisplay) { + mAccelerometerRotationObserver.onChange(false); + mUserRotationObserver.onChange(false); + } + } + + private void thawRotation() { + mTarget.thawRotation(); + + if (mTarget.isDefaultDisplay) { + mAccelerometerRotationObserver.onChange(false); + mUserRotationObserver.onChange(false); + } + } + + private class DisplayRotationBuilder { + private boolean mIsDefaultDisplay = true; + private boolean mSupportAutoRotation = true; + + private int mLidOpenRotation = WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT; + private int mCarDockRotation; + private int mDeskDockRotation; + private int mUndockedHdmiRotation; + + private DisplayRotationBuilder setIsDefaultDisplay(boolean isDefaultDisplay) { + mIsDefaultDisplay = isDefaultDisplay; + return this; + } + + private DisplayRotationBuilder setSupportAutoRotation(boolean supportAutoRotation) { + mSupportAutoRotation = supportAutoRotation; + return this; + } + + private DisplayRotationBuilder setLidOpenRotation(int rotation) { + mLidOpenRotation = rotation; + return this; + } + + private DisplayRotationBuilder setCarDockRotation(int rotation) { + mCarDockRotation = rotation; + return this; + } + + private DisplayRotationBuilder setDeskDockRotation(int rotation) { + mDeskDockRotation = rotation; + return this; + } + + private DisplayRotationBuilder setUndockedHdmiRotation(int rotation) { + mUndockedHdmiRotation = rotation; + return this; + } + + private void captureObservers() { + ArgumentCaptor<ContentObserver> captor = ArgumentCaptor.forClass( + ContentObserver.class); + verify(mMockResolver, atMost(1)).registerContentObserver( + eq(Settings.Secure.getUriFor(Settings.Secure.SHOW_ROTATION_SUGGESTIONS)), + anyBoolean(), + captor.capture(), + anyInt()); + if (!captor.getAllValues().isEmpty()) { + mShowRotationSuggestionsObserver = captor.getValue(); + } + + captor = ArgumentCaptor.forClass(ContentObserver.class); + verify(mMockResolver, atMost(1)).registerContentObserver( + eq(Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION)), + anyBoolean(), + captor.capture(), + anyInt()); + if (!captor.getAllValues().isEmpty()) { + mAccelerometerRotationObserver = captor.getValue(); + } + + captor = ArgumentCaptor.forClass(ContentObserver.class); + verify(mMockResolver, atMost(1)).registerContentObserver( + eq(Settings.System.getUriFor(Settings.System.USER_ROTATION)), + anyBoolean(), + captor.capture(), + anyInt()); + if (!captor.getAllValues().isEmpty()) { + mUserRotationObserver = captor.getValue(); + } + } + + private Sensor createSensor(int type) throws Exception { + Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor(); + constr.setAccessible(true); + Sensor sensor = constr.newInstance(); + + setSensorType(sensor, type); + setSensorField(sensor, "mName", "Mock " + sensor.getStringType() + "/" + type); + setSensorField(sensor, "mVendor", "Mock Vendor"); + setSensorField(sensor, "mVersion", 1); + setSensorField(sensor, "mHandle", -1); + setSensorField(sensor, "mMaxRange", 10); + setSensorField(sensor, "mResolution", 1); + setSensorField(sensor, "mPower", 1); + setSensorField(sensor, "mMinDelay", 1000); + setSensorField(sensor, "mMaxDelay", 1000000000); + setSensorField(sensor, "mFlags", 0); + setSensorField(sensor, "mId", -1); + + return sensor; + } + + private void setSensorType(Sensor sensor, int type) throws Exception { + Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE); + setter.setAccessible(true); + setter.invoke(sensor, type); + } + + private void setSensorField(Sensor sensor, String fieldName, Object value) + throws Exception { + Field field = Sensor.class.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(sensor, value); + } + + private int convertRotationToDegrees(@Surface.Rotation int rotation) { + switch (rotation) { + case Surface.ROTATION_0: + return 0; + case Surface.ROTATION_90: + return 90; + case Surface.ROTATION_180: + return 180; + case Surface.ROTATION_270: + return 270; + default: + return -1; + } + } + + private void build() throws Exception { + mMockContext = mock(Context.class); + + mMockDisplayContent = mock(WindowTestUtils.TestDisplayContent.class); + mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay; + + mMockDisplayPolicy = mock(DisplayPolicy.class); + + mMockRes = mock(Resources.class); + when(mMockContext.getResources()).thenReturn((mMockRes)); + when(mMockRes.getBoolean(com.android.internal.R.bool.config_supportAutoRotation)) + .thenReturn(mSupportAutoRotation); + when(mMockRes.getInteger(com.android.internal.R.integer.config_lidOpenRotation)) + .thenReturn(convertRotationToDegrees(mLidOpenRotation)); + when(mMockRes.getInteger(com.android.internal.R.integer.config_carDockRotation)) + .thenReturn(convertRotationToDegrees(mCarDockRotation)); + when(mMockRes.getInteger(com.android.internal.R.integer.config_deskDockRotation)) + .thenReturn(convertRotationToDegrees(mDeskDockRotation)); + when(mMockRes.getInteger(com.android.internal.R.integer.config_undockedHdmiRotation)) + .thenReturn(convertRotationToDegrees(mUndockedHdmiRotation)); + + mMockSensorManager = mock(SensorManager.class); + when(mMockContext.getSystemService(Context.SENSOR_SERVICE)) + .thenReturn(mMockSensorManager); + mFakeSensor = createSensor(Sensor.TYPE_DEVICE_ORIENTATION); + when(mMockSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION)).thenReturn( + Collections.singletonList(mFakeSensor)); + + mMockResolver = mock(ContentResolver.class); + when(mMockContext.getContentResolver()).thenReturn(mMockResolver); + mFakeSettingsProvider = new FakeSettingsProvider(); + when(mMockResolver.acquireProvider(Settings.AUTHORITY)) + .thenReturn(mFakeSettingsProvider.getIContentProvider()); + + mMockDisplayWindowSettings = mock(DisplayWindowSettings.class); + mTarget = new DisplayRotation(mMockWm, mMockDisplayContent, mMockDisplayPolicy, + mMockDisplayWindowSettings, mMockContext, new Object()); + + captureObservers(); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java index b823e706a586..8e881b54c422 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java @@ -21,13 +21,19 @@ import static android.view.WindowManager.REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.doAnswer; +import static org.mockito.Matchers.eq; import android.app.WindowConfiguration; import android.platform.test.annotations.Presubmit; @@ -378,6 +384,33 @@ public class DisplayWindowSettingsTests extends WindowTestsBase { mSecondaryDisplay.getDisplayRotation().getUserRotation()); } + @Test + public void testNotFixedToUserRotationByDefault() { + mTarget.setUserRotation(mPrimaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED, + Surface.ROTATION_0); + + final DisplayRotation displayRotation = mock(DisplayRotation.class); + mPrimaryDisplay = spy(mPrimaryDisplay); + when(mPrimaryDisplay.getDisplayRotation()).thenReturn(displayRotation); + + mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); + + verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(false)); + } + + @Test + public void testSetFixedToUserRotation() { + mTarget.setFixedToUserRotation(mPrimaryDisplay, true); + + final DisplayRotation displayRotation = mock(DisplayRotation.class); + mPrimaryDisplay = spy(mPrimaryDisplay); + when(mPrimaryDisplay.getDisplayRotation()).thenReturn(displayRotation); + + applySettingsToDisplayByNewInstance(mPrimaryDisplay); + + verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(true)); + } + private static void assertOverscan(DisplayContent display, int left, int top, int right, int bottom) { final DisplayInfo info = display.getDisplayInfo(); diff --git a/services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java index a04bf16eaf1c..32062080a22c 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java @@ -30,12 +30,9 @@ 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 { diff --git a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java index 55e766da09fc..f1c6eab2143d 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java @@ -20,13 +20,14 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; import android.content.ClipData; import android.graphics.PixelFormat; @@ -171,7 +172,7 @@ public class DragDropControllerTests extends WindowTestsBase { try { final SurfaceControl surface = new SurfaceControl.Builder(appSession) .setName("drag surface") - .setSize(100, 100) + .setBufferSize(100, 100) .setFormat(PixelFormat.TRANSLUCENT) .build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java new file mode 100644 index 000000000000..c11e606386e6 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java @@ -0,0 +1,76 @@ +/* + * 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.InsetsState.TYPE_TOP_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; + +import static org.junit.Assert.assertEquals; + +import android.graphics.Insets; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.view.InsetsSource; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +@SmallTest +@FlakyTest(detail = "Promote once confirmed non-flaky") +@Presubmit +public class InsetsSourceProviderTest extends WindowTestsBase { + + private InsetsSourceProvider mProvider = new InsetsSourceProvider( + new InsetsSource(TYPE_TOP_BAR)); + + @Test + public void testPostLayout() { + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + topBar.getFrameLw().set(0, 0, 500, 100); + topBar.mHasSurface = true; + mProvider.setWindow(topBar, null); + mProvider.onPostLayout(); + assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame()); + assertEquals(Insets.of(0, 100, 0, 0), + mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500), + false /* ignoreVisibility */)); + } + + @Test + public void testPostLayout_invisible() { + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + topBar.getFrameLw().set(0, 0, 500, 100); + mProvider.setWindow(topBar, null); + mProvider.onPostLayout(); + assertEquals(Insets.NONE, mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500), + false /* ignoreVisibility */)); + } + + @Test + public void testPostLayout_frameProvider() { + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + topBar.getFrameLw().set(0, 0, 500, 100); + mProvider.setWindow(topBar, + (displayFrames, windowState, rect) -> { + rect.set(10, 10, 20, 20); + }); + mProvider.onPostLayout(); + assertEquals(new Rect(10, 10, 20, 20), mProvider.getSource().getFrame()); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java new file mode 100644 index 000000000000..331622ce22a5 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -0,0 +1,77 @@ +/* + * 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.InsetsState.TYPE_IME; +import static android.view.InsetsState.TYPE_NAVIGATION_BAR; +import static android.view.InsetsState.TYPE_TOP_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import android.platform.test.annotations.Presubmit; +import android.view.InsetsState; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +@SmallTest +@FlakyTest(detail = "Promote once confirmed non-flaky") +@Presubmit +public class InsetsStateControllerTest extends WindowTestsBase { + + @Test + public void testStripForDispatch_notOwn() { + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow"); + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR) + .setWindow(topBar, null); + topBar.setInsetProvider( + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)); + assertNotNull(mDisplayContent.getInsetsStateController().getInsetsForDispatch(app) + .getSource(TYPE_TOP_BAR)); + } + + @Test + public void testStripForDispatch_own() { + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR) + .setWindow(topBar, null); + topBar.setInsetProvider( + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)); + assertEquals(new InsetsState(), + mDisplayContent.getInsetsStateController().getInsetsForDispatch(topBar)); + } + + @Test + public void testStripForDispatch_navBar() { + final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState ime = createWindow(null, TYPE_APPLICATION, "parentWindow"); + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR) + .setWindow(topBar, null); + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_NAVIGATION_BAR) + .setWindow(navBar, null); + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_IME) + .setWindow(ime, null); + assertEquals(new InsetsState(), + mDisplayContent.getInsetsStateController().getInsetsForDispatch(navBar)); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java index dc22bc1d5820..f3a125bf79e4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java @@ -110,8 +110,9 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { final DisplayInfo info = new DisplayInfo(); info.uniqueId = mDisplayUniqueId; mTestDisplay = createNewActivityDisplay(info); - mSupervisor.addChild(mTestDisplay, ActivityDisplay.POSITION_TOP); - when(mSupervisor.getActivityDisplay(eq(mDisplayUniqueId))).thenReturn(mTestDisplay); + mRootActivityContainer.addChild(mTestDisplay, ActivityDisplay.POSITION_TOP); + when(mRootActivityContainer.getActivityDisplay(eq(mDisplayUniqueId))) + .thenReturn(mTestDisplay); ActivityStack stack = mTestDisplay.createStack(TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true); @@ -184,7 +185,7 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { public void testReturnsEmptyDisplayIfDisplayIsNotFound() { mTarget.saveTask(mTestTask); - when(mSupervisor.getActivityDisplay(eq(mDisplayUniqueId))).thenReturn(null); + when(mRootActivityContainer.getActivityDisplay(eq(mDisplayUniqueId))).thenReturn(null); mTarget.getLaunchParams(mTestTask, null, mResult); @@ -271,6 +272,51 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { } @Test + public void testClearsRecordInMemory() { + mTarget.saveTask(mTestTask); + + mTarget.removeRecordForPackage(TEST_COMPONENT.getPackageName()); + + mTarget.getLaunchParams(mTestTask, null, mResult); + + assertTrue("Result should be empty.", mResult.isEmpty()); + } + + @Test + public void testClearsWriteQueueItem() { + mTarget.saveTask(mTestTask); + + mTarget.removeRecordForPackage(TEST_COMPONENT.getPackageName()); + + final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor, + mUserFolderGetter); + target.onSystemReady(); + target.onUnlockUser(TEST_USER_ID); + + target.getLaunchParams(mTestTask, null, mResult); + + assertTrue("Result should be empty.", mResult.isEmpty()); + } + + @Test + public void testClearsFile() { + mTarget.saveTask(mTestTask); + mPersisterQueue.flush(); + + mTarget.removeRecordForPackage(TEST_COMPONENT.getPackageName()); + + final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor, + mUserFolderGetter); + target.onSystemReady(); + target.onUnlockUser(TEST_USER_ID); + + target.getLaunchParams(mTestTask, null, mResult); + + assertTrue("Result should be empty.", mResult.isEmpty()); + } + + + @Test public void testClearsRecordInMemoryOnPackageUninstalled() { mTarget.saveTask(mTestTask); diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java index 33e6063bbb97..6259fa669ce8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java @@ -104,6 +104,7 @@ public class LockTaskControllerTest { new DexmakerShareClassLoaderRule(); @Mock private ActivityStackSupervisor mSupervisor; + @Mock private RootActivityContainer mRootActivityContainer; @Mock private IDevicePolicyManager mDevicePolicyManager; @Mock private IStatusBarService mStatusBarService; @Mock private WindowManagerService mWindowManager; @@ -129,6 +130,7 @@ public class LockTaskControllerTest { } mSupervisor.mRecentTasks = mRecentTasks; + mSupervisor.mRootActivityContainer = mRootActivityContainer; mLockTaskController = new LockTaskController(mContext, mSupervisor, new ImmediatelyExecuteHandler()); diff --git a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java index 1fae317f3af8..63d9fb9c17e9 100644 --- a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java @@ -18,14 +18,15 @@ package com.android.server.wm; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; @@ -46,6 +47,8 @@ import org.mockito.MockitoAnnotations; @Presubmit public class PinnedStackControllerTest extends WindowTestsBase { + private static final int SHELF_HEIGHT = 300; + @Mock private IPinnedStackListener mIPinnedStackListener; @Mock private IPinnedStackListener.Stub mIPinnedStackListenerStub; @@ -70,8 +73,6 @@ public class PinnedStackControllerTest extends WindowTestsBase { reset(mIPinnedStackListener); - final int SHELF_HEIGHT = 300; - mWm.setShelfHeight(true, SHELF_HEIGHT); verify(mIPinnedStackListener).onShelfVisibilityChanged(true, SHELF_HEIGHT); verify(mIPinnedStackListener).onMovementBoundsChanged(any(), any(), any(), eq(false), diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 8596c77bc73a..3c7b4b1248b5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -113,9 +113,10 @@ public class RecentTasksTest extends ActivityTestsBase { mTestService = new MyTestActivityTaskManagerService(mContext); mRecentTasks = (TestRecentTasks) mTestService.getRecentTasks(); mRecentTasks.loadParametersFromResources(mContext.getResources()); - mHomeStack = mTestService.mStackSupervisor.getDefaultDisplay().getOrCreateStack( + mRunningTasks = (TestRunningTasks) mTestService.mStackSupervisor.mRunningTasks; + mHomeStack = mTestService.mRootActivityContainer.getDefaultDisplay().getOrCreateStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - mStack = mTestService.mStackSupervisor.getDefaultDisplay().createStack( + mStack = mTestService.mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); mCallbacksRecorder = new CallbacksRecorder(); mRecentTasks.registerCallback(mCallbacksRecorder); @@ -872,6 +873,15 @@ public class RecentTasksTest extends ActivityTestsBase { } return mTestStackSupervisor; } + + @Override + void initRootActivityContainerMocks(WindowManagerService wm) { + super.initRootActivityContainerMocks(wm); + mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY); + mOtherDisplay = TestActivityDisplay.create(mTestStackSupervisor, DEFAULT_DISPLAY + 1); + mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP); + mRootActivityContainer.addChild(mDisplay, ActivityDisplay.POSITION_TOP); + } } private class MyTestActivityStackSupervisor extends TestActivityStackSupervisor { @@ -880,15 +890,6 @@ public class RecentTasksTest extends ActivityTestsBase { } @Override - public void initialize() { - super.initialize(); - mDisplay = getActivityDisplay(DEFAULT_DISPLAY); - mOtherDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY + 1); - addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP); - addChild(mDisplay, ActivityDisplay.POSITION_TOP); - } - - @Override RunningTasks createRunningTasks() { mRunningTasks = new TestRunningTasks(); return mRunningTasks; diff --git a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index ee3bba73cd1e..cc6a58a81635 100644 --- a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -21,6 +21,10 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; @@ -28,10 +32,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; import android.os.Binder; import android.os.IInterface; diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java index 070f0731ba2f..0ff67d7b1f83 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -67,9 +67,9 @@ public class RecentsAnimationTest extends ActivityTestsBase { @Test public void testCancelAnimationOnStackOrderChange() { ActivityStack fullscreenStack = - mService.mStackSupervisor.getDefaultDisplay().createStack( + mService.mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - ActivityStack recentsStack = mService.mStackSupervisor.getDefaultDisplay().createStack( + ActivityStack recentsStack = mService.mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */); ActivityRecord recentsActivity = new ActivityBuilder(mService) .setComponent(mRecentsComponent) @@ -77,7 +77,7 @@ public class RecentsAnimationTest extends ActivityTestsBase { .setStack(recentsStack) .build(); ActivityStack fullscreenStack2 = - mService.mStackSupervisor.getDefaultDisplay().createStack( + mService.mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); ActivityRecord fsActivity = new ActivityBuilder(mService) .setComponent(new ComponentName(mContext.getPackageName(), "App1")) diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java index fa5379576e8b..ad2a708b88d9 100644 --- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -18,13 +18,14 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; import android.graphics.Point; import android.graphics.Rect; @@ -143,8 +144,9 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { @Test public void testTimeout_scaled() throws Exception { mWm.setAnimationScale(2, 5.0f); - try{ - final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); + try { + final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, + "testWin"); final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken, new Point(50, 100), new Rect(50, 100, 150, 150)); adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); @@ -163,7 +165,6 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { } finally { mWm.setAnimationScale(2, 1.0f); } - } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java new file mode 100644 index 000000000000..631de99dc3ad --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -0,0 +1,483 @@ +/* + * 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.app.ActivityManager.START_DELIVERED_TO_TOP; +import static android.app.ActivityManager.START_TASK_TO_FRONT; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; +import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE; +import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.wm.ActivityDisplay.POSITION_TOP; +import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; +import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.eq; + +import android.app.ActivityOptions; +import android.app.WaitResult; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.graphics.Rect; +import android.os.Build; +import android.platform.test.annotations.Presubmit; +import androidx.test.filters.MediumTest; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; + +/** + * Tests for the {@link ActivityStackSupervisor} class. + * + * Build/Install/Run: + * atest WmTests:ActivityStackSupervisorTests + */ +@MediumTest +@Presubmit +public class RootActivityContainerTests extends ActivityTestsBase { + private ActivityStack mFullscreenStack; + + @Before + public void setUp() throws Exception { + setupActivityTaskManagerService(); + mFullscreenStack = mRootActivityContainer.getDefaultDisplay().createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + } + + /** + * This test ensures that we do not try to restore a task based off an invalid task id. We + * should expect {@code null} to be returned in this case. + */ + @Test + public void testRestoringInvalidTask() { + ((TestActivityDisplay) mRootActivityContainer.getDefaultDisplay()).removeAllTasks(); + TaskRecord task = mRootActivityContainer.anyTaskForId(0 /*taskId*/, + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */); + assertNull(task); + } + + /** + * This test ensures that an existing task in the pinned stack is moved to the fullscreen + * activity stack when a new task is added. + */ + @Test + public void testReplacingTaskInPinnedStack() { + final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true) + .setStack(mFullscreenStack).build(); + final TaskRecord firstTask = firstActivity.getTask(); + + final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true) + .setStack(mFullscreenStack).build(); + final TaskRecord secondTask = secondActivity.getTask(); + + mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack"); + + // Ensure full screen stack has both tasks. + ensureStackPlacement(mFullscreenStack, firstTask, secondTask); + + // Move first activity to pinned stack. + final Rect sourceBounds = new Rect(); + mRootActivityContainer.moveActivityToPinnedStack(firstActivity, sourceBounds, + 0f /*aspectRatio*/, "initialMove"); + + final ActivityDisplay display = mFullscreenStack.getDisplay(); + ActivityStack pinnedStack = display.getPinnedStack(); + // Ensure a task has moved over. + ensureStackPlacement(pinnedStack, firstTask); + ensureStackPlacement(mFullscreenStack, secondTask); + + // Move second activity to pinned stack. + mRootActivityContainer.moveActivityToPinnedStack(secondActivity, sourceBounds, + 0f /*aspectRatio*/, "secondMove"); + + // Need to get stacks again as a new instance might have been created. + pinnedStack = display.getPinnedStack(); + mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + // Ensure stacks have swapped tasks. + ensureStackPlacement(pinnedStack, secondTask); + ensureStackPlacement(mFullscreenStack, firstTask); + } + + private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) { + final ArrayList<TaskRecord> stackTasks = stack.getAllTasks(); + assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0); + + if (tasks == null) { + return; + } + + for (TaskRecord task : tasks) { + assertTrue(stackTasks.contains(task)); + } + } + + @Test + public void testApplySleepTokens() { + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); + final KeyguardController keyguard = mSupervisor.getKeyguardController(); + final ActivityStack stack = mock(ActivityStack.class); + display.addChild(stack, 0 /* position */); + + // Make sure we wake and resume in the case the display is turning on and the keyguard is + // not showing. + verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, + false /* displayShouldSleep */, true /* isFocusedStack */, + false /* keyguardShowing */, true /* expectWakeFromSleep */, + true /* expectResumeTopActivity */); + + // Make sure we wake and don't resume when the display is turning on and the keyguard is + // showing. + verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, + false /* displayShouldSleep */, true /* isFocusedStack */, + true /* keyguardShowing */, true /* expectWakeFromSleep */, + false /* expectResumeTopActivity */); + + // Make sure we wake and don't resume when the display is turning on and the keyguard is + // not showing as unfocused. + verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, + false /* displayShouldSleep */, false /* isFocusedStack */, + false /* keyguardShowing */, true /* expectWakeFromSleep */, + false /* expectResumeTopActivity */); + + // Should not do anything if the display state hasn't changed. + verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/, + false /* displayShouldSleep */, true /* isFocusedStack */, + false /* keyguardShowing */, false /* expectWakeFromSleep */, + false /* expectResumeTopActivity */); + } + + private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard, + ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep, + boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep, + boolean expectResumeTopActivity) { + reset(stack); + + doReturn(displayShouldSleep).when(display).shouldSleep(); + doReturn(displaySleeping).when(display).isSleeping(); + doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt()); + + doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay(); + doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack(); + mRootActivityContainer.applySleepTokens(true); + verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked(); + verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked( + null /* target */, null /* targetOptions */); + } + + /** + * Verifies that removal of activity with task and stack is done correctly. + */ + @Test + public void testRemovingStackOnAppCrash() { + final ActivityDisplay defaultDisplay = mRootActivityContainer.getDefaultDisplay(); + final int originalStackCount = defaultDisplay.getChildCount(); + final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true) + .setStack(stack).build(); + + assertEquals(originalStackCount + 1, defaultDisplay.getChildCount()); + + // Let's pretend that the app has crashed. + firstActivity.app.setThread(null); + mRootActivityContainer.finishTopCrashedActivities(firstActivity.app, "test"); + + // Verify that the stack was removed. + assertEquals(originalStackCount, defaultDisplay.getChildCount()); + } + + @Test + public void testFocusability() { + final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack( + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true) + .setStack(stack).build(); + + // Under split screen primary we should be focusable when not minimized + mRootActivityContainer.setDockedStackMinimized(false); + assertTrue(stack.isFocusable()); + assertTrue(activity.isFocusable()); + + // Under split screen primary we should not be focusable when minimized + mRootActivityContainer.setDockedStackMinimized(true); + assertFalse(stack.isFocusable()); + assertFalse(activity.isFocusable()); + + final ActivityStack pinnedStack = mRootActivityContainer.getDefaultDisplay().createStack( + WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true) + .setStack(pinnedStack).build(); + + // We should not be focusable when in pinned mode + assertFalse(pinnedStack.isFocusable()); + assertFalse(pinnedActivity.isFocusable()); + + // Add flag forcing focusability. + pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE; + + // We should not be focusable when in pinned mode + assertTrue(pinnedStack.isFocusable()); + assertTrue(pinnedActivity.isFocusable()); + + // Without the overridding activity, stack should not be focusable. + pinnedStack.removeTask(pinnedActivity.getTask(), "testFocusability", + REMOVE_TASK_MODE_DESTROYING); + assertFalse(pinnedStack.isFocusable()); + } + + /** + * Verify that split-screen primary stack will be chosen if activity is launched that targets + * split-screen secondary, but a matching existing instance is found on top of split-screen + * primary stack. + */ + @Test + public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() { + // Create primary split-screen stack with a task and an activity. + final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay() + .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, + true /* onTop */); + final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build(); + final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build(); + + // Find a launch stack for the top activity in split-screen primary, while requesting + // split-screen secondary. + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY); + final ActivityStack result = + mRootActivityContainer.getLaunchStack(r, options, task, true /* onTop */); + + // Assert that the primary stack is returned. + assertEquals(primaryStack, result); + } + + /** + * Verify split-screen primary stack & task can resized by + * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect. + */ + @Test + public void testResizeDockedStackForSplitScreenPrimary() { + final Rect taskSize = new Rect(0, 0, 600, 600); + final Rect stackSize = new Rect(0, 0, 300, 300); + + // Create primary split-screen stack with a task. + final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay() + .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, + true /* onTop */); + final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build(); + + // Resize dock stack. + mService.resizeDockedStack(stackSize, taskSize, null, null, null); + + // Verify dock stack & its task bounds if is equal as resized result. + assertEquals(primaryStack.getBounds(), stackSize); + assertEquals(task.getBounds(), taskSize); + } + + /** + * Verify that home stack would be moved to front when the top activity is Recents. + */ + @Test + public void testFindTaskToMoveToFrontWhenRecentsOnTop() { + // Create stack/task on default display. + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); + final TestActivityStack targetStack = + new StackBuilder(mRootActivityContainer).setOnTop(false).build(); + final TaskRecord targetTask = targetStack.getChildAt(0); + + // Create Recents on top of the display. + final ActivityStack stack = new StackBuilder(mRootActivityContainer).setActivityType( + ACTIVITY_TYPE_RECENTS).build(); + + final String reason = "findTaskToMoveToFront"; + mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, + false); + + verify(display).moveHomeStackToFront(contains(reason)); + } + + /** + * Verify that home stack won't be moved to front if the top activity on other display is + * Recents. + */ + @Test + public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() { + // Create stack/task on default display. + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); + final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, false /* onTop */); + final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build(); + + // Create Recents on secondary display. + final TestActivityDisplay secondDisplay = addNewActivityDisplayAt( + ActivityDisplay.POSITION_TOP); + final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_RECENTS, true /* onTop */); + final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build(); + new ActivityBuilder(mService).setTask(task).build(); + + final String reason = "findTaskToMoveToFront"; + mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, + false); + + verify(display, never()).moveHomeStackToFront(contains(reason)); + } + + /** + * Verify if a stack is not at the topmost position, it should be able to resume its activity if + * the stack is the top focused. + */ + @Test + public void testResumeActivityWhenNonTopmostStackIsTopFocused() { + // Create a stack at bottom. + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); + final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, false /* onTop */)); + final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build(); + final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build(); + display.positionChildAtBottom(targetStack); + + // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it + // is the current top focused stack. + assertFalse(targetStack.isTopStackOnDisplay()); + doReturn(targetStack).when(mRootActivityContainer).getTopDisplayFocusedStack(); + + // Use the stack as target to resume. + mRootActivityContainer.resumeFocusedStacksTopActivities( + targetStack, activity, null /* targetOptions */); + + // Verify the target stack should resume its activity. + verify(targetStack, times(1)).resumeTopActivityUncheckedLocked( + eq(activity), eq(null /* targetOptions */)); + } + + /** + * Tests home activities that targeted sdk before Q cannot start on secondary display. + */ + @Test + public void testStartHomeTargetSdkBeforeQ() throws Exception { + final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay()); + mRootActivityContainer.addChild(secondDisplay, POSITION_TOP); + doReturn(true).when(secondDisplay).supportsSystemDecorations(); + + final ActivityInfo info = new ActivityInfo(); + info.launchMode = LAUNCH_MULTIPLE; + info.applicationInfo = new ApplicationInfo(); + info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; + assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId, + false /* allowInstrumenting */)); + + info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P; + assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId, + false /* allowInstrumenting */)); + } + + /** + * Tests that home activities can be started on the displays that supports system decorations. + */ + @Test + public void testStartHomeOnAllDisplays() { + // Create secondary displays. + final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay()); + mRootActivityContainer.addChild(secondDisplay, POSITION_TOP); + doReturn(true).when(secondDisplay).supportsSystemDecorations(); + + // Create mock tasks and other necessary mocks. + TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false); + final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class); + TaskRecord.setTaskRecordFactory(factory); + doAnswer(i -> taskBuilder.build()).when(factory) + .create(any(), anyInt(), any(), any(), any(), any()); + doReturn(true).when(mRootActivityContainer) + .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean()); + doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay( + any(), anyInt(), anyBoolean()); + + mRootActivityContainer.startHomeOnAllDisplays(0, "testStartHome"); + + assertTrue(mRootActivityContainer.getDefaultDisplay().getTopStack().isActivityTypeHome()); + assertNotNull(secondDisplay.getTopStack()); + assertTrue(secondDisplay.getTopStack().isActivityTypeHome()); + } + + /** + * Tests that home activities won't be started before booting when display added. + */ + @Test + public void testNotStartHomeBeforeBoot() { + final int displayId = 1; + final boolean isBooting = mService.mAmInternal.isBooting(); + final boolean isBooted = mService.mAmInternal.isBooted(); + try { + mService.mAmInternal.setBooting(false); + mService.mAmInternal.setBooted(false); + mRootActivityContainer.onDisplayAdded(displayId); + verify(mRootActivityContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt()); + } finally { + mService.mAmInternal.setBooting(isBooting); + mService.mAmInternal.setBooted(isBooted); + } + } + + /** + * Tests whether home can be started if being instrumented. + */ + @Test + public void testCanStartHomeWhenInstrumented() { + final ActivityInfo info = new ActivityInfo(); + info.applicationInfo = new ApplicationInfo(); + final WindowProcessController app = mock(WindowProcessController.class); + doReturn(app).when(mService).getProcessController(any(), anyInt()); + + // Can not start home if we don't want to start home while home is being instrumented. + doReturn(true).when(app).isInstrumenting(); + assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, + false /* allowInstrumenting*/)); + + // Can start home for other cases. + assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, + true /* allowInstrumenting*/)); + + doReturn(false).when(app).isInstrumenting(); + assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, + false /* allowInstrumenting*/)); + assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, + true /* allowInstrumenting*/)); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java index 0e1624ebef63..a8b6dc373cbf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java @@ -63,7 +63,7 @@ public class RunningTasksTest extends ActivityTestsBase { final int numStacks = 2; for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) { final ActivityStack stack = - new StackBuilder(mSupervisor).setCreateActivity(false).build(); + new StackBuilder(mRootActivityContainer).setCreateActivity(false).build(); display.addChild(stack, POSITION_BOTTOM); } diff --git a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java index ce5b13cab1a7..ce5b13cab1a7 100644 --- a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java index 584f2695891b..83e7ee711831 100644 --- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java @@ -16,16 +16,17 @@ package com.android.server.wm; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeastOnce; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import static java.util.concurrent.TimeUnit.SECONDS; diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java index 6833dc5f0497..d14f30db8e9f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java @@ -16,16 +16,17 @@ package com.android.server.wm; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; import android.platform.test.annotations.Presubmit; import android.view.SurfaceControl; @@ -225,11 +226,9 @@ public class SurfaceAnimatorTest extends WindowTestsBase { mTransaction = transaction; mParent = wm.makeSurfaceBuilder(mSession) .setName("test surface parent") - .setSize(3000, 3000) .build(); mSurface = wm.makeSurfaceBuilder(mSession) .setName("test surface") - .setSize(1, 1) .build(); mFinishedCallbackCalled = false; mLeash = null; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index 6638eeb8af19..bd8cd1f37f5e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -888,10 +888,10 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase { @Test public void testAdjustBoundsToFitNewDisplay_LargerThanDisplay_RTL() { - final Configuration overrideConfig = mSupervisor.getOverrideConfiguration(); + final Configuration overrideConfig = mRootActivityContainer.getOverrideConfiguration(); // Egyptian Arabic is a RTL language. overrideConfig.setLayoutDirection(new Locale("ar", "EG")); - mSupervisor.onOverrideConfigurationChanged(overrideConfig); + mRootActivityContainer.onOverrideConfigurationChanged(overrideConfig); final TestActivityDisplay freeformDisplay = createNewActivityDisplay( WINDOWING_MODE_FREEFORM); diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java index 785b955863db..b996bfbf2101 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java @@ -52,8 +52,8 @@ public class TaskPositionerTests extends WindowTestsBase { private static final boolean DEBUGGING = false; private static final String TAG = "TaskPositionerTest"; - private final static int MOUSE_DELTA_X = 5; - private final static int MOUSE_DELTA_Y = 5; + private static final int MOUSE_DELTA_X = 5; + private static final int MOUSE_DELTA_Y = 5; private int mMinVisibleWidth; private int mMinVisibleHeight; @@ -315,7 +315,7 @@ public class TaskPositionerTests extends WindowTestsBase { // Drag all the way to the right and see the height also shrinking. mPositioner.resizeDrag(2000.0f, midY); final int w = mMinVisibleWidth; - final int h = Math.round((float)w / MIN_ASPECT); + final int h = Math.round((float) w / MIN_ASPECT); assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h), mPositioner.getWindowDragBounds()); @@ -428,7 +428,7 @@ public class TaskPositionerTests extends WindowTestsBase { // Drag all the way to the right. mPositioner.resizeDrag(2000.0f, midY); w = mMinVisibleWidth; - h = Math.max(Math.round((float)w * MIN_ASPECT), r.height()); + h = Math.max(Math.round((float) w * MIN_ASPECT), r.height()); assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h), mPositioner.getWindowDragBounds()); diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java index 00b462954bd9..3991e06d6f96 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java @@ -18,14 +18,15 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyInt; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import android.platform.test.annotations.Presubmit; import android.view.InputChannel; diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java index 1c6afd545b1f..1c6afd545b1f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java index d2c07651d83c..792e8a6f7582 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -48,7 +48,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { public void testGetClosingApps_closing() { final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, "closingWindow"); - closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET, + closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET, true /* performLayout */, false /* isVoiceInteraction */); final ArraySet<AppWindowToken> closingApps = new ArraySet<>(); closingApps.add(closingWindow.mAppToken); @@ -64,9 +64,9 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { "closingWindow"); final WindowState openingWindow = createAppWindow(closingWindow.getTask(), FIRST_APPLICATION_WINDOW, "openingWindow"); - closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET, + closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET, true /* performLayout */, false /* isVoiceInteraction */); - openingWindow.mAppToken.setVisibility(null, true /* visible */, TRANSIT_UNSET, + openingWindow.mAppToken.commitVisibility(null, true /* visible */, TRANSIT_UNSET, true /* performLayout */, false /* isVoiceInteraction */); final ArraySet<AppWindowToken> closingApps = new ArraySet<>(); closingApps.add(closingWindow.mAppToken); @@ -79,7 +79,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { public void testGetClosingApps_skipClosingAppsSnapshotTasks() { final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, "closingWindow"); - closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET, + closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET, true /* performLayout */, false /* isVoiceInteraction */); final ArraySet<AppWindowToken> closingApps = new ArraySet<>(); closingApps.add(closingWindow.mAppToken); diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java index b0eafeeae043..b0eafeeae043 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java index 946ffb60c759..946ffb60c759 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java index a569b9e6dd6f..624ef9ba1653 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java @@ -11,7 +11,7 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ package com.android.server.wm; @@ -20,14 +20,15 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.app.ActivityManager.TaskSnapshot; import android.content.ComponentName; diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java index f01e9f0662c9..f01e9f0662c9 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java index 7ac331829fb1..7ac331829fb1 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java index 1af79e41fe37..bbf508dd1630 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java @@ -17,8 +17,6 @@ package com.android.server.wm; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import android.platform.test.annotations.Presubmit; @@ -37,6 +35,7 @@ import org.junit.Test; @Presubmit public class TaskWindowContainerControllerTests extends WindowTestsBase { + /* Comment out due to removal of AppWindowContainerController @Test public void testRemoveContainer() { final WindowTestUtils.TestTaskWindowContainerController taskController = @@ -49,7 +48,9 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { assertNull(taskController.mContainer); assertNull(appController.mContainer); } + */ + /* Comment out due to removal of AppWindowContainerController @Test public void testRemoveContainer_deferRemoval() { final WindowTestUtils.TestTaskWindowContainerController taskController = @@ -74,6 +75,7 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { assertNull(appController.mContainer); assertNull(app.getController()); } + */ @Test public void testReparent() { diff --git a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java index 99deeb9e9397..432af0d7a469 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java @@ -24,6 +24,7 @@ import android.util.MergedConfiguration; import android.view.DisplayCutout; import android.view.DragEvent; import android.view.IWindow; +import android.view.InsetsState; import com.android.internal.os.IResultReceiver; @@ -39,6 +40,9 @@ public class TestIWindow extends IWindow.Stub { Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId, DisplayCutout.ParcelableWrapper displayCutout) throws RemoteException { } + @Override + public void insetsChanged(InsetsState insetsState) throws RemoteException { + } @Override public void moved(int newX, int newY) throws RemoteException { diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java index 7b542cb4f2f7..ba81bd1c3b12 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -19,8 +19,8 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import android.annotation.Nullable; import android.content.Context; @@ -46,7 +46,8 @@ import java.util.function.Supplier; class TestWindowManagerPolicy implements WindowManagerPolicy { private final Supplier<WindowManagerService> mWmSupplier; - boolean keyguardShowingAndNotOccluded = false; + int mRotationToReport = 0; + boolean mKeyguardShowingAndNotOccluded = false; private Runnable mRunnableWhenAddingSplashScreen; @@ -236,7 +237,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { @Override public boolean isKeyguardLocked() { - return keyguardShowingAndNotOccluded; + return mKeyguardShowingAndNotOccluded; } @Override @@ -256,7 +257,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { @Override public boolean isKeyguardShowingAndNotOccluded() { - return keyguardShowingAndNotOccluded; + return mKeyguardShowingAndNotOccluded; } @Override diff --git a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java index 9e22c0a86d96..612f9ad923d6 100644 --- a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java @@ -73,7 +73,7 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase { public void testClear() { final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent); mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token); - mDisplayContent.mUnknownAppVisibilityController.clear();; + mDisplayContent.mUnknownAppVisibilityController.clear(); assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved()); } diff --git a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java index 4ea6b3901f03..d07230ef2ca3 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java @@ -18,11 +18,12 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + import static junit.framework.TestCase.assertNotNull; import static org.junit.Assert.assertNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import android.graphics.Bitmap; import android.os.IBinder; diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java index 3643457f061f..3643457f061f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java index 7592f1c1fca0..7592f1c1fca0 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index e59afd656420..a2e0ed933bb9 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -22,6 +22,13 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -30,13 +37,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyFloat; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; import android.content.res.Configuration; import android.graphics.Rect; @@ -459,13 +459,13 @@ public class WindowContainerTests extends WindowTestsBase { @Test public void testGetOrientation_childSpecified() { testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_LANDSCAPE, - SCREEN_ORIENTATION_LANDSCAPE); + SCREEN_ORIENTATION_LANDSCAPE); testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_UNSET, - SCREEN_ORIENTATION_UNSPECIFIED); + SCREEN_ORIENTATION_UNSPECIFIED); } private void testGetOrientation_childSpecifiedConfig(boolean childVisible, int childOrientation, - int expectedOrientation) { + int expectedOrientation) { final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); final TestWindowContainer root = builder.setLayer(0).build(); root.setFillsParent(true); @@ -704,7 +704,7 @@ public class WindowContainerTests extends WindowTestsBase { final TestWindowContainer root = builder.build(); final TestWindowContainer child = root.addChildWindow(); - child.setBounds(new Rect(1,1,2,2)); + child.setBounds(new Rect(1, 1, 2, 2)); final TestWindowContainer grandChild = mock(TestWindowContainer.class); @@ -742,7 +742,7 @@ public class WindowContainerTests extends WindowTestsBase { private static final Comparator<TestWindowContainer> SUBLAYER_COMPARATOR = (w1, w2) -> { final int layer1 = w1.mLayer; final int layer2 = w2.mLayer; - if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0 )) { + if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0)) { // We insert the child window into the list ordered by the mLayer. For same layers, // the negative one should go below others; the positive one should go above others. return -1; diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java index fcde08e18a6f..4b666f538ea2 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java @@ -21,9 +21,10 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import android.platform.test.annotations.Presubmit; diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java index 227eb00be7f7..b3e90debc84b 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java @@ -290,7 +290,7 @@ public class WindowFrameTests extends WindowTestsBase { w.mRequestedHeight = 300; w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP; w.computeFrameLw(); - assertFrame(w, 700, 0, 1000, 300); + assertFrame(w, 700, 0, 1000, 300); w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM; w.computeFrameLw(); assertFrame(w, 700, 700, 1000, 1000); diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java index 9a13efb4c58b..4a99172160f5 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java @@ -21,14 +21,15 @@ import static android.view.Display.DEFAULT_DISPLAY; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; import android.app.ActivityManagerInternal; import android.content.Context; @@ -100,6 +101,7 @@ public class WindowManagerServiceRule implements TestRule { mock(PowerManagerInternal.class)); final PowerManagerInternal pm = LocalServices.getService(PowerManagerInternal.class); + doNothing().when(pm).registerLowPowerModeObserver(any()); PowerSaveState state = new PowerSaveState.Builder().build(); doReturn(state).when(pm).getLowPowerState(anyInt()); @@ -146,8 +148,8 @@ public class WindowManagerServiceRule implements TestRule { final Display display = mService.mDisplayManager.getDisplay(DEFAULT_DISPLAY); final DisplayWindowController dcw = new DisplayWindowController(display, mService); - // Display creation is driven by the ActivityManagerService via ActivityStackSupervisor. - // We emulate those steps here. + // Display creation is driven by the ActivityManagerService via + // ActivityStackSupervisor. We emulate those steps here. mService.mRoot.createDisplayContent(display, dcw); } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java index 343d35959df4..343d35959df4 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 118ce8962259..7f7803463543 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -32,6 +32,12 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; @@ -43,11 +49,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; import android.graphics.Insets; import android.graphics.Matrix; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java index e56edabfa02f..3bd9a89075c0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java @@ -16,6 +16,9 @@ package com.android.server.wm; +import static android.app.AppOpsManager.OP_NONE; +import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat; @@ -23,12 +26,21 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; +import static com.android.server.wm.WindowContainer.POSITION_TOP; +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; import org.mockito.invocation.InvocationOnMock; @@ -37,6 +49,7 @@ import org.mockito.invocation.InvocationOnMock; * to WindowManager related test functionality. */ public class WindowTestUtils { + private static int sNextTaskId = 0; /** An extension of {@link DisplayContent} to gain package scoped access. */ public static class TestDisplayContent extends DisplayContent { @@ -54,6 +67,14 @@ public class WindowTestUtils { return null; } + /** + * Stubbing method of non-public parent class isn't supported, so here explicitly overrides. + */ + @Override + DockedStackDividerController getDockedDividerController() { + return null; + } + /** Create a mocked default {@link DisplayContent}. */ public static TestDisplayContent create(Context context) { final TestDisplayContent displayContent = mock(TestDisplayContent.class); @@ -65,7 +86,7 @@ public class WindowTestUtils { final DisplayRotation displayRotation = new DisplayRotation( mock(WindowManagerService.class), displayContent, displayPolicy, - context, new Object()); + mock(DisplayWindowSettings.class), context, new Object()); displayRotation.mPortraitRotation = Surface.ROTATION_0; displayRotation.mLandscapeRotation = Surface.ROTATION_90; displayRotation.mUpsideDownRotation = Surface.ROTATION_180; @@ -106,6 +127,17 @@ public class WindowTestUtils { return controller; } + /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ + public static Task createTaskInStack(WindowManagerService service, TaskStack stack, + int userId) { + synchronized (service.mGlobalLock) { + final Task newTask = new Task(sNextTaskId++, stack, userId, service, 0, false, + new ActivityManager.TaskDescription(), null); + stack.addTask(newTask, POSITION_TOP); + return newTask; + } + } + /** * An extension of {@link TestTaskStack}, which overrides package scoped methods that would not * normally be mocked out. @@ -120,4 +152,233 @@ public class WindowTestUtils { // Do nothing. } } + + static TestAppWindowToken createTestAppWindowToken(DisplayContent dc) { + synchronized (dc.mService.mGlobalLock) { + return new TestAppWindowToken(dc); + } + } + + /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */ + public static class TestAppWindowToken extends AppWindowToken { + boolean mOnTop = false; + private Transaction mPendingTransactionOverride; + + private TestAppWindowToken(DisplayContent dc) { + super(dc.mService, new IApplicationToken.Stub() { + @Override + public String getName() { + return null; + } + }, new ComponentName("", ""), false, dc, true /* fillsParent */); + } + + TestAppWindowToken(WindowManagerService service, IApplicationToken token, + ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc, + long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, + int targetSdk, int orientation, int rotationAnimationHint, int configChanges, + boolean launchTaskBehind, boolean alwaysFocusable, ActivityRecord activityRecord) { + super(service, token, activityComponent, voiceInteraction, dc, + inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, + orientation, rotationAnimationHint, configChanges, launchTaskBehind, + alwaysFocusable, activityRecord); + } + + int getWindowsCount() { + return mChildren.size(); + } + + boolean hasWindow(WindowState w) { + return mChildren.contains(w); + } + + WindowState getFirstChild() { + return mChildren.peekFirst(); + } + + WindowState getLastChild() { + return mChildren.peekLast(); + } + + int positionInParent() { + return getParent().mChildren.indexOf(this); + } + + void setIsOnTop(boolean onTop) { + mOnTop = onTop; + } + + @Override + boolean isOnTop() { + return mOnTop; + } + + void setPendingTransaction(Transaction transaction) { + mPendingTransactionOverride = transaction; + } + + @Override + public Transaction getPendingTransaction() { + return mPendingTransactionOverride == null + ? super.getPendingTransaction() + : mPendingTransactionOverride; + } + } + + static TestWindowToken createTestWindowToken(int type, DisplayContent dc) { + return createTestWindowToken(type, dc, false /* persistOnEmpty */); + } + + static TestWindowToken createTestWindowToken(int type, DisplayContent dc, + boolean persistOnEmpty) { + synchronized (dc.mService.mGlobalLock) { + return new TestWindowToken(type, dc, persistOnEmpty); + } + } + + /* Used so we can gain access to some protected members of the {@link WindowToken} class */ + public static class TestWindowToken extends WindowToken { + + private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) { + super(dc.mService, mock(IBinder.class), type, persistOnEmpty, dc, + false /* ownerCanManageAppTokens */); + } + + int getWindowsCount() { + return mChildren.size(); + } + + boolean hasWindow(WindowState w) { + return mChildren.contains(w); + } + } + + /* Used so we can gain access to some protected members of the {@link Task} class */ + public static class TestTask extends Task { + boolean mShouldDeferRemoval = false; + boolean mOnDisplayChangedCalled = false; + private boolean mIsAnimating = false; + + TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, + int resizeMode, boolean supportsPictureInPicture, + TaskWindowContainerController controller) { + super(taskId, stack, userId, service, resizeMode, supportsPictureInPicture, + new ActivityManager.TaskDescription(), controller); + } + + boolean shouldDeferRemoval() { + return mShouldDeferRemoval; + } + + int positionInParent() { + return getParent().mChildren.indexOf(this); + } + + @Override + void onDisplayChanged(DisplayContent dc) { + super.onDisplayChanged(dc); + mOnDisplayChangedCalled = true; + } + + @Override + boolean isSelfAnimating() { + return mIsAnimating; + } + + void setLocalIsAnimating(boolean isAnimating) { + mIsAnimating = isAnimating; + } + } + + /** + * Used so we can gain access to some protected members of {@link TaskWindowContainerController} + * class. + */ + public static class TestTaskWindowContainerController extends TaskWindowContainerController { + + static final TaskWindowContainerListener NOP_LISTENER = new TaskWindowContainerListener() { + @Override + public void registerConfigurationChangeListener( + ConfigurationContainerListener listener) { + } + + @Override + public void unregisterConfigurationChangeListener( + ConfigurationContainerListener listener) { + } + + @Override + public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) { + } + + @Override + public void requestResize(Rect bounds, int resizeMode) { + } + }; + + TestTaskWindowContainerController(WindowTestsBase testsBase) { + this(testsBase.createStackControllerOnDisplay(testsBase.mDisplayContent)); + } + + TestTaskWindowContainerController(StackWindowController stackController) { + super(sNextTaskId++, NOP_LISTENER, stackController, 0 /* userId */, null /* bounds */, + RESIZE_MODE_UNRESIZEABLE, false /* supportsPictureInPicture */, true /* toTop*/, + true /* showForAllUsers */, new ActivityManager.TaskDescription(), + stackController.mService); + } + + @Override + TestTask createTask(int taskId, TaskStack stack, int userId, int resizeMode, + boolean supportsPictureInPicture, ActivityManager.TaskDescription taskDescription) { + return new TestTask(taskId, stack, userId, mService, resizeMode, + supportsPictureInPicture, this); + } + } + + public static class TestIApplicationToken implements IApplicationToken { + + private final Binder mBinder = new Binder(); + @Override + public IBinder asBinder() { + return mBinder; + } + @Override + public String getName() { + return null; + } + } + + /** Used to track resize reports. */ + public static class TestWindowState extends WindowState { + boolean mResizeReported; + + TestWindowState(WindowManagerService service, Session session, IWindow window, + WindowManager.LayoutParams attrs, WindowToken token) { + super(service, session, window, token, null, OP_NONE, 0, attrs, 0, 0, + false /* ownerCanAddInternalSystemWindow */); + } + + @Override + void reportResized() { + super.reportResized(); + mResizeReported = true; + } + + @Override + public boolean isGoneForLayoutLw() { + return false; + } + + @Override + void updateResizingWindowIfNeeded() { + // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive + // the system that it can actually update the window. + boolean hadSurface = mHasSurface; + mHasSurface = true; + + super.updateResizingWindowIfNeeded(); + + mHasSurface = hadSurface; + } + } } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 1eb46fb05b5e..b3f56e7ae99f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -37,7 +37,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static org.mockito.Mockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import android.content.Context; import android.content.res.Configuration; diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index 3048f1a3487b..3048f1a3487b 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java index 3dcea75b8ae5..3dcea75b8ae5 100644 --- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 34a8c9659595..45cfe1e303ec 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -569,14 +569,6 @@ public class SubscriptionManager { public static final String IS_OPPORTUNISTIC = "is_opportunistic"; /** - * TelephonyProvider column name for subId of parent subscription of an opportunistic - * subscription. - * if the parent sub id is valid, then is_opportunistic should always to true. - * @hide - */ - public static final String PARENT_SUB_ID = "parent_sub_id"; - - /** * TelephonyProvider column name for group ID. Subscriptions with same group ID * are considered bundled together, and should behave as a single subscription at * certain scenarios. diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt index 7842a1cd03b3..5b58dd5d758b 100644 --- a/test-mock/api/current.txt +++ b/test-mock/api/current.txt @@ -80,6 +80,7 @@ package android.test.mock { method public java.io.File getNoBackupFilesDir(); method public java.io.File getObbDir(); method public java.io.File[] getObbDirs(); + method public java.lang.String getOpPackageName(); method public java.lang.String getPackageCodePath(); method public android.content.pm.PackageManager getPackageManager(); method public java.lang.String getPackageName(); diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt index f1ec000dfa6a..8b2c815432ea 100644 --- a/test-mock/api/test-current.txt +++ b/test-mock/api/test-current.txt @@ -1,9 +1,5 @@ package android.test.mock { - public class MockContext extends android.content.Context { - method public java.lang.String getOpPackageName(); - } - public deprecated class MockPackageManager extends android.content.pm.PackageManager { method public boolean arePermissionsIndividuallyControlled(); method public java.lang.String getDefaultBrowserPackageNameAsUser(int); diff --git a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java index 8d8fc84126ec..b9e282ebdfef 100644 --- a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java +++ b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java @@ -38,7 +38,7 @@ public class WallpaperServiceTest { public Engine onCreateEngine() { return new Engine() { @Override - public void onAmbientModeChanged(boolean inAmbientMode, boolean animated) { + public void onAmbientModeChanged(boolean inAmbientMode, long duration) { ambientModeChangedCount[0]++; } }; @@ -47,12 +47,12 @@ public class WallpaperServiceTest { WallpaperService.Engine engine = service.onCreateEngine(); engine.setCreated(true); - engine.doAmbientModeChanged(false, false); + engine.doAmbientModeChanged(false, 0); assertFalse("ambient mode should be false", engine.isInAmbientMode()); assertEquals("onAmbientModeChanged should have been called", ambientModeChangedCount[0], 1); - engine.doAmbientModeChanged(true, false); + engine.doAmbientModeChanged(true, 0); assertTrue("ambient mode should be false", engine.isInAmbientMode()); assertEquals("onAmbientModeChanged should have been called", ambientModeChangedCount[0], 2); diff --git a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java index ae3914ebf162..d5987a5373b4 100644 --- a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java +++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java @@ -26,6 +26,7 @@ import android.util.MergedConfiguration; import android.view.Display; import android.view.DisplayCutout; import android.view.IWindowSession; +import android.view.InsetsState; import android.view.Surface; import android.view.View; import android.view.WindowManager; @@ -105,7 +106,7 @@ public class MainActivity extends Activity { window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, -1, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, new DisplayCutout.ParcelableWrapper(), new MergedConfiguration(), - new Surface()); + new Surface(), new InsetsState()); } catch (RemoteException e) { e.printStackTrace(); } @@ -131,8 +132,9 @@ public class MainActivity extends Activity { final IWindowSession session = WindowManagerGlobal.getWindowSession(); final Rect tmpRect = new Rect(); try { - final int res = session.addToDisplayWithoutInputChannel(window, window.mSeq, layoutParams, - View.VISIBLE, Display.DEFAULT_DISPLAY, tmpRect, tmpRect); + final int res = session.addToDisplayWithoutInputChannel(window, window.mSeq, + layoutParams, View.VISIBLE, Display.DEFAULT_DISPLAY, tmpRect, tmpRect, + new InsetsState()); } catch (RemoteException e) { e.printStackTrace(); } diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh new file mode 100755 index 000000000000..2291e5a92730 --- /dev/null +++ b/tools/hiddenapi/exclude.sh @@ -0,0 +1,57 @@ +#!/bin/bash +set -e +# Make sure that entries are not added for packages that are already fully handled using +# annotations. +LOCAL_DIR="$( dirname ${BASH_SOURCE} )" +# Each team should add a <team>_PACKAGES and <team>_EMAIL with the list of packages and +# the team email to use in the event of this detecting an entry in a <team> package. Also +# add <team> to the TEAMS list. +LIBCORE_PACKAGES="\ + android.icu \ + android.system \ + com.android.bouncycastle \ + com.android.conscrypt \ + com.android.okhttp \ + com.sun \ + dalvik \ + java \ + javax \ + libcore \ + org.apache.harmony \ + org.json \ + org.w3c.dom \ + org.xml.sax \ + sun \ + " +LIBCORE_EMAIL=libcore-team@android.com + +# List of teams. +TEAMS=LIBCORE + +# Generate the list of packages and convert to a regular expression. +PACKAGES=$(for t in $TEAMS; do echo $(eval echo \${${t}_PACKAGES}); done) +RE=$(echo ${PACKAGES} | sed "s/ /|/g") +git show --name-only --pretty=format: $1 | grep "config/hiddenapi-.*txt" | while read file; do + ENTRIES=$(grep -E "^L(${RE})/" <(git show $1:$file)) + if [[ -n "${ENTRIES}" ]]; then + echo -e "\e[1m\e[31m$file $1 contains the following entries\e[0m" + echo -e "\e[1m\e[31mfor packages that are handled using UnsupportedAppUsage. Please remove\e[0m" + echo -e "\e[1m\e[31mthese entries and add annotations instead.\e[0m" + # Partition the entries by team and provide contact details to aid in fixing the issue. + for t in ${TEAMS} + do + PACKAGES=$(eval echo \${${t}_PACKAGES}) + RE=$(echo ${PACKAGES} | sed "s/ /|/g") + TEAM_ENTRIES=$(grep -E "^L(${RE})/" <(echo "${ENTRIES}")) + if [[ -n "${TEAM_ENTRIES}" ]]; then + EMAIL=$(eval echo \${${t}_EMAIL}) + echo -e "\e[33mContact ${EMAIL} or compat- for help with the following:\e[0m" + for i in ${ENTRIES} + do + echo -e "\e[33m ${i}\e[0m" + done + fi + done + exit 1 + fi +done diff --git a/tools/powermodel/Android.bp b/tools/powermodel/Android.bp new file mode 100644 index 000000000000..f597aab0f464 --- /dev/null +++ b/tools/powermodel/Android.bp @@ -0,0 +1,26 @@ + +java_library_host { + name: "powermodel", + srcs: [ + "src/**/*.java", + ], + static_libs: [ + "guava", + ], +} + +java_test_host { + name: "powermodel-test", + + test_suites: ["general-tests"], + + srcs: ["test/**/*.java"], + java_resource_dirs: ["test-resource"], + + static_libs: [ + "powermodel", + "junit", + "mockito", + ], +} + diff --git a/tools/powermodel/TEST_MAPPING b/tools/powermodel/TEST_MAPPING new file mode 100644 index 000000000000..c8db339ce23b --- /dev/null +++ b/tools/powermodel/TEST_MAPPING @@ -0,0 +1,8 @@ +{ + "presubmit": [ + { + "name": "powermodel-test" + } + ] +} + diff --git a/tools/powermodel/src/com/android/powermodel/ActivityReport.java b/tools/powermodel/src/com/android/powermodel/ActivityReport.java new file mode 100644 index 000000000000..4a8f63370cda --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/ActivityReport.java @@ -0,0 +1,92 @@ +/* + * 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.powermodel; + +import java.util.Collection; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.google.common.collect.ImmutableList; + +/** + * ActivityReport contains the summary of the activity that consumes power + * as reported by batterystats or statsd. + */ +public class ActivityReport { + private AppList<AppActivity> mApps; + + public ImmutableList<AppActivity> getAllApps() { + return mApps.getAllApps(); + } + + public ImmutableList<AppActivity> getRegularApps() { + return mApps.getRegularApps(); + } + + public List<AppActivity> findApp(String pkg) { + return mApps.findApp(pkg); + } + + public AppActivity findApp(SpecialApp specialApp) { + return mApps.findApp(specialApp); + } + + /** + * Find a component in the GLOBAL app. + * <p> + * Returns null if either the global app doesn't exist (bad data?) or the component + * doesn't exist in the global app. + */ + public ComponentActivity findGlobalComponent(Component component) { + final AppActivity global = mApps.findApp(SpecialApp.GLOBAL); + if (global == null) { + return null; + } + return global.getComponentActivity(component); + } + + public static class Builder { + private AppList.Builder<AppActivity,AppActivity.Builder> mApps = new AppList.Builder(); + + public Builder() { + } + + public ActivityReport build() { + final ActivityReport result = new ActivityReport(); + result.mApps = mApps.build(); + return result; + } + + public void addActivity(Component component, Collection<ComponentActivity> activities) { + for (final ComponentActivity activity: activities) { + addActivity(component, activity); + } + } + + public void addActivity(Component component, ComponentActivity activity) { + AppActivity.Builder app = mApps.get(activity.attribution); + if (app == null) { + app = new AppActivity.Builder(); + app.setAttribution(activity.attribution); + mApps.put(activity.attribution, app); + } + app.addComponentActivity(component, activity); + } + } +} diff --git a/tools/powermodel/src/com/android/powermodel/AppActivity.java b/tools/powermodel/src/com/android/powermodel/AppActivity.java new file mode 100644 index 000000000000..b87426ce0dc3 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/AppActivity.java @@ -0,0 +1,80 @@ +/* + * 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.powermodel; + +import java.util.HashMap; + +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +public class AppActivity extends AppInfo { + + private ImmutableMap<Component, ComponentActivity> mComponents; + // TODO: power rails + // private ImmutableMap<Component, PowerRailActivity> mRails; + + private AppActivity() { + } + + /** + * Returns the {@link ComponentActivity} for the {@link Component} provided, + * or null if this AppActivity does not have that component. + * @more + * If there is no ComponentActivity for a particular Component, then + * there was no usage associated with that app for the app in question. + */ + public ComponentActivity getComponentActivity(Component component) { + return mComponents.get(component); + } + + public ImmutableSet<Component> getComponents() { + return mComponents.keySet(); + } + + public ImmutableMap<Component,ComponentActivity> getComponentActivities() { + return mComponents; + } + + // TODO: power rails + // public ComponentActivity getPowerRail(Component component) { + // return mComponents.get(component); + // } + // + // public Set<Component> getPowerRails() { + // return mComponents.keySet(); + // } + + public static class Builder extends AppInfo.Builder<AppActivity> { + private HashMap<Component, ComponentActivity> mComponents = new HashMap(); + // TODO power rails. + + public Builder() { + } + + public AppActivity build() { + final AppActivity result = new AppActivity(); + init(result); + result.mComponents = ImmutableMap.copyOf(mComponents); + return result; + } + + public void addComponentActivity(Component component, ComponentActivity activity) { + mComponents.put(component, activity); + } + } +} diff --git a/tools/powermodel/src/com/android/powermodel/AppInfo.java b/tools/powermodel/src/com/android/powermodel/AppInfo.java new file mode 100644 index 000000000000..208339e8e491 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/AppInfo.java @@ -0,0 +1,56 @@ +/* + * 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.powermodel; + +class AppInfo { + private AttributionKey mAttribution; + + protected AppInfo() { + } + + public boolean hasPackage(String pkg) { + return mAttribution.hasPackage(pkg); + } + + public AttributionKey getAttribution() { + return mAttribution; + } + + abstract static class Builder<APP extends AppInfo> { + private AttributionKey mAttribution; + + public Builder() { + } + + public abstract APP build(); + + protected void init(AppInfo app) { + if (mAttribution == null) { + throw new RuntimeException("setAttribution(AttributionKey attribution) not called"); + } + app.mAttribution = mAttribution; + } + + public void setAttribution(AttributionKey attribution) { + mAttribution = attribution; + } + + public AttributionKey getAttribution() { + return mAttribution; + } + } +} diff --git a/tools/powermodel/src/com/android/powermodel/AppList.java b/tools/powermodel/src/com/android/powermodel/AppList.java new file mode 100644 index 000000000000..19572fc1cf15 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/AppList.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.powermodel; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +class AppList<APP extends AppInfo> { + private ImmutableList<APP> mAllApps; + private ImmutableList<APP> mRegularApps; + private ImmutableMap<SpecialApp,APP> mSpecialApps; + + private AppList() { + } + + public ImmutableList<APP> getAllApps() { + return mAllApps; + } + + public ImmutableList<APP> getRegularApps() { + return mRegularApps; + } + + public List<APP> findApp(String pkg) { + List<APP> result = new ArrayList(); + for (APP app: mRegularApps) { + if (app.hasPackage(pkg)) { + result.add(app); + } + } + return result; + } + + public APP findApp(SpecialApp specialApp) { + return mSpecialApps.get(specialApp); + } + + public static class Builder<APP extends AppInfo, BUILDER extends AppInfo.Builder<APP>> { + private final HashMap<AttributionKey,BUILDER> mApps = new HashMap(); + + public Builder() { + } + + public AppList<APP> build() { + final AppList<APP> result = new AppList(); + final ArrayList<APP> allApps = new ArrayList(); + final ArrayList<APP> regularApps = new ArrayList(); + final HashMap<SpecialApp,APP> specialApps = new HashMap(); + for (AppInfo.Builder<APP> app: mApps.values()) { + final AttributionKey attribution = app.getAttribution(); + final APP appActivity = app.build(); + allApps.add(appActivity); + if (attribution.isSpecialApp()) { + specialApps.put(attribution.getSpecialApp(), appActivity); + } else { + regularApps.add(appActivity); + } + } + result.mAllApps = ImmutableList.copyOf(allApps); + result.mRegularApps = ImmutableList.copyOf(regularApps); + result.mSpecialApps = ImmutableMap.copyOf(specialApps); + return result; + } + + public BUILDER get(AttributionKey attribution) { + return mApps.get(attribution); + } + + public BUILDER put(AttributionKey attribution, BUILDER app) { + return mApps.put(attribution, app); + } + } +} diff --git a/tools/powermodel/src/com/android/powermodel/AppPower.java b/tools/powermodel/src/com/android/powermodel/AppPower.java new file mode 100644 index 000000000000..283982b8eda6 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/AppPower.java @@ -0,0 +1,86 @@ +/* + * 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.powermodel; + +import java.util.HashMap; +import java.util.Set; + +import com.google.common.collect.ImmutableMap; + +public class AppPower extends AppInfo { + private ImmutableMap<Component, ComponentPower> mComponents; + + private double mAppPowerMah; + + + private AppPower() { + } + + /** + * Returns the {@link ComponentPower} for the {@link Component} provided, + * or null if this AppPower does not have that component. + * @more + * If the component was in the power profile for this device, there + * will be a component for it, even if there was no power used + * by that component. In that case, the + * {@link ComponentPower.getUsage() ComponentPower.getUsage()} + * method will return 0. + */ + public ComponentPower getComponentPower(Component component) { + return mComponents.get(component); + } + + public Set<Component> getComponents() { + return mComponents.keySet(); + } + + /** + * Return the total power used by this app. + */ + public double getAppPowerMah() { + return mAppPowerMah; + } + + /** + * Builder class for {@link AppPower} + */ + public static class Builder extends AppInfo.Builder<AppPower> { + private HashMap<Component, ComponentPower> mComponents = new HashMap(); + + public Builder() { + } + + public AppPower build() { + final AppPower result = new AppPower(); + init(result); + result.mComponents = ImmutableMap.copyOf(mComponents); + + // Add up the components + double appPowerMah = 0; + for (final ComponentPower componentPower: mComponents.values()) { + appPowerMah += componentPower.powerMah; + } + result.mAppPowerMah = appPowerMah; + + return result; + } + + public void addComponentPower(Component component, ComponentPower componentPower) { + mComponents.put(component, componentPower); + } + } +} diff --git a/tools/powermodel/src/com/android/powermodel/AttributionKey.java b/tools/powermodel/src/com/android/powermodel/AttributionKey.java new file mode 100644 index 000000000000..f19e0b7373c7 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/AttributionKey.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.powermodel; + +import java.util.Set; +import java.util.HashSet; + +import com.google.common.collect.ImmutableSet; + +public class AttributionKey { + private final int mUid; + private final ImmutableSet<String> mPackages; + private final SpecialApp mSpecialApp; + + public AttributionKey(SpecialApp specialApp) { + mUid = -1; + mPackages = ImmutableSet.of(); + mSpecialApp = specialApp; + } + + public AttributionKey(int uid, Set<String> packages) { + mUid = uid; + mPackages = ImmutableSet.copyOf(packages); + mSpecialApp = null; + } + + public ImmutableSet<String> getPackages() { + return mPackages; + } + + public boolean hasPackage(String pkg) { + return mPackages.contains(pkg); + } + + public SpecialApp getSpecialApp() { + return mSpecialApp; + } + + public boolean isSpecialApp() { + return mSpecialApp != null; + } + + /** + * Returns the uid for this attribution, or -1 if there isn't one + * (e.g. if it is a special app). + */ + public int getUid() { + return mUid; + } + @Override + public int hashCode() { + int hash = 7; + hash = (31 * hash) + (mUid); + hash = (31 * hash) + (mPackages == null ? 0 : mPackages.hashCode()); + hash = (31 * hash) + (mSpecialApp == null ? 0 : mSpecialApp.hashCode()); + return hash; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null) { + return false; + } + if (this.getClass() != o.getClass()) { + return false; + } + final AttributionKey that = (AttributionKey)o; + return (this.mUid == that.mUid) + && this.mPackages != null && this.mPackages.equals(that.mPackages) + && this.mSpecialApp != null && this.mSpecialApp.equals(that.mSpecialApp); + } + + @Override + public String toString() { + final StringBuilder str = new StringBuilder("AttributionKey("); + if (mUid >= 0) { + str.append(" uid="); + str.append(mUid); + } + if (mPackages.size() > 0) { + str.append(" packages=["); + for (String pkg: mPackages) { + str.append(' '); + str.append(pkg); + } + str.append(" ]"); + } + if (mSpecialApp != null) { + str.append(" specialApp="); + str.append(mSpecialApp.name()); + } + str.append(" )"); + return str.toString(); + } +} + diff --git a/tools/powermodel/src/com/android/powermodel/BatteryStatsReader.java b/tools/powermodel/src/com/android/powermodel/BatteryStatsReader.java new file mode 100644 index 000000000000..595c6612dc3b --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/BatteryStatsReader.java @@ -0,0 +1,74 @@ +/* + * 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.powermodel; + +import java.io.InputStream; +import java.io.IOException; +import com.android.powermodel.component.ModemBatteryStatsReader; + +public class BatteryStatsReader { + /** + * Construct a reader. + */ + public BatteryStatsReader() { + } + + /** + * Parse a powermodel.xml file and return a PowerProfile object. + * + * @param stream An InputStream containing the batterystats output. + * + * @throws ParseException Thrown when the xml file can not be parsed. + * @throws IOException When there is a problem reading the stream. + */ + public static ActivityReport parse(InputStream stream) throws ParseException, IOException { + final Parser parser = new Parser(stream); + return parser.parse(); + } + + /** + * Implements the reading and power model logic. + */ + private static class Parser { + final InputStream mStream; + final ActivityReport mResult; + RawBatteryStats mBs; + + /** + * Constructor to capture the parameters to read. + */ + Parser(InputStream stream) { + mStream = stream; + mResult = new ActivityReport(); + } + + /** + * Read the stream, parse it, and apply the power model. + * Do not call this more than once. + */ + ActivityReport parse() throws ParseException, IOException { + mBs = RawBatteryStats.parse(mStream); + + final ActivityReport.Builder report = new ActivityReport.Builder(); + + report.addActivity(Component.MODEM, ModemBatteryStatsReader.createActivities(mBs)); + + return report.build(); + } + } +} + diff --git a/tools/powermodel/src/com/android/powermodel/Component.java b/tools/powermodel/src/com/android/powermodel/Component.java new file mode 100644 index 000000000000..baae6d784c47 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/Component.java @@ -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 com.android.powermodel; + +/** + * The hardware components that use power on a device. + */ +public enum Component { + CPU, + SCREEN, + MODEM, + WIFI, + BLUETOOTH, + VIDEO, + AUDIO, + FLASHLIGHT, + CAMERA, + GPS, +} + diff --git a/tools/powermodel/src/com/android/powermodel/ComponentActivity.java b/tools/powermodel/src/com/android/powermodel/ComponentActivity.java new file mode 100644 index 000000000000..c1e2662b7b5f --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/ComponentActivity.java @@ -0,0 +1,42 @@ +/* + * 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.powermodel; + + +/** + * Encapsulates the work done by an app (including synthetic apps) that costs power. + */ +public class ComponentActivity { + public AttributionKey attribution; + + protected ComponentActivity(AttributionKey attribution) { + this.attribution = attribution; + } + + // TODO: Can we refactor what goes into the activities so this function + // doesn't need the global state? + /** + * Apply the power profile for this component. Subclasses should implement this + * to do the per-component calculatinos. The default implementation returns null. + * If this method returns null, then there will be no power associated for this + * component, which, for example is true with some of the GLOBAL activities. + */ + public ComponentPower applyProfile(ActivityReport activityReport, PowerProfile profile) { + return null; + } +} + diff --git a/tools/powermodel/src/com/android/powermodel/ComponentPower.java b/tools/powermodel/src/com/android/powermodel/ComponentPower.java new file mode 100644 index 000000000000..b22ff8731d6f --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/ComponentPower.java @@ -0,0 +1,41 @@ +/* + * 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.powermodel; + +/** + * The hardware component that uses power on a device. + * <p> + * This base class contains the total power used by each Component in an app. + * Subclasses may add more detail, which is a drill-down, but is not to be + * <i>added</i> to {@link #powerMah}. + */ +public abstract class ComponentPower<ACTIVITY extends ComponentActivity> { + /** + * The app associated with this ComponentPower. + */ + public AttributionKey attribution; + + /** + * The app activity that resulted in the power usage for this component. + */ + public ACTIVITY activity; + + /** + * The total power used by this component in this app. + */ + public double powerMah; +} diff --git a/tools/powermodel/src/com/android/powermodel/ComponentProfile.java b/tools/powermodel/src/com/android/powermodel/ComponentProfile.java new file mode 100644 index 000000000000..e76e5fb52481 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/ComponentProfile.java @@ -0,0 +1,20 @@ +/* + * 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.powermodel; + +public class ComponentProfile { +} diff --git a/tools/powermodel/src/com/android/powermodel/CsvParser.java b/tools/powermodel/src/com/android/powermodel/CsvParser.java new file mode 100644 index 000000000000..78cd261306fc --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/CsvParser.java @@ -0,0 +1,173 @@ +/* + * 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.powermodel; + +import java.io.InputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; + +/** + * Parses CSV. + * <p> + * Call parse() with an InputStream. + * <p> + * CsvLineProcessor.onLine() will be called for each line in the source document. + * <p> + * To simplify parsing and to protect against using too much memory for bad + * data, the maximum field length is {@link #MAX_FIELD_SIZE}. + */ +class CsvParser { + /** + * The maximum size of a single field in bytes. + */ + public static final int MAX_FIELD_SIZE = (8*1024)-1; + + /** + * Callback interface for each line of CSV as it is parsed. + */ + interface LineProcessor { + /** + * A line of CSV was parsed. + * + * @param lineNumber the line number in the file, starting at 1 + * @param fields the comma separated fields for the line + */ + void onLine(int lineNumber, ArrayList<String> fields) throws ParseException; + } + + /** + * Parse the CSV text in input, calling onto processor for each row. + */ + public static void parse(InputStream input, LineProcessor processor) + throws IOException, ParseException { + final Charset utf8 = StandardCharsets.UTF_8; + final byte[] buf = new byte[MAX_FIELD_SIZE+1]; + int lineNumber = 1; + int readPos = 0; + int prev = 0; + ArrayList<String> fields = new ArrayList<String>(); + boolean finalBuffer = false; + boolean escaping = false; + boolean sawQuote = false; + + while (!finalBuffer) { + int amt = input.read(buf, readPos, buf.length-readPos); + if (amt < 0) { + // No more data. Process whatever's left from before. + amt = readPos; + finalBuffer = true; + } else { + // Process whatever's left from before, plus the new data. + amt += readPos; + finalBuffer = false; + } + + // Process as much of this buffer as we can. + int fieldStart = 0; + int index = readPos; + int escapeIndex = escaping ? readPos : -1; + while (index < amt) { + byte c = buf[index]; + if (c == '\r' || c == '\n') { + if (escaping) { + // TODO: Quotes do not escape newlines in our CSV dialect, + // but we actually see some data where it should. + fields.add(new String(buf, fieldStart, escapeIndex-fieldStart)); + escapeIndex = -1; + escaping = false; + sawQuote = false; + } else { + fields.add(new String(buf, fieldStart, index-fieldStart)); + } + // Don't report blank lines + if (fields.size() > 1 || (fields.size() == 1 && fields.get(0).length() > 0)) { + processor.onLine(lineNumber, fields); + } + fields = new ArrayList<String>(); + if (!(c == '\n' && prev == '\r')) { + // Don't double increment for dos line endings. + lineNumber++; + } + fieldStart = index = index + 1; + } else { + if (escaping) { + // Field started with a " so quotes are escaped with " and commas + // don't matter except when following a single quote. + if (c == '"') { + if (sawQuote) { + buf[escapeIndex] = buf[index]; + escapeIndex++; + sawQuote = false; + } else { + sawQuote = true; + } + index++; + } else if (sawQuote && c == ',') { + fields.add(new String(buf, fieldStart, escapeIndex-fieldStart)); + fieldStart = index = index + 1; + escapeIndex = -1; + escaping = false; + sawQuote = false; + } else { + buf[escapeIndex] = buf[index]; + escapeIndex++; + index++; + sawQuote = false; + } + } else { + if (c == ',') { + fields.add(new String(buf, fieldStart, index-fieldStart)); + fieldStart = index + 1; + } else if (c == '"' && fieldStart == index) { + // First character is a " + escaping = true; + fieldStart = escapeIndex = index + 1; + } + index++; + } + } + prev = c; + } + + // A single field is greater than buf.length, so fail. + if (fieldStart == 0 && index == buf.length) { + throw new ParseException(lineNumber, "Line is too long: " + + new String(buf, 0, 20, utf8) + "..."); + } + + // Move whatever we didn't process to the beginning of the buffer + // and try again. + if (fieldStart != amt) { + readPos = (escaping ? escapeIndex : index) - fieldStart; + System.arraycopy(buf, fieldStart, buf, 0, readPos); + } else { + readPos = 0; + } + + // Process whatever's left over + if (finalBuffer) { + fields.add(new String(buf, 0, readPos)); + // If there is any content, return the last line. + if (fields.size() > 1 || (fields.size() == 1 && fields.get(0).length() > 0)) { + processor.onLine(lineNumber, fields); + } + } + } + } +} diff --git a/tools/powermodel/src/com/android/powermodel/ParseException.java b/tools/powermodel/src/com/android/powermodel/ParseException.java new file mode 100644 index 000000000000..e1f232bfc44f --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/ParseException.java @@ -0,0 +1,35 @@ +/* + * 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.powermodel; + +public class ParseException extends Exception { + public final int line; + + public ParseException(int line, String message, Throwable th) { + super(message, th); + this.line = line; + } + + public ParseException(int line, String message) { + this(line, message, null); + } + + public ParseException(String message) { + this(0, message, null); + } +} + diff --git a/tools/powermodel/src/com/android/powermodel/PowerProfile.java b/tools/powermodel/src/com/android/powermodel/PowerProfile.java new file mode 100644 index 000000000000..373a9c981ec5 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/PowerProfile.java @@ -0,0 +1,527 @@ +/* + * 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.powermodel; + +import java.io.InputStream; +import java.io.IOException; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import org.xml.sax.Attributes; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.DefaultHandler; + +import com.android.powermodel.component.AudioProfile; +import com.android.powermodel.component.BluetoothProfile; +import com.android.powermodel.component.CameraProfile; +import com.android.powermodel.component.CpuProfile; +import com.android.powermodel.component.FlashlightProfile; +import com.android.powermodel.component.GpsProfile; +import com.android.powermodel.component.ModemProfile; +import com.android.powermodel.component.ScreenProfile; +import com.android.powermodel.component.VideoProfile; +import com.android.powermodel.component.WifiProfile; +import com.android.powermodel.util.Conversion; + +public class PowerProfile { + + // Remaining fields from the android code for which the actual usage is unclear. + // battery.capacity + // bluetooth.controller.voltage + // modem.controller.voltage + // gps.voltage + // wifi.controller.voltage + // radio.on + // radio.scanning + // radio.active + // memory.bandwidths + // wifi.batchedscan + // wifi.scan + // wifi.on + // wifi.active + // wifi.controller.tx_levels + + private static Pattern RE_CLUSTER_POWER = Pattern.compile("cpu.cluster_power.cluster([0-9]*)"); + private static Pattern RE_CORE_SPEEDS = Pattern.compile("cpu.core_speeds.cluster([0-9]*)"); + private static Pattern RE_CORE_POWER = Pattern.compile("cpu.core_power.cluster([0-9]*)"); + + private HashMap<Component, ComponentProfile> mComponents = new HashMap(); + + /** + * Which element we are currently parsing. + */ + enum ElementState { + BEGIN, + TOP, + ITEM, + ARRAY, + VALUE + } + + /** + * Implements the reading and power model logic. + */ + private static class Parser { + private final InputStream mStream; + private final PowerProfile mResult; + + // Builders for the ComponentProfiles. + private final AudioProfile mAudio = new AudioProfile(); + private final BluetoothProfile mBluetooth = new BluetoothProfile(); + private final CameraProfile mCamera = new CameraProfile(); + private final CpuProfile.Builder mCpuBuilder = new CpuProfile.Builder(); + private final FlashlightProfile mFlashlight = new FlashlightProfile(); + private final GpsProfile.Builder mGpsBuilder = new GpsProfile.Builder(); + private final ModemProfile.Builder mModemBuilder = new ModemProfile.Builder(); + private final ScreenProfile mScreen = new ScreenProfile(); + private final VideoProfile mVideo = new VideoProfile(); + private final WifiProfile mWifi = new WifiProfile(); + + /** + * Constructor to capture the parameters to read. + */ + Parser(InputStream stream) { + mStream = stream; + mResult = new PowerProfile(); + } + + /** + * Read the stream, parse it, and apply the power model. + * Do not call this more than once. + */ + PowerProfile parse() throws ParseException { + final SAXParserFactory factory = SAXParserFactory.newInstance(); + AndroidResourceHandler handler = null; + try { + final SAXParser saxParser = factory.newSAXParser(); + + handler = new AndroidResourceHandler() { + @Override + public void onItem(Locator locator, String name, float value) + throws SAXParseException { + Parser.this.onItem(locator, name, value); + } + + @Override + public void onArray(Locator locator, String name, float[] value) + throws SAXParseException { + Parser.this.onArray(locator, name, value); + } + }; + + saxParser.parse(mStream, handler); + } catch (ParserConfigurationException ex) { + // Coding error, not runtime error. + throw new RuntimeException(ex); + } catch (SAXParseException ex) { + throw new ParseException(ex.getLineNumber(), ex.getMessage(), ex); + } catch (SAXException | IOException ex) { + // Make a guess about the line number. + throw new ParseException(handler.getLineNumber(), ex.getMessage(), ex); + } + + // TODO: This doesn't cover the multiple algorithms. Some refactoring will + // be necessary. + mResult.mComponents.put(Component.AUDIO, mAudio); + mResult.mComponents.put(Component.BLUETOOTH, mBluetooth); + mResult.mComponents.put(Component.CAMERA, mCamera); + mResult.mComponents.put(Component.CPU, mCpuBuilder.build()); + mResult.mComponents.put(Component.FLASHLIGHT, mFlashlight); + mResult.mComponents.put(Component.GPS, mGpsBuilder.build()); + mResult.mComponents.put(Component.MODEM, mModemBuilder.build()); + mResult.mComponents.put(Component.SCREEN, mScreen); + mResult.mComponents.put(Component.VIDEO, mVideo); + mResult.mComponents.put(Component.WIFI, mWifi); + + return mResult; + } + + /** + * Handles an item tag in the power_profile.xml. + */ + public void onItem(Locator locator, String name, float value) throws SAXParseException { + Integer index; + try { + if ("ambient.on".equals(name)) { + mScreen.ambientMa = value; + } else if ("audio".equals(name)) { + mAudio.onMa = value; + } else if ("bluetooth.controller.idle".equals(name)) { + mBluetooth.idleMa = value; + } else if ("bluetooth.controller.rx".equals(name)) { + mBluetooth.rxMa = value; + } else if ("bluetooth.controller.tx".equals(name)) { + mBluetooth.txMa = value; + } else if ("camera.avg".equals(name)) { + mCamera.onMa = value; + } else if ("camera.flashlight".equals(name)) { + mFlashlight.onMa = value; + } else if ("cpu.suspend".equals(name)) { + mCpuBuilder.setSuspendMa(value); + } else if ("cpu.idle".equals(name)) { + mCpuBuilder.setIdleMa(value); + } else if ("cpu.active".equals(name)) { + mCpuBuilder.setActiveMa(value); + } else if ((index = matchIndexedRegex(locator, RE_CLUSTER_POWER, name)) != null) { + mCpuBuilder.setClusterPower(index, value); + } else if ("gps.on".equals(name)) { + mGpsBuilder.setOnMa(value); + } else if ("modem.controller.sleep".equals(name)) { + mModemBuilder.setSleepMa(value); + } else if ("modem.controller.idle".equals(name)) { + mModemBuilder.setIdleMa(value); + } else if ("modem.controller.rx".equals(name)) { + mModemBuilder.setRxMa(value); + } else if ("radio.scanning".equals(name)) { + mModemBuilder.setScanningMa(value); + } else if ("screen.on".equals(name)) { + mScreen.onMa = value; + } else if ("screen.full".equals(name)) { + mScreen.fullMa = value; + } else if ("video".equals(name)) { + mVideo.onMa = value; + } else if ("wifi.controller.idle".equals(name)) { + mWifi.idleMa = value; + } else if ("wifi.controller.rx".equals(name)) { + mWifi.rxMa = value; + } else if ("wifi.controller.tx".equals(name)) { + mWifi.txMa = value; + } else { + // TODO: Uncomment this when we have all of the items parsed. + // throw new SAXParseException("Unhandled <item name=\"" + name + "\"> element", + // locator, ex); + + } + } catch (ParseException ex) { + throw new SAXParseException(ex.getMessage(), locator, ex); + } + } + + /** + * Handles an array tag in the power_profile.xml. + */ + public void onArray(Locator locator, String name, float[] value) throws SAXParseException { + Integer index; + try { + if ("cpu.clusters.cores".equals(name)) { + mCpuBuilder.setCoreCount(Conversion.toIntArray(value)); + } else if ((index = matchIndexedRegex(locator, RE_CORE_SPEEDS, name)) != null) { + mCpuBuilder.setCoreSpeeds(index, Conversion.toIntArray(value)); + } else if ((index = matchIndexedRegex(locator, RE_CORE_POWER, name)) != null) { + mCpuBuilder.setCorePower(index, value); + } else if ("gps.signalqualitybased".equals(name)) { + mGpsBuilder.setSignalMa(value); + } else if ("modem.controller.tx".equals(name)) { + mModemBuilder.setTxMa(value); + } else { + // TODO: Uncomment this when we have all of the items parsed. + // throw new SAXParseException("Unhandled <item name=\"" + name + "\"> element", + // locator, ex); + } + } catch (ParseException ex) { + throw new SAXParseException(ex.getMessage(), locator, ex); + } + } + } + + /** + * SAX XML handler that can parse the android resource files. + * In our case, all elements are floats. + */ + abstract static class AndroidResourceHandler extends DefaultHandler { + /** + * The set of names already processed. Map of name to line number. + */ + private HashMap<String,Integer> mAlreadySeen = new HashMap<String,Integer>(); + + /** + * Where in the document we are parsing. + */ + private Locator mLocator; + + /** + * Which element we are currently parsing. + */ + private ElementState mState = ElementState.BEGIN; + + /** + * Saved name from item and array elements. + */ + private String mName; + + /** + * The text that is currently being captured, or null if {@link #startCapturingText()} + * has not been called. + */ + private StringBuilder mText; + + /** + * The array values that have been parsed so for for this array. Null if we are + * not inside an array tag. + */ + private ArrayList<Float> mArray; + + /** + * Called when an item tag is encountered. + */ + public abstract void onItem(Locator locator, String name, float value) + throws SAXParseException; + + /** + * Called when an array is encountered. + */ + public abstract void onArray(Locator locator, String name, float[] value) + throws SAXParseException; + + /** + * If we have a Locator set, return the line number, otherwise return 0. + */ + public int getLineNumber() { + return mLocator != null ? mLocator.getLineNumber() : 0; + } + + /** + * Handle setting the parse location object. + */ + public void setDocumentLocator(Locator locator) { + mLocator = locator; + } + + /** + * Handle beginning of an element. + * + * @param ns Namespace uri + * @param ln Local name (inside namespace) + * @param element Tag name + */ + @Override + public void startElement(String ns, String ln, String element, + Attributes attr) throws SAXException { + switch (mState) { + case BEGIN: + // Outer element, we don't care the tag name. + mState = ElementState.TOP; + return; + case TOP: + if ("item".equals(element)) { + mState = ElementState.ITEM; + saveNameAttribute(attr); + startCapturingText(); + return; + } else if ("array".equals(element)) { + mState = ElementState.ARRAY; + mArray = new ArrayList<Float>(); + saveNameAttribute(attr); + return; + } + break; + case ARRAY: + if ("value".equals(element)) { + mState = ElementState.VALUE; + startCapturingText(); + return; + } + break; + } + throw new SAXParseException("unexpected element: '" + element + "'", mLocator); + } + + /** + * Handle end of an element. + * + * @param ns Namespace uri + * @param ln Local name (inside namespace) + * @param element Tag name + */ + @Override + public void endElement(String ns, String ln, String element) throws SAXException { + switch (mState) { + case ITEM: { + float value = parseFloat(finishCapturingText()); + mState = ElementState.TOP; + onItem(mLocator, mName, value); + break; + } + case ARRAY: { + final int N = mArray.size(); + float[] values = new float[N]; + for (int i=0; i<N; i++) { + values[i] = mArray.get(i); + } + mArray = null; + mState = ElementState.TOP; + onArray(mLocator, mName, values); + break; + } + case VALUE: { + mArray.add(parseFloat(finishCapturingText())); + mState = ElementState.ARRAY; + break; + } + } + } + + /** + * Interstitial text received. + * + * @throws SAXException if there shouldn't be non-whitespace text here + */ + @Override + public void characters(char text[], int start, int length) throws SAXException { + if (mText == null && length > 0 && !isWhitespace(text, start, length)) { + throw new SAXParseException("unexpected text: '" + + firstLine(text, start, length).trim() + "'", mLocator); + } + if (mText != null) { + mText.append(text, start, length); + } + } + + /** + * Begin collecting text from inside an element. + */ + private void startCapturingText() { + if (mText != null) { + throw new RuntimeException("ASSERTION FAILED: Shouldn't be already capturing" + + " text. mState=" + mState.name() + + " line=" + mLocator.getLineNumber() + + " column=" + mLocator.getColumnNumber()); + } + mText = new StringBuilder(); + } + + /** + * Stop capturing text from inside an element. + * + * @return the captured text + */ + private String finishCapturingText() { + if (mText == null) { + throw new RuntimeException("ASSERTION FAILED: Should already be capturing" + + " text. mState=" + mState.name() + + " line=" + mLocator.getLineNumber() + + " column=" + mLocator.getColumnNumber()); + } + final String result = mText.toString().trim(); + mText = null; + return result; + } + + /** + * Get the "name" attribute. + * + * @throws SAXParseException if the name attribute is not present or if + * the name has already been seen in the file. + */ + private void saveNameAttribute(Attributes attr) throws SAXParseException { + final String name = attr.getValue("name"); + if (name == null) { + throw new SAXParseException("expected 'name' attribute", mLocator); + } + Integer prev = mAlreadySeen.put(name, mLocator.getLineNumber()); + if (prev != null) { + throw new SAXParseException("name '" + name + "' already seen on line: " + prev, + mLocator); + } + mName = name; + } + + /** + * Gets the float value of the string. + * + * @throws SAXParseException if 'text' can't be parsed as a float. + */ + private float parseFloat(String text) throws SAXParseException { + try { + return Float.parseFloat(text); + } catch (NumberFormatException ex) { + throw new SAXParseException("not a valid float value: '" + text + "'", + mLocator, ex); + } + } + } + + /** + * Return whether the given substring is all whitespace. + */ + private static boolean isWhitespace(char[] text, int start, int length) { + for (int i = start; i < (start + length); i++) { + if (!Character.isSpace(text[i])) { + return false; + } + } + return true; + } + + /** + * Return the contents of text up to the first newline. + */ + private static String firstLine(char[] text, int start, int length) { + // TODO: The line number will be wrong if we skip preceeding blank lines. + while (length > 0) { + if (Character.isSpace(text[start])) { + start++; + length--; + } + } + int newlen = 0; + for (; newlen < length; newlen++) { + final char c = text[newlen]; + if (c == '\n' || c == '\r') { + break; + } + } + return new String(text, start, newlen); + } + + /** + * If the pattern matches, return the first group of that as an Integer. + * If not return null. + */ + private static Integer matchIndexedRegex(Locator locator, Pattern pattern, String text) + throws SAXParseException { + final Matcher m = pattern.matcher(text); + if (m.matches()) { + try { + return Integer.parseInt(m.group(1)); + } catch (NumberFormatException ex) { + throw new SAXParseException("Invalid field name: '" + text + "'", locator, ex); + } + } else { + return null; + } + } + + public static PowerProfile parse(InputStream stream) throws ParseException { + return (new Parser(stream)).parse(); + } + + private PowerProfile() { + } + + public ComponentProfile getComponent(Component component) { + return mComponents.get(component); + } + +} diff --git a/tools/powermodel/src/com/android/powermodel/PowerReport.java b/tools/powermodel/src/com/android/powermodel/PowerReport.java new file mode 100644 index 000000000000..76ba67235b0a --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/PowerReport.java @@ -0,0 +1,101 @@ +/* + * 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.powermodel; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import com.google.common.collect.ImmutableMap; + +/** + * PowerReport contains the summary of all power used on a device + * as reported by batterystats or statsd, based on the power profile. + */ +public class PowerReport { + private AppList<AppPower> mApps; + private double mTotalPowerMah; + + private PowerReport() { + } + + /** + * The total power used by this device for this PowerReport. + */ + public double getTotalPowerMah() { + return mTotalPowerMah; + } + + public List<AppPower> getAllApps() { + return mApps.getAllApps(); + } + + public List<AppPower> getRegularApps() { + return mApps.getRegularApps(); + } + + public List<AppPower> findApp(String pkg) { + return mApps.findApp(pkg); + } + + public AppPower findApp(SpecialApp specialApp) { + return mApps.findApp(specialApp); + } + + public static PowerReport createReport(PowerProfile profile, ActivityReport activityReport) { + final PowerReport.Builder powerReport = new PowerReport.Builder(); + for (final AppActivity appActivity: activityReport.getAllApps()) { + final AppPower.Builder appPower = new AppPower.Builder(); + appPower.setAttribution(appActivity.getAttribution()); + + for (final ImmutableMap.Entry<Component,ComponentActivity> entry: + appActivity.getComponentActivities().entrySet()) { + final ComponentPower componentPower = entry.getValue() + .applyProfile(activityReport, profile); + if (componentPower != null) { + appPower.addComponentPower(entry.getKey(), componentPower); + } + } + + powerReport.add(appPower); + } + return powerReport.build(); + } + + private static class Builder { + private AppList.Builder mApps = new AppList.Builder(); + + public Builder() { + } + + public PowerReport build() { + final PowerReport report = new PowerReport(); + + report.mApps = mApps.build(); + + for (AppPower app: report.mApps.getAllApps()) { + report.mTotalPowerMah += app.getAppPowerMah(); + } + + return report; + } + + public void add(AppPower.Builder app) { + mApps.put(app.getAttribution(), app); + } + } +} diff --git a/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java b/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java new file mode 100644 index 000000000000..76c0482772f7 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java @@ -0,0 +1,1175 @@ +/* + * 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.powermodel; + +import java.io.InputStream; +import java.io.IOException; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +public class RawBatteryStats { + /** + * The factory objects for the records. Initialized in the static block. + */ + private static HashMap<String,RecordFactory> sFactories + = new HashMap<String,RecordFactory>(); + + /** + * The Record objects that have been parsed. + */ + private ArrayList<Record> mRecords = new ArrayList<Record>(); + + /** + * The Record objects that have been parsed, indexed by type. + * + * Don't use this before {@link #indexRecords()} has been called. + */ + private ImmutableMap<String,ImmutableList<Record>> mRecordsByType; + + /** + * The attribution keys for which we have data (corresponding to UIDs we've seen). + * <p> + * Does not include the synthetic apps. + * <p> + * Don't use this before {@link #indexRecords()} has been called. + */ + private ImmutableSet<AttributionKey> mApps; + + /** + * The warnings that have been issued during parsing. + */ + private ArrayList<Warning> mWarnings = new ArrayList<Warning>(); + + /** + * The version of the BatteryStats dumpsys that we are using. This value + * is set to -1 initially, and then when parsing the (hopefully) first + * line, 'vers', it is set to the correct version. + */ + private int mDumpsysVersion = -1; + + /** + * Enum used in the Line annotation to mark whether a field is expected to be + * system-wide or scoped to an app. + */ + public enum Scope { + SYSTEM, + UID + } + + /** + * Enum used to indicated the expected number of results. + */ + public enum Count { + SINGLE, + MULTIPLE + } + + /** + * Annotates classes that represent a line of CSV in the batterystats CSV + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + @interface Line { + String tag(); + Scope scope(); + Count count(); + } + + /** + * Annotates fields that should be parsed automatically from CSV + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + @interface Field { + /** + * The "column" of this field in the most recent version of the CSV. + * When parsing old versions, fields that were added will be automatically + * removed and the indices will be fixed up. + * + * The header fields (version, uid, category, type) will be automatically + * handled for the base Line type. The index 0 should start after those. + */ + int index(); + + /** + * First version that this field appears in. + */ + int added() default 0; + } + + /** + * Each line in the BatteryStats CSV is tagged with a category, that says + * which of the time collection modes was used for the data. + */ + public enum Category { + INFO("i"), + LAST("l"), + UNPLUGGED("u"), + CURRENT("c"); + + public final String tag; + + Category(String tag) { + this.tag = tag; + } + } + + /** + * Base class for all lines in a batterystats CSV file. + */ + public static class Record { + /** + * Whether all of the fields for the indicated version of this record + * have been filled in. + */ + public boolean complete; + + + @Field(index=-4) + public int lineVersion; + + @Field(index=-3) + public int uid; + + @Field(index=-2) + public Category category; + + @Field(index=-1) + public String lineType; + } + + @Line(tag="bt", scope=Scope.SYSTEM, count=Count.SINGLE) + public static class Battery extends Record { + // If which != STATS_SINCE_CHARGED, the csv will be "N/A" and we will get + // a parsing warning. Nobody uses anything other than STATS_SINCE_CHARGED. + @Field(index=0) + public int startCount; + + @Field(index=1) + public long whichBatteryRealtimeMs; + + @Field(index=2) + public long whichBatteryUptimeMs; + + @Field(index=3) + public long totalRealtimeMs; + + @Field(index=4) + public long totalUptimeMs; + + @Field(index=5) + public long getStartClockTimeMs; + + @Field(index=6) + public long whichBatteryScreenOffRealtimeMs; + + @Field(index=7) + public long whichBatteryScreenOffUptimeMs; + + @Field(index=8) + public long estimatedBatteryCapacityMah; + + @Field(index=9) + public long minLearnedBatteryCapacityMah; + + @Field(index=10) + public long maxLearnedBatteryCapacityMah; + + @Field(index=11) + public long screenDozeTimeMs; + } + + @Line(tag="gn", scope=Scope.SYSTEM, count=Count.SINGLE) + public static class GlobalNetwork extends Record { + @Field(index=0) + public long mobileRxTotalBytes; + + @Field(index=1) + public long mobileTxTotalBytes; + + @Field(index=2) + public long wifiRxTotalBytes; + + @Field(index=3) + public long wifiTxTotalBytes; + + @Field(index=4) + public long mobileRxTotalPackets; + + @Field(index=5) + public long mobileTxTotalPackets; + + @Field(index=6) + public long wifiRxTotalPackets; + + @Field(index=7) + public long wifiTxTotalPackets; + + @Field(index=8) + public long btRxTotalBytes; + + @Field(index=9) + public long btTxTotalBytes; + } + + @Line(tag="gmcd", scope=Scope.SYSTEM, count=Count.SINGLE) + public static class GlobalModemController extends Record { + @Field(index=0) + public long idleMs; + + @Field(index=1) + public long rxTimeMs; + + @Field(index=2) + public long powerMaMs; + + @Field(index=3) + public long[] txTimeMs; + } + + @Line(tag="m", scope=Scope.SYSTEM, count=Count.SINGLE) + public static class Misc extends Record { + @Field(index=0) + public long screenOnTimeMs; + + @Field(index=1) + public long phoneOnTimeMs; + + @Field(index=2) + public long fullWakeLockTimeTotalMs; + + @Field(index=3) + public long partialWakeLockTimeTotalMs; + + @Field(index=4) + public long mobileRadioActiveTimeMs; + + @Field(index=5) + public long mobileRadioActiveAdjustedTimeMs; + + @Field(index=6) + public long interactiveTimeMs; + + @Field(index=7) + public long powerSaveModeEnabledTimeMs; + + @Field(index=8) + public int connectivityChangeCount; + + @Field(index=9) + public long deepDeviceIdleModeTimeMs; + + @Field(index=10) + public long deepDeviceIdleModeCount; + + @Field(index=11) + public long deepDeviceIdlingTimeMs; + + @Field(index=12) + public long deepDeviceIdlingCount; + + @Field(index=13) + public long mobileRadioActiveCount; + + @Field(index=14) + public long mobileRadioActiveUnknownTimeMs; + + @Field(index=15) + public long lightDeviceIdleModeTimeMs; + + @Field(index=16) + public long lightDeviceIdleModeCount; + + @Field(index=17) + public long lightDeviceIdlingTimeMs; + + @Field(index=18) + public long lightDeviceIdlingCount; + + @Field(index=19) + public long lightLongestDeviceIdleModeTimeMs; + + @Field(index=20) + public long deepLongestDeviceIdleModeTimeMs; + } + + @Line(tag="nt", scope=Scope.UID, count=Count.SINGLE) + public static class Network extends Record { + @Field(index=0) + public long mobileRxBytes; + + @Field(index=1) + public long mobileTxBytes; + + @Field(index=2) + public long wifiRxBytes; + + @Field(index=3) + public long wifiTxBytes; + + @Field(index=4) + public long mobileRxPackets; + + @Field(index=5) + public long mobileTxPackets; + + @Field(index=6) + public long wifiRxPackets; + + @Field(index=7) + public long wifiTxPackets; + + // This is microseconds, because... batterystats. + @Field(index=8) + public long mobileRadioActiveTimeUs; + + @Field(index=9) + public long mobileRadioActiveCount; + + @Field(index=10) + public long btRxBytes; + + @Field(index=11) + public long btTxBytes; + + @Field(index=12) + public long mobileWakeupCount; + + @Field(index=13) + public long wifiWakeupCount; + + @Field(index=14) + public long mobileBgRxBytes; + + @Field(index=15) + public long mobileBgTxBytes; + + @Field(index=16) + public long wifiBgRxBytes; + + @Field(index=17) + public long wifiBgTxBytes; + + @Field(index=18) + public long mobileBgRxPackets; + + @Field(index=19) + public long mobileBgTxPackets; + + @Field(index=20) + public long wifiBgRxPackets; + + @Field(index=21) + public long wifiBgTxPackets; + } + + @Line(tag="sgt", scope=Scope.SYSTEM, count=Count.SINGLE) + public static class SignalStrengthTime extends Record { + @Field(index=0) + public long[] phoneSignalStrengthTimeMs; + } + + @Line(tag="sst", scope=Scope.SYSTEM, count=Count.SINGLE) + public static class SignalScanningTime extends Record { + @Field(index=0) + public long phoneSignalScanningTimeMs; + } + + @Line(tag="uid", scope=Scope.UID, count=Count.MULTIPLE) + public static class Uid extends Record { + @Field(index=0) + public int uidKey; + + @Field(index=1) + public String pkg; + } + + @Line(tag="vers", scope=Scope.SYSTEM, count=Count.SINGLE) + public static class Version extends Record { + @Field(index=0) + public int dumpsysVersion; + + @Field(index=1) + public int parcelVersion; + + @Field(index=2) + public String startPlatformVersion; + + @Field(index=3) + public String endPlatformVersion; + } + + /** + * Codes for the warnings to classify warnings without parsing them. + */ + public enum WarningId { + /** + * A row didn't have enough fields to match any records and let us extract + * a line type. + */ + TOO_FEW_FIELDS_FOR_LINE_TYPE, + + /** + * We couldn't find a Record for the given line type. + */ + NO_MATCHING_LINE_TYPE, + + /** + * Couldn't set the value of a field. Usually this is because the + * contents of a numeric type couldn't be parsed. + */ + BAD_FIELD_TYPE, + + /** + * There were extra field values in the input text. + */ + TOO_MANY_FIELDS, + + /** + * There were fields that we were expecting (for this version + * of the dumpsys) that weren't provided in the CSV data. + */ + NOT_ENOUGH_FIELDS, + + /** + * The dumpsys version in the 'vers' CSV line couldn't be parsed. + */ + BAD_DUMPSYS_VERSION + } + + /** + * A non-fatal problem detected during parsing. + */ + public static class Warning { + private int mLineNumber; + private WarningId mId; + private ArrayList<String> mFields; + private String mMessage; + private String[] mExtras; + + public Warning(int lineNumber, WarningId id, ArrayList<String> fields, String message, + String[] extras) { + mLineNumber = lineNumber; + mId = id; + mFields = fields; + mMessage = message; + mExtras = extras; + } + + public int getLineNumber() { + return mLineNumber; + } + + public ArrayList<String> getFields() { + return mFields; + } + + public String getMessage() { + return mMessage; + } + + public String[] getExtras() { + return mExtras; + } + } + + /** + * Base class for classes to set fields on Record objects via reflection. + */ + private abstract static class FieldSetter { + private int mIndex; + private int mAdded; + protected java.lang.reflect.Field mField; + + FieldSetter(int index, int added, java.lang.reflect.Field field) { + mIndex = index; + mAdded = added; + mField = field; + } + + String getName() { + return mField.getName(); + } + + int getIndex() { + return mIndex; + } + + int getAdded() { + return mAdded; + } + + boolean isArray() { + return mField.getType().isArray(); + } + + abstract void setField(int lineNumber, Record object, String value) throws ParseException; + abstract void setArray(int lineNumber, Record object, ArrayList<String> values, + int startIndex, int endIndex) throws ParseException; + + @Override + public String toString() { + final String className = getClass().getName(); + int startIndex = Math.max(className.lastIndexOf('.'), className.lastIndexOf('$')); + if (startIndex < 0) { + startIndex = 0; + } else { + startIndex++; + } + return className.substring(startIndex) + "(index=" + mIndex + " added=" + mAdded + + " field=" + mField.getName() + + " type=" + mField.getType().getSimpleName() + + ")"; + } + } + + /** + * Sets int fields on Record objects using reflection. + */ + private static class IntFieldSetter extends FieldSetter { + IntFieldSetter(int index, int added, java.lang.reflect.Field field) { + super(index, added, field); + } + + void setField(int lineNumber, Record object, String value) throws ParseException { + try { + mField.setInt(object, Integer.parseInt(value.trim())); + } catch (NumberFormatException ex) { + throw new ParseException(lineNumber, "can't parse as integer: " + value); + } catch (IllegalAccessException | IllegalArgumentException + | ExceptionInInitializerError ex) { + throw new RuntimeException(ex); + } + } + + void setArray(int lineNumber, Record object, ArrayList<String> values, + int startIndex, int endIndex) throws ParseException { + try { + final int[] array = new int[endIndex-startIndex]; + for (int i=startIndex; i<endIndex; i++) { + final String value = values.get(startIndex+i); + try { + array[i] = Integer.parseInt(value.trim()); + } catch (NumberFormatException ex) { + throw new ParseException(lineNumber, "can't parse field " + + i + " as integer: " + value); + } + } + mField.set(object, array); + } catch (IllegalAccessException | IllegalArgumentException + | ExceptionInInitializerError ex) { + throw new RuntimeException(ex); + } + } + } + + /** + * Sets long fields on Record objects using reflection. + */ + private static class LongFieldSetter extends FieldSetter { + LongFieldSetter(int index, int added, java.lang.reflect.Field field) { + super(index, added, field); + } + + void setField(int lineNumber, Record object, String value) throws ParseException { + try { + mField.setLong(object, Long.parseLong(value.trim())); + } catch (NumberFormatException ex) { + throw new ParseException(lineNumber, "can't parse as long: " + value); + } catch (IllegalAccessException | IllegalArgumentException + | ExceptionInInitializerError ex) { + throw new RuntimeException(ex); + } + } + + void setArray(int lineNumber, Record object, ArrayList<String> values, + int startIndex, int endIndex) throws ParseException { + try { + final long[] array = new long[endIndex-startIndex]; + for (int i=0; i<(endIndex-startIndex); i++) { + final String value = values.get(startIndex+i); + try { + array[i] = Long.parseLong(value.trim()); + } catch (NumberFormatException ex) { + throw new ParseException(lineNumber, "can't parse field " + + i + " as long: " + value); + } + } + mField.set(object, array); + } catch (IllegalAccessException | IllegalArgumentException + | ExceptionInInitializerError ex) { + throw new RuntimeException(ex); + } + } + } + + /** + * Sets String fields on Record objects using reflection. + */ + private static class StringFieldSetter extends FieldSetter { + StringFieldSetter(int index, int added, java.lang.reflect.Field field) { + super(index, added, field); + } + + void setField(int lineNumber, Record object, String value) throws ParseException { + try { + mField.set(object, value); + } catch (IllegalAccessException | IllegalArgumentException + | ExceptionInInitializerError ex) { + throw new RuntimeException(ex); + } + } + + void setArray(int lineNumber, Record object, ArrayList<String> values, + int startIndex, int endIndex) throws ParseException { + try { + final String[] array = new String[endIndex-startIndex]; + for (int i=0; i<(endIndex-startIndex); i++) { + array[i] = values.get(startIndex+1); + } + mField.set(object, array); + } catch (IllegalAccessException | IllegalArgumentException + | ExceptionInInitializerError ex) { + throw new RuntimeException(ex); + } + } + } + + /** + * Sets enum fields on Record objects using reflection. + * + * To be parsed automatically, enums must have a public final String tag + * field, which is the string that will appear in the csv for that enum value. + */ + private static class EnumFieldSetter extends FieldSetter { + private final HashMap<String,Enum> mTags = new HashMap<String,Enum>(); + + EnumFieldSetter(int index, int added, java.lang.reflect.Field field) { + super(index, added, field); + + // Build the mapping of tags to values. + final Class<?> fieldType = field.getType(); + + java.lang.reflect.Field tagField = null; + try { + tagField = fieldType.getField("tag"); + } catch (NoSuchFieldException ex) { + throw new RuntimeException("Missing tag field." + + " To be parsed automatically, enums must have" + + " a String field called tag. Enum class: " + fieldType.getName() + + " Containing class: " + field.getDeclaringClass().getName() + + " Field: " + field.getName()); + + } + if (!String.class.equals(tagField.getType())) { + throw new RuntimeException("Tag field is not string." + + " To be parsed automatically, enums must have" + + " a String field called tag. Enum class: " + fieldType.getName() + + " Containing class: " + field.getDeclaringClass().getName() + + " Field: " + field.getName() + + " Tag field type: " + tagField.getType().getName()); + } + + for (final Object enumValue: fieldType.getEnumConstants()) { + String tag = null; + try { + tag = (String)tagField.get(enumValue); + } catch (IllegalAccessException | IllegalArgumentException + | ExceptionInInitializerError ex) { + throw new RuntimeException(ex); + } + mTags.put(tag, (Enum)enumValue); + } + } + + void setField(int lineNumber, Record object, String value) throws ParseException { + final Enum enumValue = mTags.get(value); + if (enumValue == null) { + throw new ParseException(lineNumber, "Could not find enum for field " + + getName() + " for tag: " + value); + } + try { + mField.set(object, enumValue); + } catch (IllegalAccessException | IllegalArgumentException + | ExceptionInInitializerError ex) { + throw new RuntimeException(ex); + } + } + + void setArray(int lineNumber, Record object, ArrayList<String> values, + int startIndex, int endIndex) throws ParseException { + try { + final Object array = Array.newInstance(mField.getType().getComponentType(), + endIndex-startIndex); + for (int i=0; i<(endIndex-startIndex); i++) { + final String value = values.get(startIndex+i); + final Enum enumValue = mTags.get(value); + if (enumValue == null) { + throw new ParseException(lineNumber, "Could not find enum for field " + + getName() + " for tag: " + value); + } + Array.set(array, i, enumValue); + } + mField.set(object, array); + } catch (IllegalAccessException | IllegalArgumentException + | ExceptionInInitializerError ex) { + throw new RuntimeException(ex); + } + } + } + + /** + * Factory for the record classes. Uses reflection to create + * the fields. + */ + private static class RecordFactory { + private String mTag; + private Class<? extends Record> mSubclass; + private ArrayList<FieldSetter> mFieldSetters; + + RecordFactory(String tag, Class<? extends Record> subclass, + ArrayList<FieldSetter> fieldSetters) { + mTag = tag; + mSubclass = subclass; + mFieldSetters = fieldSetters; + } + + /** + * Create an object of one of the subclasses of Record, and fill + * in the fields marked with the Field annotation. + * + * @return a new Record with the fields filled in. If there are missing + * fields, the {@link Record.complete} field will be set to false. + */ + Record create(RawBatteryStats bs, int dumpsysVersion, int lineNumber, + ArrayList<String> fieldValues) { + final boolean debug = false; + Record record = null; + try { + if (debug) { + System.err.println("Creating object: " + mSubclass.getSimpleName()); + } + record = mSubclass.newInstance(); + } catch (IllegalAccessException | InstantiationException + | ExceptionInInitializerError | SecurityException ex) { + throw new RuntimeException("Exception creating " + mSubclass.getName() + + " for '" + mTag + "' record.", ex); + } + record.complete = true; + int fieldIndex = 0; + int setterIndex = 0; + while (fieldIndex < fieldValues.size() && setterIndex < mFieldSetters.size()) { + final FieldSetter setter = mFieldSetters.get(setterIndex); + + if (dumpsysVersion >= 0 && dumpsysVersion < setter.getAdded()) { + // The version being parsed doesn't have the field for this setter, + // so skip the setter but not the field. + setterIndex++; + continue; + } + + final String value = fieldValues.get(fieldIndex); + try { + if (debug) { + System.err.println(" setting field " + setter + " to: " + value); + } + if (setter.isArray()) { + setter.setArray(lineNumber, record, fieldValues, + fieldIndex, fieldValues.size()); + // The rest of the fields have been consumed. + fieldIndex = fieldValues.size(); + setterIndex = mFieldSetters.size(); + break; + } else { + setter.setField(lineNumber, record, value); + } + } catch (ParseException ex) { + bs.addWarning(lineNumber, WarningId.BAD_FIELD_TYPE, fieldValues, + ex.getMessage(), mTag, value); + record.complete = false; + } + + fieldIndex++; + setterIndex++; + } + + // If there are extra fields, this record is complete, there are just + // extra values, so we issue a warning but don't mark it incomplete. + if (fieldIndex < fieldValues.size()) { + bs.addWarning(lineNumber, WarningId.TOO_MANY_FIELDS, fieldValues, + "Line '" + mTag + "' has extra fields.", + mTag, Integer.toString(fieldIndex), Integer.toString(fieldValues.size())); + if (debug) { + for (int i=0; i<mFieldSetters.size(); i++) { + System.err.println(" setter: [" + i + "] " + mFieldSetters.get(i)); + } + } + } + + // If we have any fields that are missing, add a warning and return null. + for (; setterIndex < mFieldSetters.size(); setterIndex++) { + final FieldSetter setter = mFieldSetters.get(setterIndex); + if (dumpsysVersion >= 0 && dumpsysVersion >= setter.getAdded()) { + bs.addWarning(lineNumber, WarningId.NOT_ENOUGH_FIELDS, fieldValues, + "Line '" + mTag + "' missing field: index=" + setterIndex + + " name=" + setter.getName(), + mTag, Integer.toString(setterIndex)); + record.complete = false; + } + } + + return record; + } + } + + /** + * Parse the input stream and return a RawBatteryStats object. + */ + public static RawBatteryStats parse(InputStream input) throws ParseException, IOException { + final RawBatteryStats result = new RawBatteryStats(); + result.parseImpl(input); + return result; + } + + /** + * Get a record. + * <p> + * If multiple of that record are found, returns the first one. There will already + * have been a warning recorded if the count annotation did not match what was in the + * csv. + * <p> + * Returns null if there are no records of that type. + */ + public <T extends Record> T getSingle(Class<T> cl) { + final List<Record> list = mRecordsByType.get(cl.getName()); + if (list == null) { + return null; + } + // Notes: + // - List can never be empty because the list itself wouldn't have been added. + // - Cast is safe because list was populated based on class name (let's assume + // there's only one class loader involved here). + return (T)list.get(0); + } + + /** + * Get a record. + * <p> + * If multiple of that record are found, returns the first one that matches that uid. + * <p> + * Returns null if there are no records of that type that match the given uid. + */ + public <T extends Record> T getSingle(Class<T> cl, int uid) { + final List<Record> list = mRecordsByType.get(cl.getName()); + if (list == null) { + return null; + } + for (final Record record: list) { + if (record.uid == uid) { + // Cast is safe because list was populated based on class name (let's assume + // there's only one class loader involved here). + return (T)record; + } + } + return null; + } + + /** + * Get all the records of the given type. + */ + public <T extends Record> List<T> getMultiple(Class<T> cl) { + final List<Record> list = mRecordsByType.get(cl.getName()); + if (list == null) { + return ImmutableList.<T>of(); + } + // Cast is safe because list was populated based on class name (let's assume + // there's only one class loader involved here). + return ImmutableList.copyOf((List<T>)list); + } + + /** + * Get the UIDs that are covered by this batterystats dump. + */ + public Set<AttributionKey> getApps() { + return mApps; + } + + /** + * No public constructor. Use {@link #parse}. + */ + private RawBatteryStats() { + } + + /** + * Get the list of Record objects that were parsed from the csv. + */ + public List<Record> getRecords() { + return mRecords; + } + + /** + * Gets the warnings that were encountered during parsing. + */ + public List<Warning> getWarnings() { + return mWarnings; + } + + /** + * Implementation of the csv parsing. + */ + private void parseImpl(InputStream input) throws ParseException, IOException { + // Parse the csv + CsvParser.parse(input, new CsvParser.LineProcessor() { + @Override + public void onLine(int lineNumber, ArrayList<String> fields) + throws ParseException { + handleCsvLine(lineNumber, fields); + } + }); + + // Gather the records by class name for the getSingle() and getMultiple() functions. + indexRecords(); + + // Gather the uids from all the places UIDs come from, for getApps(). + indexApps(); + } + + /** + * Handle a line of CSV input, creating the right Record object. + */ + private void handleCsvLine(int lineNumber, ArrayList<String> fields) throws ParseException { + // The standard rows all have the 4 core fields. Anything less isn't what we're + // looking for. + if (fields.size() <= 4) { + addWarning(lineNumber, WarningId.TOO_FEW_FIELDS_FOR_LINE_TYPE, fields, + "Line with too few fields (" + fields.size() + ")", + Integer.toString(fields.size())); + return; + } + + final String lineType = fields.get(3); + + // Handle the vers line specially, because we need the version number + // to make the rest of the machinery work. + if ("vers".equals(lineType)) { + final String versionText = fields.get(4); + try { + mDumpsysVersion = Integer.parseInt(versionText); + } catch (NumberFormatException ex) { + addWarning(lineNumber, WarningId.BAD_DUMPSYS_VERSION, fields, + "Couldn't parse dumpsys version number: '" + versionText, + versionText); + } + } + + // Find the right factory. + final RecordFactory factory = sFactories.get(lineType); + if (factory == null) { + addWarning(lineNumber, WarningId.NO_MATCHING_LINE_TYPE, fields, + "No Record for line type '" + lineType + "'", + lineType); + return; + } + + // Create the record. + final Record record = factory.create(this, mDumpsysVersion, lineNumber, fields); + mRecords.add(record); + } + + /** + * Add to the list of warnings. + */ + private void addWarning(int lineNumber, WarningId id, + ArrayList<String> fields, String message, String... extras) { + mWarnings.add(new Warning(lineNumber, id, fields, message, extras)); + final boolean debug = false; + if (debug) { + final StringBuilder text = new StringBuilder("line "); + text.append(lineNumber); + text.append(": WARNING: "); + text.append(message); + text.append("\n fields: "); + for (int i=0; i<fields.size(); i++) { + final String field = fields.get(i); + if (field.indexOf('"') >= 0) { + text.append('"'); + text.append(field.replace("\"", "\"\"")); + text.append('"'); + } else { + text.append(field); + } + if (i != fields.size() - 1) { + text.append(','); + } + } + text.append('\n'); + for (String extra: extras) { + text.append(" extra: "); + text.append(extra); + text.append('\n'); + } + System.err.print(text.toString()); + } + } + + /** + * Group records by class name. + */ + private void indexRecords() { + final HashMap<String,ArrayList<Record>> map = new HashMap<String,ArrayList<Record>>(); + + // Iterate over all of the records + for (Record record: mRecords) { + final String className = record.getClass().getName(); + + ArrayList<Record> list = map.get(className); + if (list == null) { + list = new ArrayList<Record>(); + map.put(className, list); + } + + list.add(record); + } + + // Make it immutable + final HashMap<String,ImmutableList<Record>> result + = new HashMap<String,ImmutableList<Record>>(); + for (HashMap.Entry<String,ArrayList<Record>> entry: map.entrySet()) { + result.put(entry.getKey(), ImmutableList.copyOf(entry.getValue())); + } + + // Initialize here so uninitialized access will result in NPE. + mRecordsByType = ImmutableMap.copyOf(result); + } + + /** + * Collect the UIDs from the csv. + * + * They come from two places. + * <ul> + * <li>The uid to package name map entries ({@link #Uid}) at the beginning. + * <li>The uid fields of the rest of the entries, some of which might not + * have package names associated with them. + * </ul> + * + * TODO: Is this where we should also do the logic about the special UIDs? + */ + private void indexApps() { + final HashMap<Integer,HashSet<String>> uids = new HashMap<Integer,HashSet<String>>(); + + // The Uid rows, from which we get package names + for (Uid record: getMultiple(Uid.class)) { + HashSet<String> list = uids.get(record.uidKey); + if (list == null) { + list = new HashSet<String>(); + uids.put(record.uidKey, list); + } + list.add(record.pkg); + } + + // The uid fields of everything + for (Record record: mRecords) { + // The 0 in the INFO records isn't really root, it's just unfilled data. + // The root uid (0) will show up practically in every record, but don't force it. + if (record.category != Category.INFO) { + if (uids.get(record.uid) == null) { + // There is no other data about this UID, but it does exist! + uids.put(record.uid, new HashSet<String>()); + } + } + } + + // Turn our temporary lists of package names into AttributionKeys. + final HashSet<AttributionKey> result = new HashSet<AttributionKey>(); + for (HashMap.Entry<Integer,HashSet<String>> entry: uids.entrySet()) { + result.add(new AttributionKey(entry.getKey(), entry.getValue())); + } + + // Initialize here so uninitialized access will result in NPE. + mApps = ImmutableSet.copyOf(result); + } + + /** + * Init the factory classes. + */ + static { + for (Class<?> cl: RawBatteryStats.class.getClasses()) { + final Line lineAnnotation = cl.getAnnotation(Line.class); + if (lineAnnotation != null && Record.class.isAssignableFrom(cl)) { + final ArrayList<FieldSetter> fieldSetters = new ArrayList<FieldSetter>(); + + for (java.lang.reflect.Field field: cl.getFields()) { + final Field fa = field.getAnnotation(Field.class); + if (fa != null) { + final Class<?> fieldType = field.getType(); + final Class<?> innerType = fieldType.isArray() + ? fieldType.getComponentType() + : fieldType; + if (Integer.TYPE.equals(innerType)) { + fieldSetters.add(new IntFieldSetter(fa.index(), fa.added(), field)); + } else if (Long.TYPE.equals(innerType)) { + fieldSetters.add(new LongFieldSetter(fa.index(), fa.added(), field)); + } else if (String.class.equals(innerType)) { + fieldSetters.add(new StringFieldSetter(fa.index(), fa.added(), field)); + } else if (innerType.isEnum()) { + fieldSetters.add(new EnumFieldSetter(fa.index(), fa.added(), field)); + } else { + throw new RuntimeException("Unsupported field type '" + + fieldType.getName() + "' on " + + cl.getName() + "." + field.getName()); + } + } + } + // Sort by index + Collections.sort(fieldSetters, new Comparator<FieldSetter>() { + @Override + public int compare(FieldSetter a, FieldSetter b) { + return a.getIndex() - b.getIndex(); + } + }); + // Only the last one can be an array + for (int i=0; i<fieldSetters.size()-1; i++) { + if (fieldSetters.get(i).isArray()) { + throw new RuntimeException("Only the last (highest index) @Field" + + " in class " + cl.getName() + " can be an array: " + + fieldSetters.get(i).getName()); + } + } + // Add to the map + sFactories.put(lineAnnotation.tag(), new RecordFactory(lineAnnotation.tag(), + (Class<Record>)cl, fieldSetters)); + } + } + } +} + diff --git a/tools/powermodel/src/com/android/powermodel/SpecialApp.java b/tools/powermodel/src/com/android/powermodel/SpecialApp.java new file mode 100644 index 000000000000..df1e1fbda5f6 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/SpecialApp.java @@ -0,0 +1,85 @@ +/* + * 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.powermodel; + +/** + * Identifiers for well-known apps that have unique characteristics. + * + * @more + * This includes three categories: + * <ul> + * <li><b>Built-in system components</b> – These have predefined UIDs that are + * always the same. For example, the system UID is always 1000.</li> + * <li><b>Well known apps with shared UIDs</b> – These do not have predefined + * UIDs (i.e. are different on each device), but since they have shared UIDs + * with varying sets of package names (GmsCore is the canonical example), we + * have special logic to capture these into a single entity with a well defined + * key. These have the {@link #uid uid} field set to + * {@link Uid#UID_VARIES Uid.UID_VARIES}.</li> + * <li><b>Synthetic remainder app</b> – The {@link #REMAINDER REMAINDER} app doesn't + * represent a real app. It contains accounting for usage which is not attributed + * to any UID. This app has the {@link #uid uid} field set to + * {@link Uid#UID_SYNTHETIC Uid.UID_SYNTHETIC}.</li> + * </ul> + */ +public enum SpecialApp { + + /** + * Synthetic app that accounts for the remaining amount of resources used + * that is unaccounted for by apps, or overcounted because of inaccuracies + * in the model. + */ + REMAINDER(Uid.UID_SYNTHETIC), + + /** + * Synthetic app that holds system-wide numbers, for example the total amount + * of various resources used, device-wide. + */ + GLOBAL(Uid.UID_SYNTHETIC), + + SYSTEM(1000), + + GOOGLE_SERVICES(Uid.UID_VARIES); + + /** + * Constants for SpecialApps where the uid is not actually a UID. + */ + public static class Uid { + /** + * Constant to indicate that this special app does not have a fixed UID. + */ + public static final int UID_VARIES = -1; + + /** + * Constant to indicate that this special app is not actually an app with a UID. + * + * @see SpecialApp#REMAINDER + * @see SpecialApp#GLOBAL + */ + public static final int UID_SYNTHETIC = -2; + } + + /** + * The fixed UID value of this special app, or {@link #UID_VARIES} if there + * isn't one. + */ + public final int uid; + + private SpecialApp(int uid) { + this.uid = uid; + } +} diff --git a/tools/powermodel/src/com/android/powermodel/component/AudioProfile.java b/tools/powermodel/src/com/android/powermodel/component/AudioProfile.java new file mode 100644 index 000000000000..63ff3a6b09fa --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/AudioProfile.java @@ -0,0 +1,27 @@ +/* + * 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.powermodel.component; + +import java.util.Arrays; + +import com.android.powermodel.ComponentProfile; +import com.android.powermodel.ParseException; + +public class AudioProfile extends ComponentProfile { + public float onMa; +} + diff --git a/tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java b/tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java new file mode 100644 index 000000000000..8f5e7d0ae1df --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java @@ -0,0 +1,29 @@ +/* + * 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.powermodel.component; + +import java.util.Arrays; + +import com.android.powermodel.ComponentProfile; +import com.android.powermodel.ParseException; + +public class BluetoothProfile extends ComponentProfile { + public float idleMa; + public float rxMa; + public float txMa; +} + diff --git a/tools/powermodel/src/com/android/powermodel/component/CameraProfile.java b/tools/powermodel/src/com/android/powermodel/component/CameraProfile.java new file mode 100644 index 000000000000..8ee22d03268c --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/CameraProfile.java @@ -0,0 +1,27 @@ +/* + * 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.powermodel.component; + +import java.util.Arrays; + +import com.android.powermodel.ComponentProfile; +import com.android.powermodel.ParseException; + +public class CameraProfile extends ComponentProfile { + public float onMa; +} + diff --git a/tools/powermodel/src/com/android/powermodel/component/CpuProfile.java b/tools/powermodel/src/com/android/powermodel/component/CpuProfile.java new file mode 100644 index 000000000000..0b34fc82622a --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/CpuProfile.java @@ -0,0 +1,145 @@ +/* + * 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.powermodel.component; + +import java.util.Arrays; +import java.util.HashMap; + +import com.android.powermodel.ComponentProfile; +import com.android.powermodel.ParseException; + +public class CpuProfile extends ComponentProfile { + public float suspendMa; + public float idleMa; + public float activeMa; + public Cluster[] clusters; + + public static class Cluster { + public int coreCount; + public float onMa; + public Frequency[] frequencies; + } + + public static class Frequency { + public int speedHz; + public float onMa; + } + + public static class Builder { + private float mSuspendMa; + private float mIdleMa; + private float mActiveMa; + private int[] mCoreCount; + private HashMap<Integer,Float> mClusterOnPower = new HashMap<Integer,Float>(); + private HashMap<Integer,int[]> mCoreSpeeds = new HashMap<Integer,int[]>(); + private HashMap<Integer,float[]> mCorePower = new HashMap<Integer,float[]>(); + + public Builder() { + } + + public void setSuspendMa(float value) throws ParseException { + mSuspendMa = value; + } + + public void setIdleMa(float value) throws ParseException { + mIdleMa = value; + } + + public void setActiveMa(float value) throws ParseException { + mActiveMa = value; + } + + public void setCoreCount(int[] value) throws ParseException { + mCoreCount = Arrays.copyOf(value, value.length); + } + + public void setClusterPower(int cluster, float value) throws ParseException { + mClusterOnPower.put(cluster, value); + } + + public void setCoreSpeeds(int cluster, int[] value) throws ParseException { + mCoreSpeeds.put(cluster, Arrays.copyOf(value, value.length)); + float[] power = mCorePower.get(cluster); + if (power != null && value.length != power.length) { + throw new ParseException("length of cpu.core_speeds.cluster" + cluster + + " (" + value.length + ") is different from length of" + + " cpu.core_power.cluster" + cluster + " (" + power.length + ")"); + } + if (mCoreCount != null && cluster >= mCoreCount.length) { + throw new ParseException("cluster " + cluster + + " in cpu.core_speeds.cluster" + cluster + + " is larger than the number of clusters specified in cpu.clusters.cores (" + + mCoreCount.length + ")"); + } + } + + public void setCorePower(int cluster, float[] value) throws ParseException { + mCorePower.put(cluster, Arrays.copyOf(value, value.length)); + int[] speeds = mCoreSpeeds.get(cluster); + if (speeds != null && value.length != speeds.length) { + throw new ParseException("length of cpu.core_power.cluster" + cluster + + " (" + value.length + ") is different from length of" + + " cpu.clusters.cores" + cluster + " (" + speeds.length + ")"); + } + if (mCoreCount != null && cluster >= mCoreCount.length) { + throw new ParseException("cluster " + cluster + + " in cpu.core_power.cluster" + cluster + + " is larger than the number of clusters specified in cpu.clusters.cores (" + + mCoreCount.length + ")"); + } + } + + public CpuProfile build() throws ParseException { + final CpuProfile result = new CpuProfile(); + + // Validate cluster count + + // All null or none null + // TODO + + // Same size + // TODO + + // No gaps + // TODO + + // Fill in values + result.suspendMa = mSuspendMa; + result.idleMa = mIdleMa; + result.activeMa = mActiveMa; + if (mCoreCount != null) { + result.clusters = new Cluster[mCoreCount.length]; + for (int i = 0; i < result.clusters.length; i++) { + final Cluster cluster = result.clusters[i] = new Cluster(); + cluster.coreCount = mCoreCount[i]; + cluster.onMa = mClusterOnPower.get(i); + int[] speeds = mCoreSpeeds.get(i); + float[] power = mCorePower.get(i); + cluster.frequencies = new Frequency[speeds.length]; + for (int j = 0; j < speeds.length; j++) { + final Frequency freq = cluster.frequencies[j] = new Frequency(); + freq.speedHz = speeds[j]; + freq.onMa = power[j]; + } + } + } + + return result; + } + } +} + diff --git a/tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java b/tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java new file mode 100644 index 000000000000..c85f3ff236fd --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java @@ -0,0 +1,27 @@ +/* + * 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.powermodel.component; + +import java.util.Arrays; + +import com.android.powermodel.ComponentProfile; +import com.android.powermodel.ParseException; + +public class FlashlightProfile extends ComponentProfile { + public float onMa; +} + diff --git a/tools/powermodel/src/com/android/powermodel/component/GpsProfile.java b/tools/powermodel/src/com/android/powermodel/component/GpsProfile.java new file mode 100644 index 000000000000..83c06a7881ca --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/GpsProfile.java @@ -0,0 +1,53 @@ +/* + * 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.powermodel.component; + +import java.util.Arrays; + +import com.android.powermodel.ComponentProfile; +import com.android.powermodel.ParseException; + +public class GpsProfile extends ComponentProfile { + public float onMa; + public float[] signalQualityMa; + + public static class Builder { + private float onMa; + private float[] mSignalQualityMa; + + public Builder() { + } + + public void setOnMa(float value) throws ParseException { + onMa = value; + } + + public void setSignalMa(float[] value) throws ParseException { + mSignalQualityMa = value; + } + + public GpsProfile build() throws ParseException { + GpsProfile result = new GpsProfile(); + result.onMa = onMa; + result.signalQualityMa = mSignalQualityMa == null + ? new float[0] + : Arrays.copyOf(mSignalQualityMa, mSignalQualityMa.length); + return result; + } + } +} + diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemAppActivity.java b/tools/powermodel/src/com/android/powermodel/component/ModemAppActivity.java new file mode 100644 index 000000000000..cb70051f1ae6 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/ModemAppActivity.java @@ -0,0 +1,85 @@ +/* + * 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.powermodel.component; + +import com.android.powermodel.ActivityReport; +import com.android.powermodel.AttributionKey; +import com.android.powermodel.Component; +import com.android.powermodel.ComponentActivity; +import com.android.powermodel.PowerProfile; +import com.android.powermodel.util.Conversion; + +/** + * Encapsulates the work done by the celluar modem on behalf of an app. + */ +public class ModemAppActivity extends ComponentActivity { + /** + * Construct a new ModemAppActivity. + */ + public ModemAppActivity(AttributionKey attribution) { + super(attribution); + } + + /** + * The number of packets received by the app. + */ + public long rxPacketCount; + + /** + * The number of packets sent by the app. + */ + public long txPacketCount; + + @Override + public ModemAppPower applyProfile(ActivityReport activityReport, PowerProfile profile) { + // Profile + final ModemProfile modemProfile = (ModemProfile)profile.getComponent(Component.MODEM); + if (modemProfile == null) { + // TODO: This is kind of a big problem... Should this throw instead? + return null; + } + + // Activity + final ModemGlobalActivity global + = (ModemGlobalActivity)activityReport.findGlobalComponent(Component.MODEM); + if (global == null) { + return null; + } + + final double averageModemPowerMa = getAverageModemPowerMa(modemProfile); + final long totalPacketCount = global.rxPacketCount + global.txPacketCount; + final long appPacketCount = this.rxPacketCount + this.txPacketCount; + + final ModemAppPower result = new ModemAppPower(); + result.attribution = this.attribution; + result.activity = this; + result.powerMah = Conversion.msToHr( + (totalPacketCount > 0 ? (appPacketCount / (double)totalPacketCount) : 0) + * global.totalActiveTimeMs + * averageModemPowerMa); + return result; + } + + static final double getAverageModemPowerMa(ModemProfile profile) { + double sumMa = profile.getRxMa(); + for (float powerAtTxLevelMa: profile.getTxMa()) { + sumMa += powerAtTxLevelMa; + } + return sumMa / (profile.getTxMa().length + 1); + } +} + diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemAppPower.java b/tools/powermodel/src/com/android/powermodel/component/ModemAppPower.java new file mode 100644 index 000000000000..f5531272d0b9 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/ModemAppPower.java @@ -0,0 +1,24 @@ +/* + * 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.powermodel.component; + +import com.android.powermodel.Component; +import com.android.powermodel.ComponentPower; + +public class ModemAppPower extends ComponentPower<ModemAppActivity> { +} + diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemBatteryStatsReader.java b/tools/powermodel/src/com/android/powermodel/component/ModemBatteryStatsReader.java new file mode 100644 index 000000000000..6dbfbc24d1ef --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/ModemBatteryStatsReader.java @@ -0,0 +1,110 @@ +/* + * 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.powermodel.component; + +import java.util.ArrayList; +import java.util.List; +import com.android.powermodel.AttributionKey; +import com.android.powermodel.ComponentActivity; +import com.android.powermodel.RawBatteryStats; +import com.android.powermodel.SpecialApp; + +public class ModemBatteryStatsReader { + private ModemBatteryStatsReader() { + } + + public static List<ComponentActivity> createActivities(RawBatteryStats bs) { + final List<ComponentActivity> result = new ArrayList<ComponentActivity>(); + + // The whole system + createGlobal(result, bs); + + // The apps + createApps(result, bs); + + // The synthetic "cell" app. + createRemainder(result, bs); + + return result; + } + + private static void createGlobal(List<ComponentActivity> result, RawBatteryStats bs) { + final ModemGlobalActivity global + = new ModemGlobalActivity(new AttributionKey(SpecialApp.GLOBAL)); + + final RawBatteryStats.GlobalNetwork gn = bs.getSingle(RawBatteryStats.GlobalNetwork.class); + final RawBatteryStats.Misc misc = bs.getSingle(RawBatteryStats.Misc.class); + + // Null here just means no network activity. + if (gn != null && misc != null) { + global.rxPacketCount = gn.mobileRxTotalPackets; + global.txPacketCount = gn.mobileTxTotalPackets; + + global.totalActiveTimeMs = misc.mobileRadioActiveTimeMs; + } + + result.add(global); + } + + private static void createApps(List<ComponentActivity> result, RawBatteryStats bs) { + for (AttributionKey key: bs.getApps()) { + final int uid = key.getUid(); + final RawBatteryStats.Network network + = bs.getSingle(RawBatteryStats.Network.class, uid); + + // Null here just means no network activity. + if (network != null) { + final ModemAppActivity app = new ModemAppActivity(key); + + app.rxPacketCount = network.mobileRxPackets; + app.txPacketCount = network.mobileTxPackets; + + result.add(app); + } + } + } + + private static void createRemainder(List<ComponentActivity> result, RawBatteryStats bs) { + final RawBatteryStats.SignalStrengthTime strength + = bs.getSingle(RawBatteryStats.SignalStrengthTime.class); + final RawBatteryStats.SignalScanningTime scanning + = bs.getSingle(RawBatteryStats.SignalScanningTime.class); + final RawBatteryStats.Misc misc = bs.getSingle(RawBatteryStats.Misc.class); + + if (strength != null && scanning != null && misc != null) { + final ModemRemainderActivity remainder + = new ModemRemainderActivity(new AttributionKey(SpecialApp.REMAINDER)); + + // Signal strength buckets + remainder.strengthTimeMs = strength.phoneSignalStrengthTimeMs; + + // Time spent scanning + remainder.scanningTimeMs = scanning.phoneSignalScanningTimeMs; + + // Unaccounted for active time + final long totalActiveTimeMs = misc.mobileRadioActiveTimeMs; + long appActiveTimeMs = 0; + for (RawBatteryStats.Network nw: bs.getMultiple(RawBatteryStats.Network.class)) { + appActiveTimeMs += nw.mobileRadioActiveTimeUs / 1000; + } + remainder.activeTimeMs = totalActiveTimeMs - appActiveTimeMs; + + result.add(remainder); + } + } +} + diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemGlobalActivity.java b/tools/powermodel/src/com/android/powermodel/component/ModemGlobalActivity.java new file mode 100644 index 000000000000..a53b53eede2b --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/ModemGlobalActivity.java @@ -0,0 +1,51 @@ +/* + * 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.powermodel.component; + +import com.android.powermodel.ActivityReport; +import com.android.powermodel.AttributionKey; +import com.android.powermodel.ComponentActivity; +import com.android.powermodel.ComponentPower; +import com.android.powermodel.PowerProfile; + +/** + * Encapsulates total work done by the modem for the device. + */ +public class ModemGlobalActivity extends ComponentActivity { + /** + * Construct a new ModemGlobalActivity. + */ + public ModemGlobalActivity(AttributionKey attribution) { + super(attribution); + } + + /** + * Returns the total number of packets received in the whole device. + */ + public long rxPacketCount; + + /** + * Returns the total number of packets sent in the whole device. + */ + public long txPacketCount; + + /** + * Returns the total time the radio was active in the whole device. + */ + public long totalActiveTimeMs; +} + diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemProfile.java b/tools/powermodel/src/com/android/powermodel/component/ModemProfile.java new file mode 100644 index 000000000000..cda72ee205e3 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/ModemProfile.java @@ -0,0 +1,92 @@ +/* + * 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.powermodel.component; + +import java.util.Arrays; + +import com.android.powermodel.ComponentProfile; +import com.android.powermodel.ParseException; + +public class ModemProfile extends ComponentProfile { + public float sleepMa; + public float idleMa; + public float scanningMa; + public float rxMa; + public float[] txMa; + + public float getSleepMa() { + return sleepMa; + } + + public float getIdleMa() { + return idleMa; + } + + public float getRxMa() { + return rxMa; + } + + public float[] getTxMa() { + return Arrays.copyOf(txMa, txMa.length); + } + + public float getScanningMa() { + return scanningMa; + } + + public static class Builder { + private float mSleepMa; + private float mIdleMa; + private float mRxMa; + private float[] mTxMa; + private float mScanningMa; + + public Builder() { + } + + public void setSleepMa(float value) throws ParseException { + mSleepMa = value; + } + + public void setIdleMa(float value) throws ParseException { + mIdleMa = value; + } + + public void setRxMa(float value) throws ParseException { + mRxMa = value; + } + + public void setTxMa(float[] value) throws ParseException { + mTxMa = Arrays.copyOf(value, value.length); + } + + public void setScanningMa(float value) throws ParseException { + mScanningMa = value; + } + + public ModemProfile build() throws ParseException { + ModemProfile result = new ModemProfile(); + result.sleepMa = mSleepMa; + result.idleMa = mIdleMa; + result.rxMa = mRxMa; + result.txMa = mTxMa == null ? new float[0] : mTxMa; + result.scanningMa = mScanningMa; + return result; + } + } +} + diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemRemainderActivity.java b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderActivity.java new file mode 100644 index 000000000000..0e268c21d01d --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderActivity.java @@ -0,0 +1,87 @@ +/* + * 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.powermodel.component; + +import com.android.powermodel.ActivityReport; +import com.android.powermodel.AttributionKey; +import com.android.powermodel.Component; +import com.android.powermodel.ComponentActivity; +import com.android.powermodel.PowerProfile; +import com.android.powermodel.util.Conversion; + +/** + * Encapsulates the work done by the remaining + */ +public class ModemRemainderActivity extends ComponentActivity { + /** + * Construct a new ModemRemainderActivity. + */ + public ModemRemainderActivity(AttributionKey attribution) { + super(attribution); + } + + /** + * Number of milliseconds spent at each of the signal strengths. + */ + public long[] strengthTimeMs; + + /** + * Number of milliseconds spent scanning for a network. + */ + public long scanningTimeMs; + + /** + * Number of milliseconds that the radio is active for reasons other + * than an app transmitting and receiving data. + */ + public long activeTimeMs; + + @Override + public ModemRemainderPower applyProfile(ActivityReport activityReport, PowerProfile profile) { + // Profile + final ModemProfile modemProfile = (ModemProfile)profile.getComponent(Component.MODEM); + if (modemProfile == null) { + return null; + } + + // Activity + final ModemRemainderPower result = new ModemRemainderPower(); + result.attribution = this.attribution; + result.activity = this; + + // strengthMah + // TODO: If the array lengths don't match... then? + result.strengthMah = new double[this.strengthTimeMs.length]; + for (int i=0; i<this.strengthTimeMs.length; i++) { + result.strengthMah[i] = Conversion.msToHr( + this.strengthTimeMs[i] * modemProfile.getTxMa()[i]); + result.powerMah += result.strengthMah[i]; + } + + // scanningMah + result.scanningMah = Conversion.msToHr(this.scanningTimeMs * modemProfile.getScanningMa()); + result.powerMah += result.scanningMah; + + // activeMah + result.activeMah = Conversion.msToHr( + this.activeTimeMs * ModemAppActivity.getAverageModemPowerMa(modemProfile)); + result.powerMah += result.activeMah; + + return result; + } +} + diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemRemainderPower.java b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderPower.java new file mode 100644 index 000000000000..7f38cd342e2f --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderPower.java @@ -0,0 +1,30 @@ +/* + * 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.powermodel.component; + +import com.android.powermodel.Component; +import com.android.powermodel.ComponentPower; + +public class ModemRemainderPower extends ComponentPower<ModemRemainderActivity> { + + public double[] strengthMah; + + public double scanningMah; + + public double activeMah; +} + diff --git a/tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java b/tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java new file mode 100644 index 000000000000..e1051c69dec6 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java @@ -0,0 +1,29 @@ +/* + * 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.powermodel.component; + +import java.util.Arrays; + +import com.android.powermodel.ComponentProfile; +import com.android.powermodel.ParseException; + +public class ScreenProfile extends ComponentProfile { + public float onMa; + public float fullMa; + public float ambientMa; +} + diff --git a/tools/powermodel/src/com/android/powermodel/component/VideoProfile.java b/tools/powermodel/src/com/android/powermodel/component/VideoProfile.java new file mode 100644 index 000000000000..515279552245 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/VideoProfile.java @@ -0,0 +1,28 @@ +/* + * 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.powermodel.component; + +import java.util.Arrays; + +import com.android.powermodel.ComponentProfile; +import com.android.powermodel.ParseException; + +public class VideoProfile extends ComponentProfile { + public float onMa; +} + + diff --git a/tools/powermodel/src/com/android/powermodel/component/WifiProfile.java b/tools/powermodel/src/com/android/powermodel/component/WifiProfile.java new file mode 100644 index 000000000000..6f424bf0837d --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/WifiProfile.java @@ -0,0 +1,29 @@ +/* + * 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.powermodel.component; + +import java.util.Arrays; + +import com.android.powermodel.ComponentProfile; +import com.android.powermodel.ParseException; + +public class WifiProfile extends ComponentProfile { + public float idleMa; + public float rxMa; + public float txMa; +} + diff --git a/tools/powermodel/src/com/android/powermodel/util/Conversion.java b/tools/powermodel/src/com/android/powermodel/util/Conversion.java new file mode 100644 index 000000000000..e556c251a1c9 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/util/Conversion.java @@ -0,0 +1,47 @@ +/* + * 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.powermodel.util; + +public class Conversion { + + /** + * Convert the the float[] to an int[]. + * <p> + * Values are rounded to the nearest integral value. Null input + * results in null output. + */ + public static int[] toIntArray(float[] value) { + if (value == null) { + return null; + } + int[] result = new int[value.length]; + for (int i=0; i<result.length; i++) { + result[i] = (int)(value[i] + 0.5f); + } + return result; + } + + public static double msToHr(double ms) { + return ms / 3600.0 / 1000.0; + } + + /** + * No public constructor. + */ + private Conversion() { + } +} diff --git a/tools/powermodel/test-resource/bs.csv b/tools/powermodel/test-resource/bs.csv new file mode 100644 index 000000000000..6e84120168ce --- /dev/null +++ b/tools/powermodel/test-resource/bs.csv @@ -0,0 +1,7 @@ +9,0,i,vers,32,177,PPR1.180326.002,PQ1A.181105.015 +9,0,i,uid,10139,com.google.android.gm +9,0,l,gn,108060756,17293456,4896592,3290614,97840,72941,6903,8107,390,105 +9,0,l,m,2590630,0,384554,3943868,5113727,265,2565483,0,16,0,0,0,0,192,25331,3472068,17,3543323,14,614050,0 +9,10139,l,nt,13688501,534571,13842,7792,9925,5577,30,67,190051799,27,0,0,5,3,126020,42343,13842,7792,207,167,30,67 +9,0,l,sgt,3066958,0,34678,1643364,7045084 +9,0,l,sst,2443805 diff --git a/tools/powermodel/test-resource/power_profile.xml b/tools/powermodel/test-resource/power_profile.xml new file mode 100644 index 000000000000..8e388eadc608 --- /dev/null +++ b/tools/powermodel/test-resource/power_profile.xml @@ -0,0 +1,170 @@ +<?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. +--> + +<!-- Test power profile that parses correctly. --> +<device> + <item name="battery.capacity">2915</item> + + <!-- Number of cores each CPU cluster contains --> + <array name="cpu.clusters.cores"> + <value>4</value> + <value>2</value> + </array> + + <!-- Power consumption when CPU is suspended --> + <item name="cpu.suspend">1.3</item> + + <!-- Additional power consumption when CPU is in a kernel idle loop --> + <item name="cpu.idle">3.9</item> + + <!-- Additional power consumption by CPU excluding cluster and core when + running --> + <item name="cpu.active">18.33</item> + + <!-- Additional power consumption by CPU cluster0 itself when running + excluding cores in it --> + <item name="cpu.cluster_power.cluster0">2.41</item> + + <!-- Additional power consumption by CPU cluster1 itself when running + excluding cores in it --> + <item name="cpu.cluster_power.cluster1">5.29</item> + + <!-- Different CPU speeds as reported in + /sys/devices/system/cpu/cpu0/cpufreq/stats/scaling_available_frequencies --> + <array name="cpu.core_speeds.cluster0"> + <value>100000</value> + <value>303200</value> + <value>380000</value> + <value>476000</value> + <value>552800</value> + <value>648800</value> + <value>725600</value> + <value>802400</value> + <value>879200</value> + </array> + + <!-- Different CPU speeds as reported in + /sys/devices/system/cpu/cpu4/cpufreq/stats/scaling_available_frequencies --> + <array name="cpu.core_speeds.cluster1"> + <value>825600</value> + <value>902400</value> + <value>979200</value> + <value>1056000</value> + <value>1209600</value> + <value>1286400</value> + <value>1363200</value> + </array> + + <!-- Additional power used by a CPU core from cluster 0 when running at + different speeds, excluding cluster and active cost --> + <array name="cpu.core_power.cluster0"> + <value>0.29</value> + <value>0.63</value> + <value>1.23</value> + <value>1.24</value> + <value>2.47</value> + <value>2.54</value> + <value>3.60</value> + <value>3.64</value> + <value>4.42</value> + </array> + + <!-- Additional power used by a CPU core from cluster 1 when running at + different speeds, excluding cluster and active cost --> + <array name="cpu.core_power.cluster1"> + <value>28.98</value> + <value>31.40</value> + <value>33.33</value> + <value>40.12</value> + <value>44.10</value> + <value>90.14</value> + <value>100</value> + </array> + + <!-- Additional power used when screen is ambient mode --> + <item name="ambient.on">12</item> + + <!-- Additional power used when screen is turned on at minimum brightness --> + <item name="screen.on">102.4</item> + <!-- Additional power used when screen is at maximum brightness, compared to + screen at minimum brightness --> + <item name="screen.full">1234</item> + + <!-- Average power used by the camera flash module when on --> + <item name="camera.flashlight">1233.47</item> + + <!-- Average power use by the camera subsystem for a typical camera + application. Intended as a rough estimate for an application running a + preview and capturing approximately 10 full-resolution pictures per + minute. --> + <item name="camera.avg">941</item> + + <!-- Additional power used when video is playing --> + <item name="video">123</item> + + <!-- Additional power used when audio is playing --> + <item name="audio">12</item> + + <!-- Cellular modem related values.--> + <item name="modem.controller.sleep">1</item> + <item name="modem.controller.idle">44</item> + <item name="modem.controller.rx">11</item> + <array name="modem.controller.tx"> <!-- Strength 0 to 4 --> + <value>16</value> + <value>19</value> + <value>22</value> + <value>73</value> + <value>132</value> + </array> + <item name="modem.controller.voltage">1400</item> + <item name="radio.scanning">12</item> + + <!-- GPS related values.--> + <item name="gps.on">1</item> + <array name="gps.signalqualitybased"> <!-- Strength 0 to 1 --> + <value>88</value> + <value>07</value> + </array> + <item name="gps.voltage">1500</item> + + <!-- Idle Receive current for wifi radio in mA.--> + <item name="wifi.controller.idle">2</item> + + <!-- Rx current for wifi radio in mA.--> + <item name="wifi.controller.rx">123</item> + + <!-- Tx current for wifi radio in mA--> + <item name="wifi.controller.tx">333</item> + + <!-- Operating volatage for wifi radio in mV.--> + <item name="wifi.controller.voltage">3700</item> + + <!-- Idle current for bluetooth in mA.--> + <item name="bluetooth.controller.idle">0.02</item> + + <!-- Rx current for bluetooth in mA.--> + <item name="bluetooth.controller.rx">3</item> + + <!-- Tx current for bluetooth in mA--> + <item name="bluetooth.controller.tx">5</item> + + <!-- Operating voltage for bluetooth in mV.--> + <item name="bluetooth.controller.voltage">3300</item> + +</device> + + diff --git a/tools/powermodel/test/com/android/powermodel/BatteryStatsReaderTest.java b/tools/powermodel/test/com/android/powermodel/BatteryStatsReaderTest.java new file mode 100644 index 000000000000..e7b2c3746c85 --- /dev/null +++ b/tools/powermodel/test/com/android/powermodel/BatteryStatsReaderTest.java @@ -0,0 +1,80 @@ +/* + * 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.powermodel; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import org.junit.Test; +import org.junit.Assert; + +import com.android.powermodel.component.ModemAppActivity; +import com.android.powermodel.component.ModemGlobalActivity; +import com.android.powermodel.component.ModemRemainderActivity; + +/** + * Tests {@link BatteryStatsReader}. + */ +public class BatteryStatsReaderTest { + private static InputStream loadCsvStream() { + return BatteryStatsReaderTest.class.getResourceAsStream("/bs.csv"); + } + + @Test public void testModemGlobal() throws Exception { + final ActivityReport report = BatteryStatsReader.parse(loadCsvStream()); + + final AppActivity global = report.findApp(SpecialApp.GLOBAL); + Assert.assertNotNull(global); + + final ModemGlobalActivity modem + = (ModemGlobalActivity)global.getComponentActivity(Component.MODEM); + Assert.assertNotNull(modem); + Assert.assertEquals(97840, modem.rxPacketCount); + Assert.assertEquals(72941, modem.txPacketCount); + Assert.assertEquals(5113727, modem.totalActiveTimeMs); + } + + @Test public void testModemApp() throws Exception { + final ActivityReport report = BatteryStatsReader.parse(loadCsvStream()); + + final List<AppActivity> gmailList = report.findApp("com.google.android.gm"); + Assert.assertEquals(1, gmailList.size()); + final AppActivity gmail = gmailList.get(0); + + final ModemAppActivity modem + = (ModemAppActivity)gmail.getComponentActivity(Component.MODEM); + Assert.assertNotNull(modem); + Assert.assertEquals(9925, modem.rxPacketCount); + Assert.assertEquals(5577, modem.txPacketCount); + } + + @Test public void testModemRemainder() throws Exception { + final ActivityReport report = BatteryStatsReader.parse(loadCsvStream()); + + final AppActivity remainder = report.findApp(SpecialApp.REMAINDER); + Assert.assertNotNull(remainder); + + final ModemRemainderActivity modem + = (ModemRemainderActivity)remainder.getComponentActivity(Component.MODEM); + Assert.assertNotNull(modem); + Assert.assertArrayEquals(new long[] { 3066958, 0, 34678, 1643364, 7045084 }, + modem.strengthTimeMs); + Assert.assertEquals(2443805, modem.scanningTimeMs); + Assert.assertEquals(4923676, modem.activeTimeMs); + } +} diff --git a/tools/powermodel/test/com/android/powermodel/CsvParserTest.java b/tools/powermodel/test/com/android/powermodel/CsvParserTest.java new file mode 100644 index 000000000000..55dde412b78e --- /dev/null +++ b/tools/powermodel/test/com/android/powermodel/CsvParserTest.java @@ -0,0 +1,311 @@ +/* + * 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.powermodel; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import org.junit.Assert; +import org.junit.Test; + +/** + * Tests {@link PowerProfile} + */ +public class CsvParserTest { + + class LineCollector implements CsvParser.LineProcessor { + ArrayList<ArrayList<String>> results = new ArrayList<ArrayList<String>>(); + + @Override + public void onLine(int lineNumber, ArrayList<String> fields) { + System.out.println(lineNumber); + for (String str: fields) { + System.out.println("-->" + str + "<--"); + } + results.add(fields); + } + } + + private void assertEquals(String[][] expected, ArrayList<ArrayList<String>> results) { + final String[][] resultArray = new String[results.size()][]; + for (int i=0; i<results.size(); i++) { + final ArrayList<String> list = results.get(i); + resultArray[i] = list.toArray(new String[list.size()]); + } + Assert.assertArrayEquals(expected, resultArray); + } + + private String makeString(int length) { + final StringBuilder str = new StringBuilder(); + for (int i=0; i<length; i++) { + str.append('a'); + } + return str.toString(); + } + + @Test public void testEmpty() throws Exception { + final String text = ""; + System.out.println("Test: [" + text + "]"); + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + CsvParser.parse(is, collector); + + assertEquals(new String[][] { + }, collector.results); + } + + @Test public void testOnlyNewline() throws Exception { + final String text = "\n"; + System.out.println("Test: [" + text + "]"); + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + CsvParser.parse(is, collector); + + assertEquals(new String[][] { + }, collector.results); + } + + @Test public void testTwoLines() throws Exception { + final String text = "one,twoo,3\nfour,5,six\n"; + System.out.println("Test: [" + text + "]"); + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + CsvParser.parse(is, collector); + + assertEquals(new String[][] { + { "one", "twoo", "3", }, + { "four", "5", "six", }, + }, collector.results); + } + + + @Test public void testEscapedEmpty() throws Exception { + final String text = "\"\",\"\",\"\"\n"; + System.out.println("Test: [" + text + "]"); + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + CsvParser.parse(is, collector); + + assertEquals(new String[][] { + { "", "", "", }, + }, collector.results); + } + + @Test public void testEscapedText() throws Exception { + final String text = "\"one\",\"twoo\",\"3\"\n"; + System.out.println("Test: [" + text + "]"); + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + CsvParser.parse(is, collector); + + assertEquals(new String[][] { + { "one", "twoo", "3", }, + }, collector.results); + } + + @Test public void testEscapedQuotes() throws Exception { + final String text = "\"\"\"\",\"\"\"\"\"\",\"\"\"\"\n"; + System.out.println("Test: [" + text + "]"); + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + CsvParser.parse(is, collector); + + assertEquals(new String[][] { + { "\"", "\"\"", "\"", }, + }, collector.results); + } + + @Test public void testEscapedCommas() throws Exception { + final String text = "\",\",\",\",\",\"\n"; + System.out.println("Test: [" + text + "]"); + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + CsvParser.parse(is, collector); + + assertEquals(new String[][] { + { ",", ",", ",", }, + }, collector.results); + } + + @Test public void testEscapedQuotesAndCommas() throws Exception { + final String text = "\"\"\",\",\"\"\",\",\"\"\",\"\n"; + System.out.println("Test: [" + text + "]"); + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + CsvParser.parse(is, collector); + + assertEquals(new String[][] { + { "\",", "\",", "\",", }, + }, collector.results); + } + + @Test public void testNoNewline() throws Exception { + final String text = "a,b,c"; + System.out.println("Test: [" + text + "]"); + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + CsvParser.parse(is, collector); + + assertEquals(new String[][] { + { "a", "b", "c", } + }, collector.results); + } + + @Test public void testNoNewlineWithCommas() throws Exception { + final String text = "a,b,,"; + System.out.println("Test: [" + text + "]"); + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + CsvParser.parse(is, collector); + + assertEquals(new String[][] { + { "a", "b", "", "" } + }, collector.results); + } + + @Test public void testNoNewlineWithQuote() throws Exception { + final String text = "a,b,\",\""; + System.out.println("Test: [" + text + "]"); + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + CsvParser.parse(is, collector); + + assertEquals(new String[][] { + { "a", "b", "," } + }, collector.results); + } + + @Test public void testNoCommas() throws Exception { + final String text = "aasdfadfadfad"; + System.out.println("Test: [" + text + "]"); + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + CsvParser.parse(is, collector); + + assertEquals(new String[][] { + { "aasdfadfadfad", } + }, collector.results); + } + + @Test public void testMaxLength() throws Exception { + final String text = makeString(CsvParser.MAX_FIELD_SIZE); + System.out.println("Test: [" + text + "]"); + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + CsvParser.parse(is, collector); + + assertEquals(new String[][] { + { text, } + }, collector.results); + } + + @Test public void testMaxLengthTwice() throws Exception { + String big = makeString(CsvParser.MAX_FIELD_SIZE); + final String text = big + "," + big; + System.out.println("Test: [" + text + "]"); + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + CsvParser.parse(is, collector); + + assertEquals(new String[][] { + { big, big, } + }, collector.results); + } + + @Test public void testTooLong() throws Exception { + final String text = makeString(CsvParser.MAX_FIELD_SIZE+1); + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + try { + CsvParser.parse(is, collector); + throw new RuntimeException("Expected CsvParser.parse to throw ParseException"); + } catch (ParseException ex) { + // good + } + } + + @Test public void testBufferBoundary() throws Exception { + final String big = makeString(CsvParser.MAX_FIELD_SIZE-3); + final String text = big + ",b,c,d,e,f,g"; + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + CsvParser.parse(is, collector); + + assertEquals(new String[][] { + { big, "b", "c", "d", "e", "f", "g", } + }, collector.results); + } + + @Test public void testBufferBoundaryEmpty() throws Exception { + final String big = makeString(CsvParser.MAX_FIELD_SIZE-3); + final String text = big + ",,,,,,"; + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + CsvParser.parse(is, collector); + + assertEquals(new String[][] { + { big, "", "", "", "", "", "", } + }, collector.results); + } + + // Checks that the escaping and sawQuote behavior is correct at the buffer boundary + @Test public void testBufferBoundaryEscapingEven() throws Exception { + final String big = makeString(CsvParser.MAX_FIELD_SIZE-2); + final String text = big + ",\"\"\"\"\"\"\"\"\"\"\"\"," + big; + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + CsvParser.parse(is, collector); + + assertEquals(new String[][] { + { big, "\"\"\"\"\"", big } + }, collector.results); + } + + // Checks that the escaping and sawQuote behavior is correct at the buffer boundary + @Test public void testBufferBoundaryEscapingOdd() throws Exception { + final String big = makeString(CsvParser.MAX_FIELD_SIZE-3); + final String text = big + ",\"\"\"\"\"\"\"\"\"\"\"\"," + big; + final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + LineCollector collector = new LineCollector(); + + CsvParser.parse(is, collector); + + assertEquals(new String[][] { + { big, "\"\"\"\"\"", big } + }, collector.results); + } + +} diff --git a/tools/powermodel/test/com/android/powermodel/PowerProfileTest.java b/tools/powermodel/test/com/android/powermodel/PowerProfileTest.java new file mode 100644 index 000000000000..ab458311a98e --- /dev/null +++ b/tools/powermodel/test/com/android/powermodel/PowerProfileTest.java @@ -0,0 +1,159 @@ +/* + * 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.powermodel; + +import java.io.InputStream; + +import com.android.powermodel.component.CpuProfile; +import com.android.powermodel.component.AudioProfile; +import com.android.powermodel.component.BluetoothProfile; +import com.android.powermodel.component.CameraProfile; +import com.android.powermodel.component.FlashlightProfile; +import com.android.powermodel.component.GpsProfile; +import com.android.powermodel.component.ModemProfile; +import com.android.powermodel.component.ScreenProfile; +import com.android.powermodel.component.VideoProfile; +import com.android.powermodel.component.WifiProfile; +import org.junit.Assert; +import org.junit.Test; + +/* + * Additional tests needed: + * - CPU clusters with mismatching counts of speeds and coefficients + * - Extra fields + * - Name listed twice + */ + +/** + * Tests {@link PowerProfile} + */ +public class PowerProfileTest { + private static final float EPSILON = 0.00001f; + + private static InputStream loadPowerProfileStream() { + return PowerProfileTest.class.getResourceAsStream("/power_profile.xml"); + } + + @Test public void testReadGood() throws Exception { + final InputStream is = loadPowerProfileStream(); + + final PowerProfile profile = PowerProfile.parse(is); + + // Audio + final AudioProfile audio = (AudioProfile)profile.getComponent(Component.AUDIO); + Assert.assertEquals(12.0f, audio.onMa, EPSILON); + + // Bluetooth + final BluetoothProfile bluetooth + = (BluetoothProfile)profile.getComponent(Component.BLUETOOTH); + Assert.assertEquals(0.02f, bluetooth.idleMa, EPSILON); + Assert.assertEquals(3.0f, bluetooth.rxMa, EPSILON); + Assert.assertEquals(5.0f, bluetooth.txMa, EPSILON); + + // Camera + final CameraProfile camera = (CameraProfile)profile.getComponent(Component.CAMERA); + Assert.assertEquals(941.0f, camera.onMa, EPSILON); + + // CPU + final CpuProfile cpu = (CpuProfile)profile.getComponent(Component.CPU); + Assert.assertEquals(1.3f, cpu.suspendMa, EPSILON); + Assert.assertEquals(3.9f, cpu.idleMa, EPSILON); + Assert.assertEquals(18.33f, cpu.activeMa, EPSILON); + Assert.assertEquals(2, cpu.clusters.length); + // Cluster 0 + Assert.assertEquals(4, cpu.clusters[0].coreCount); + Assert.assertEquals(2.41f, cpu.clusters[0].onMa, EPSILON); + Assert.assertEquals(9, cpu.clusters[0].frequencies.length, EPSILON); + Assert.assertEquals(100000, cpu.clusters[0].frequencies[0].speedHz); + Assert.assertEquals(0.29f, cpu.clusters[0].frequencies[0].onMa, EPSILON); + Assert.assertEquals(303200, cpu.clusters[0].frequencies[1].speedHz); + Assert.assertEquals(0.63f, cpu.clusters[0].frequencies[1].onMa, EPSILON); + Assert.assertEquals(380000, cpu.clusters[0].frequencies[2].speedHz); + Assert.assertEquals(1.23f, cpu.clusters[0].frequencies[2].onMa, EPSILON); + Assert.assertEquals(476000, cpu.clusters[0].frequencies[3].speedHz); + Assert.assertEquals(1.24f, cpu.clusters[0].frequencies[3].onMa, EPSILON); + Assert.assertEquals(552800, cpu.clusters[0].frequencies[4].speedHz); + Assert.assertEquals(2.47f, cpu.clusters[0].frequencies[4].onMa, EPSILON); + Assert.assertEquals(648800, cpu.clusters[0].frequencies[5].speedHz); + Assert.assertEquals(2.54f, cpu.clusters[0].frequencies[5].onMa, EPSILON); + Assert.assertEquals(725600, cpu.clusters[0].frequencies[6].speedHz); + Assert.assertEquals(3.60f, cpu.clusters[0].frequencies[6].onMa, EPSILON); + Assert.assertEquals(802400, cpu.clusters[0].frequencies[7].speedHz); + Assert.assertEquals(3.64f, cpu.clusters[0].frequencies[7].onMa, EPSILON); + Assert.assertEquals(879200, cpu.clusters[0].frequencies[8].speedHz); + Assert.assertEquals(4.42f, cpu.clusters[0].frequencies[8].onMa, EPSILON); + // Cluster 1 + Assert.assertEquals(2, cpu.clusters[1].coreCount); + Assert.assertEquals(5.29f, cpu.clusters[1].onMa, EPSILON); + Assert.assertEquals(7, cpu.clusters[1].frequencies.length, EPSILON); + Assert.assertEquals(825600, cpu.clusters[1].frequencies[0].speedHz); + Assert.assertEquals(28.98f, cpu.clusters[1].frequencies[0].onMa, EPSILON); + Assert.assertEquals(902400, cpu.clusters[1].frequencies[1].speedHz); + Assert.assertEquals(31.40f, cpu.clusters[1].frequencies[1].onMa, EPSILON); + Assert.assertEquals(979200, cpu.clusters[1].frequencies[2].speedHz); + Assert.assertEquals(33.33f, cpu.clusters[1].frequencies[2].onMa, EPSILON); + Assert.assertEquals(1056000, cpu.clusters[1].frequencies[3].speedHz); + Assert.assertEquals(40.12f, cpu.clusters[1].frequencies[3].onMa, EPSILON); + Assert.assertEquals(1209600, cpu.clusters[1].frequencies[4].speedHz); + Assert.assertEquals(44.10f, cpu.clusters[1].frequencies[4].onMa, EPSILON); + Assert.assertEquals(1286400, cpu.clusters[1].frequencies[5].speedHz); + Assert.assertEquals(90.14f, cpu.clusters[1].frequencies[5].onMa, EPSILON); + Assert.assertEquals(1363200, cpu.clusters[1].frequencies[6].speedHz); + Assert.assertEquals(100f, cpu.clusters[1].frequencies[6].onMa, EPSILON); + + // Flashlight + final FlashlightProfile flashlight + = (FlashlightProfile)profile.getComponent(Component.FLASHLIGHT); + Assert.assertEquals(1233.47f, flashlight.onMa, EPSILON); + + // GPS + final GpsProfile gps = (GpsProfile)profile.getComponent(Component.GPS); + Assert.assertEquals(1.0f, gps.onMa, EPSILON); + Assert.assertEquals(2, gps.signalQualityMa.length); + Assert.assertEquals(88.0f, gps.signalQualityMa[0], EPSILON); + Assert.assertEquals(7.0f, gps.signalQualityMa[1], EPSILON); + + // Modem + final ModemProfile modem = (ModemProfile)profile.getComponent(Component.MODEM); + Assert.assertEquals(1.0f, modem.sleepMa, EPSILON); + Assert.assertEquals(44.0f, modem.idleMa, EPSILON); + Assert.assertEquals(12.0f, modem.scanningMa, EPSILON); + Assert.assertEquals(11.0f, modem.rxMa, EPSILON); + Assert.assertEquals(5, modem.txMa.length); + Assert.assertEquals(16.0f, modem.txMa[0], EPSILON); + Assert.assertEquals(19.0f, modem.txMa[1], EPSILON); + Assert.assertEquals(22.0f, modem.txMa[2], EPSILON); + Assert.assertEquals(73.0f, modem.txMa[3], EPSILON); + Assert.assertEquals(132.0f, modem.txMa[4], EPSILON); + + // Screen + final ScreenProfile screen = (ScreenProfile)profile.getComponent(Component.SCREEN); + Assert.assertEquals(102.4f, screen.onMa, EPSILON); + Assert.assertEquals(1234.0f, screen.fullMa, EPSILON); + Assert.assertEquals(12.0f, screen.ambientMa, EPSILON); + + // Video + final VideoProfile video = (VideoProfile)profile.getComponent(Component.VIDEO); + Assert.assertEquals(123.0f, video.onMa, EPSILON); + + // Wifi + final WifiProfile wifi = (WifiProfile)profile.getComponent(Component.WIFI); + Assert.assertEquals(2.0f, wifi.idleMa, EPSILON); + Assert.assertEquals(123.0f, wifi.rxMa, EPSILON); + Assert.assertEquals(333.0f, wifi.txMa, EPSILON); + } +} diff --git a/tools/powermodel/test/com/android/powermodel/PowerReportTest.java b/tools/powermodel/test/com/android/powermodel/PowerReportTest.java new file mode 100644 index 000000000000..1a61737a4b2f --- /dev/null +++ b/tools/powermodel/test/com/android/powermodel/PowerReportTest.java @@ -0,0 +1,128 @@ +/* + * 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.powermodel; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import org.junit.Test; +import org.junit.Assert; + +import com.android.powermodel.component.ModemAppPower; +import com.android.powermodel.component.ModemRemainderPower; + +/** + * Tests {@link PowerReport}. + */ +public class PowerReportTest { + private static final double EPSILON = 0.001; + private static final double MS_PER_HR = 3600000.0; + + private static final double AVERAGE_MODEM_POWER = ((11+16+19+22+73+132) / 6.0); + private static final double GMAIL_MODEM_MAH = ((9925+5577) / (double)(97840+72941)) + * 5113727 * AVERAGE_MODEM_POWER * (1.0 / 3600 / 1000); + private static final double GMAIL_MAH + = GMAIL_MODEM_MAH; + + private static final double REMAINDER_MODEM_MAH + = (1.0 / 3600 / 1000) + * ((3066958 * 16) + (0 * 19) + (34678 * 22) + (1643364 * 73) + (7045084 * 132) + + (2443805 * 12) + + (4923676 * AVERAGE_MODEM_POWER)); + private static final double REMAINDER_MAH + = REMAINDER_MODEM_MAH; + + private static final double TOTAL_MAH + = GMAIL_MAH + + REMAINDER_MAH; + + private static InputStream loadPowerProfileStream() { + return PowerProfileTest.class.getResourceAsStream("/power_profile.xml"); + } + + private static InputStream loadCsvStream() { + return BatteryStatsReaderTest.class.getResourceAsStream("/bs.csv"); + } + + private static PowerReport loadPowerReport() throws Exception { + final PowerProfile profile = PowerProfile.parse(loadPowerProfileStream()); + final ActivityReport activity = BatteryStatsReader.parse(loadCsvStream()); + return PowerReport.createReport(profile, activity); + } + + @Test public void testModemApp() throws Exception { + final PowerReport report = loadPowerReport(); + + final List<AppPower> gmailList = report.findApp("com.google.android.gm"); + Assert.assertEquals(1, gmailList.size()); + final AppPower gmail = gmailList.get(0); + + final ModemAppPower modem = (ModemAppPower)gmail.getComponentPower(Component.MODEM); + Assert.assertNotNull(modem); + Assert.assertEquals(GMAIL_MODEM_MAH, modem.powerMah, EPSILON); + } + + @Test public void testModemRemainder() throws Exception { + final PowerReport report = loadPowerReport(); + + final AppPower remainder = report.findApp(SpecialApp.REMAINDER); + Assert.assertNotNull(remainder); + + final ModemRemainderPower modem + = (ModemRemainderPower)remainder.getComponentPower(Component.MODEM); + Assert.assertNotNull(modem); + + Assert.assertArrayEquals(new double[] { + 3066958 * 16.0 / MS_PER_HR, + 0 * 19.0 / MS_PER_HR, + 34678 * 22.0 / MS_PER_HR, + 1643364 * 73.0 / MS_PER_HR, + 7045084 * 132.0 / MS_PER_HR }, + modem.strengthMah, EPSILON); + Assert.assertEquals(2443805 * 12 / MS_PER_HR, modem.scanningMah, EPSILON); + Assert.assertEquals(4923676 * AVERAGE_MODEM_POWER / MS_PER_HR, modem.activeMah, EPSILON); + + Assert.assertEquals(REMAINDER_MODEM_MAH, modem.powerMah, EPSILON); + } + + @Test public void testAppTotal() throws Exception { + final PowerReport report = loadPowerReport(); + + final List<AppPower> gmailList = report.findApp("com.google.android.gm"); + Assert.assertEquals(1, gmailList.size()); + final AppPower gmail = gmailList.get(0); + + Assert.assertEquals(GMAIL_MAH, gmail.getAppPowerMah(), EPSILON); + } + + @Test public void testRemainderTotal() throws Exception { + final PowerReport report = loadPowerReport(); + + final AppPower remainder = report.findApp(SpecialApp.REMAINDER); + Assert.assertNotNull(remainder); + + Assert.assertEquals(REMAINDER_MAH, remainder.getAppPowerMah(), EPSILON); + } + + @Test public void testTotal() throws Exception { + final PowerReport report = loadPowerReport(); + + Assert.assertEquals(TOTAL_MAH, report.getTotalPowerMah(), EPSILON); + } +} + diff --git a/tools/powermodel/test/com/android/powermodel/RawBatteryStatsTest.java b/tools/powermodel/test/com/android/powermodel/RawBatteryStatsTest.java new file mode 100644 index 000000000000..fbcac41a9e1c --- /dev/null +++ b/tools/powermodel/test/com/android/powermodel/RawBatteryStatsTest.java @@ -0,0 +1,96 @@ +/* + * 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.powermodel; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import org.junit.Test; +import org.junit.Assert; + +/** + * Tests {@link RawBatteryStats}. + */ +public class RawBatteryStatsTest { + private static final int BS_VERSION = 32; + + private static InputStream makeCsv(String... lines) { + return makeCsv(BS_VERSION, lines); + } + + private static InputStream makeCsv(int version, String... lines) { + final StringBuilder result = new StringBuilder("9,0,i,vers,"); + result.append(version); + result.append(",177,PPR1.180326.002,PQ1A.181105.015\n"); + for (String line: lines) { + result.append(line); + result.append('\n'); + } + return new ByteArrayInputStream(result.toString().getBytes(StandardCharsets.UTF_8)); + } + + @Test public void testVersion() throws Exception { + final InputStream is = makeCsv(); + + final RawBatteryStats bs = RawBatteryStats.parse(is); + final List<RawBatteryStats.Record> records = bs.getRecords(); + final RawBatteryStats.Version line = (RawBatteryStats.Version)records.get(0); + + Assert.assertEquals(0, bs.getWarnings().size()); + Assert.assertEquals(true, line.complete); + + Assert.assertEquals(9, line.lineVersion); + Assert.assertEquals(0, line.uid); + Assert.assertEquals(RawBatteryStats.Category.INFO, line.category); + Assert.assertEquals("vers", line.lineType); + + Assert.assertEquals(BS_VERSION, line.dumpsysVersion); + Assert.assertEquals(177, line.parcelVersion); + Assert.assertEquals("PPR1.180326.002", line.startPlatformVersion); + Assert.assertEquals("PQ1A.181105.015", line.endPlatformVersion); + } + + @Test public void testUid() throws Exception { + final InputStream is = makeCsv("9,0,i,uid,1000,com.example.app"); + + final RawBatteryStats bs = RawBatteryStats.parse(is); + final List<RawBatteryStats.Record> records = bs.getRecords(); + final RawBatteryStats.Uid line = (RawBatteryStats.Uid)records.get(1); + + Assert.assertEquals(1000, line.uidKey); + Assert.assertEquals("com.example.app", line.pkg); + } + + @Test public void testVarargs() throws Exception { + final InputStream is = makeCsv("9,0,i,gmcd,1,2,3,4,5,6,7"); + + final RawBatteryStats bs = RawBatteryStats.parse(is); + final List<RawBatteryStats.Record> records = bs.getRecords(); + final RawBatteryStats.GlobalModemController line + = (RawBatteryStats.GlobalModemController)records.get(1); + + Assert.assertEquals(1, line.idleMs); + Assert.assertEquals(2, line.rxTimeMs); + Assert.assertEquals(3, line.powerMaMs); + Assert.assertEquals(4, line.txTimeMs.length); + Assert.assertEquals(4, line.txTimeMs[0]); + Assert.assertEquals(5, line.txTimeMs[1]); + Assert.assertEquals(6, line.txTimeMs[2]); + Assert.assertEquals(7, line.txTimeMs[3]); + } +} diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java index 1d4c435939db..d368136c7081 100644 --- a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java +++ b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java @@ -28,6 +28,7 @@ import com.sun.tools.javac.util.Position; import java.io.IOException; import java.io.PrintStream; +import java.net.URLEncoder; import java.util.Map; import java.util.Set; import java.util.TreeMap; @@ -38,7 +39,9 @@ import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; /** @@ -108,10 +111,25 @@ public class UnsupportedAppUsageProcessor extends AbstractProcessor { "startline", "startcol", "endline", - "endcol" + "endcol", + "properties" ); } + private String encodeAnnotationProperties(AnnotationMirror annotation) { + StringBuilder sb = new StringBuilder(); + for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> e + : annotation.getElementValues().entrySet()) { + if (sb.length() > 0) { + sb.append("&"); + } + sb.append(e.getKey().getSimpleName()) + .append("=") + .append(URLEncoder.encode(e.getValue().toString())); + } + return sb.toString(); + } + /** * Maps an annotated element to the source position of the @UnsupportedAppUsage annotation * attached to it. It returns CSV in the format: @@ -137,7 +155,8 @@ public class UnsupportedAppUsageProcessor extends AbstractProcessor { lines.getLineNumber(pair.fst.pos().getStartPosition()), lines.getColumnNumber(pair.fst.pos().getStartPosition()), lines.getLineNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)), - lines.getColumnNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions))); + lines.getColumnNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)), + encodeAnnotationProperties(unsupportedAppUsage)); } /** diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp index 8585ae9f3f61..88b7e2e9de21 100644 --- a/tools/stats_log_api_gen/main.cpp +++ b/tools/stats_log_api_gen/main.cpp @@ -1128,7 +1128,10 @@ write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp hadStringOrChain = true; fprintf(out, " jbyte* jbyte_array%d;\n", argIndex); fprintf(out, " const char* str%d;\n", argIndex); - fprintf(out, " if (arg%d != NULL) {\n", argIndex); + fprintf(out, + " if (arg%d != NULL && env->GetArrayLength(arg%d) > " + "0) {\n", + argIndex, argIndex); fprintf(out, " jbyte_array%d = " "env->GetByteArrayElements(arg%d, NULL);\n", diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 3ec8a4155292..364d5084fbc9 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -58,7 +58,7 @@ interface IWifiManager */ oneway void requestActivityInfo(in ResultReceiver result); - ParceledListSlice getConfiguredNetworks(); + ParceledListSlice getConfiguredNetworks(String packageName); ParceledListSlice getPrivilegedConfiguredNetworks(); @@ -90,11 +90,11 @@ interface IWifiManager List<ScanResult> getScanResults(String callingPackage); - void disconnect(String packageName); + boolean disconnect(String packageName); - void reconnect(String packageName); + boolean reconnect(String packageName); - void reassociate(String packageName); + boolean reassociate(String packageName); WifiInfo getConnectionInfo(String callingPackage); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 7aff03c00dd3..8dd6c771a924 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1067,7 +1067,7 @@ public class WifiManager { public List<WifiConfiguration> getConfiguredNetworks() { try { ParceledListSlice<WifiConfiguration> parceledList = - mService.getConfiguredNetworks(); + mService.getConfiguredNetworks(mContext.getOpPackageName()); if (parceledList == null) { return Collections.emptyList(); } @@ -1761,8 +1761,7 @@ public class WifiManager { @Deprecated public boolean disconnect() { try { - mService.disconnect(mContext.getOpPackageName()); - return true; + return mService.disconnect(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1786,8 +1785,7 @@ public class WifiManager { @Deprecated public boolean reconnect() { try { - mService.reconnect(mContext.getOpPackageName()); - return true; + return mService.reconnect(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1811,8 +1809,7 @@ public class WifiManager { @Deprecated public boolean reassociate() { try { - mService.reassociate(mContext.getOpPackageName()); - return true; + return mService.reassociate(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2132,14 +2129,14 @@ public class WifiManager { * existing networks. You should assume the network IDs can be different * after calling this method. * - * @return {@code false} Will always return true. + * @return {@code false}. * @deprecated There is no need to call this method - * {@link #addNetwork(WifiConfiguration)}, {@link #updateNetwork(WifiConfiguration)} * and {@link #removeNetwork(int)} already persist the configurations automatically. */ @Deprecated public boolean saveConfiguration() { - return true; + return false; } /** @@ -3406,6 +3403,11 @@ public class WifiManager { * @hide */ @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD, + android.Manifest.permission.NETWORK_STACK + }) public void connect(WifiConfiguration config, ActionListener listener) { if (config == null) throw new IllegalArgumentException("config cannot be null"); // Use INVALID_NETWORK_ID for arg1 when passing a config object @@ -3426,7 +3428,12 @@ public class WifiManager { * initialized again * @hide */ - @UnsupportedAppUsage + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD, + android.Manifest.permission.NETWORK_STACK + }) public void connect(int networkId, ActionListener listener) { if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative"); getChannel().sendMessage(CONNECT_NETWORK, networkId, putListener(listener)); @@ -3452,7 +3459,12 @@ public class WifiManager { * initialized again * @hide */ - @UnsupportedAppUsage + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD, + android.Manifest.permission.NETWORK_STACK + }) public void save(WifiConfiguration config, ActionListener listener) { if (config == null) throw new IllegalArgumentException("config cannot be null"); getChannel().sendMessage(SAVE_NETWORK, 0, putListener(listener), config); @@ -3471,7 +3483,12 @@ public class WifiManager { * initialized again * @hide */ - @UnsupportedAppUsage + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD, + android.Manifest.permission.NETWORK_STACK + }) public void forget(int netId, ActionListener listener) { if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative"); getChannel().sendMessage(FORGET_NETWORK, netId, putListener(listener)); @@ -3486,7 +3503,12 @@ public class WifiManager { * initialized again * @hide */ - @UnsupportedAppUsage + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD, + android.Manifest.permission.NETWORK_STACK + }) public void disable(int netId, ActionListener listener) { if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative"); getChannel().sendMessage(DISABLE_NETWORK, netId, putListener(listener)); @@ -3498,6 +3520,12 @@ public class WifiManager { * @param SSID, in the format of WifiConfiguration's SSID. * @hide */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD, + android.Manifest.permission.NETWORK_STACK + }) public void disableEphemeralNetwork(String SSID) { if (SSID == null) throw new IllegalArgumentException("SSID cannot be null"); try { diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index 529548f1d2b8..6622a2571870 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -184,6 +184,9 @@ public class WifiScanner { public static final String SCAN_PARAMS_SCAN_SETTINGS_KEY = "ScanSettings"; /** {@hide} */ public static final String SCAN_PARAMS_WORK_SOURCE_KEY = "WorkSource"; + /** {@hide} */ + public static final String REQUEST_PACKAGE_NAME_KEY = "PackageName"; + /** * scan configuration parameters to be sent to {@link #startBackgroundScan} */ @@ -798,6 +801,7 @@ public class WifiScanner { Bundle scanParams = new Bundle(); scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings); scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource); + scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); mAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, scanParams); } @@ -812,8 +816,11 @@ public class WifiScanner { int key = removeListener(listener); if (key == INVALID_KEY) return; validateChannel(); - mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key); + Bundle scanParams = new Bundle(); + scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); + mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key, scanParams); } + /** * reports currently available scan results on appropriate listeners * @return true if all scan results were reported correctly @@ -821,7 +828,10 @@ public class WifiScanner { @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean getScanResults() { validateChannel(); - Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0); + Bundle scanParams = new Bundle(); + scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); + Message reply = + mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0, 0, scanParams); return reply.what == CMD_OP_SUCCEEDED; } @@ -856,6 +866,7 @@ public class WifiScanner { Bundle scanParams = new Bundle(); scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings); scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource); + scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams); } @@ -870,7 +881,9 @@ public class WifiScanner { int key = removeListener(listener); if (key == INVALID_KEY) return; validateChannel(); - mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key); + Bundle scanParams = new Bundle(); + scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); + mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key, scanParams); } /** @@ -879,7 +892,10 @@ public class WifiScanner { */ public List<ScanResult> getSingleScanResults() { validateChannel(); - Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0); + Bundle scanParams = new Bundle(); + scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); + Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0, 0, + scanParams); if (reply.what == WifiScanner.CMD_OP_SUCCEEDED) { return Arrays.asList(((ParcelableScanResults) reply.obj).getResults()); } diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java index 67720961f8ed..6631fa806fc6 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java @@ -16,10 +16,16 @@ package android.net.wifi.p2p; +import android.annotation.IntDef; import android.annotation.UnsupportedAppUsage; +import android.net.MacAddress; import android.net.wifi.WpsInfo; -import android.os.Parcelable; import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * A class representing a Wi-Fi P2p configuration for setting up a connection @@ -38,12 +44,46 @@ public class WifiP2pConfig implements Parcelable { */ public WpsInfo wps; + /** + * The network name of a group, should be configured by helper method + */ + /** @hide */ + public String networkName = ""; + + /** + * The passphrase of a group, should be configured by helper method + */ + /** @hide */ + public String passphrase = ""; + + /** + * The required band for Group Owner + */ + /** @hide */ + public int groupOwnerBand = GROUP_OWNER_BAND_AUTO; + /** @hide */ public static final int MAX_GROUP_OWNER_INTENT = 15; /** @hide */ @UnsupportedAppUsage public static final int MIN_GROUP_OWNER_INTENT = 0; + /** @hide */ + @IntDef(flag = false, prefix = { "GROUP_OWNER_BAND_" }, value = { + GROUP_OWNER_BAND_AUTO, + GROUP_OWNER_BAND_2GHZ, + GROUP_OWNER_BAND_5GHZ + }) + @Retention(RetentionPolicy.SOURCE) + public @interface GroupOwnerBandType {} + + /** + * Recognized Group Owner required band. + */ + public static final int GROUP_OWNER_BAND_AUTO = 0; + public static final int GROUP_OWNER_BAND_2GHZ = 1; + public static final int GROUP_OWNER_BAND_5GHZ = 2; + /** * This is an integer value between 0 and 15 where 0 indicates the least * inclination to be a group owner and 15 indicates the highest inclination @@ -115,6 +155,10 @@ public class WifiP2pConfig implements Parcelable { sbuf.append("\n wps: ").append(wps); sbuf.append("\n groupOwnerIntent: ").append(groupOwnerIntent); sbuf.append("\n persist: ").append(netId); + sbuf.append("\n networkName: ").append(networkName); + sbuf.append("\n passphrase: ").append( + TextUtils.isEmpty(passphrase) ? "<empty>" : "<non-empty>"); + sbuf.append("\n groupOwnerBand: ").append(groupOwnerBand); return sbuf.toString(); } @@ -130,6 +174,9 @@ public class WifiP2pConfig implements Parcelable { wps = new WpsInfo(source.wps); groupOwnerIntent = source.groupOwnerIntent; netId = source.netId; + networkName = source.networkName; + passphrase = source.passphrase; + groupOwnerBand = source.groupOwnerBand; } } @@ -139,6 +186,9 @@ public class WifiP2pConfig implements Parcelable { dest.writeParcelable(wps, flags); dest.writeInt(groupOwnerIntent); dest.writeInt(netId); + dest.writeString(networkName); + dest.writeString(passphrase); + dest.writeInt(groupOwnerBand); } /** Implement the Parcelable interface */ @@ -150,6 +200,9 @@ public class WifiP2pConfig implements Parcelable { config.wps = (WpsInfo) in.readParcelable(null); config.groupOwnerIntent = in.readInt(); config.netId = in.readInt(); + config.networkName = in.readString(); + config.passphrase = in.readString(); + config.groupOwnerBand = in.readInt(); return config; } @@ -157,4 +210,140 @@ public class WifiP2pConfig implements Parcelable { return new WifiP2pConfig[size]; } }; + + /** + * Builder used to build {@link WifiP2pConfig} objects for + * creating or joining a group. + */ + public static final class Builder { + + private static final MacAddress MAC_ANY_ADDRESS = + MacAddress.fromString("00:00:00:00:00:00"); + + private MacAddress mDeviceAddress = MAC_ANY_ADDRESS; + private String mNetworkName = ""; + private String mPassphrase = ""; + private int mGroupOwnerBand = GROUP_OWNER_BAND_AUTO; + private int mNetId = WifiP2pGroup.TEMPORARY_NET_ID; + + /** + * Specify the peer's MAC address. If not set, the device will + * try to find a peer whose SSID matches the network name as + * specified by {@link #setNetworkName(String)}. Specifying null will + * reset the peer's MAC address to "00:00:00:00:00:00". + * <p> + * Optional. "00:00:00:00:00:00" by default. + * + * @param deviceAddress the peer's MAC address. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setDeviceAddress(MacAddress deviceAddress) { + if (deviceAddress == null) { + mDeviceAddress = MAC_ANY_ADDRESS; + } else { + mDeviceAddress = deviceAddress; + } + return this; + } + + /** + * Specify the network name, a.k.a. group name, + * for creating or joining a group. + * <p> + * Must be called - an empty network name is not valid. + * + * @param networkName network name of a group. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setNetworkName(String networkName) { + if (TextUtils.isEmpty(networkName)) { + throw new IllegalArgumentException( + "network name must be non-empty."); + } + mNetworkName = networkName; + return this; + } + + /** + * Specify the passphrase for creating or joining a group. + * <p> + * Must be called - an empty passphrase is not valid. + * + * @param passphrase the passphrase of a group. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setPassphrase(String passphrase) { + if (TextUtils.isEmpty(passphrase)) { + throw new IllegalArgumentException( + "passphrase must be non-empty."); + } + mPassphrase = passphrase; + return this; + } + + /** + * Specify the band to use for creating the group. This method only applies when + * creating a group as Group Owner using {@link WifiP2pManager#createGroup}. + * The band should be {@link #GROUP_OWNER_BAND_2GHZ} or {@link #GROUP_OWNER_BAND_5GHZ}, + * or allow the system to pick the band by specifying {@link #GROUP_OWNER_BAND_AUTO}. + * If the Group Owner cannot create a group in the specified band, the operation will fail. + * <p> + * Optional. {@link #GROUP_OWNER_BAND_AUTO} by default. + * + * @param band the required band of group owner. + * This should be one of {@link #GROUP_OWNER_BAND_AUTO}, + * {@link #GROUP_OWNER_BAND_2GHZ}, {@link #GROUP_OWNER_BAND_5GHZ}. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setGroupOwnerBand(int band) { + mGroupOwnerBand = band; + return this; + } + + /** + * Specify that the group configuration be persisted (i.e. saved). + * By default the group configuration will not be saved. + * <p> + * Optional. false by default. + * + * @param persistent is this group persistent group. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder enablePersistentMode(boolean persistent) { + if (persistent) { + mNetId = WifiP2pGroup.PERSISTENT_NET_ID; + } else { + mNetId = WifiP2pGroup.TEMPORARY_NET_ID; + } + return this; + } + + /** + * Build {@link WifiP2pConfig} given the current requests made on the builder. + * @return {@link WifiP2pConfig} constructed based on builder method calls. + */ + public WifiP2pConfig build() { + if (TextUtils.isEmpty(mNetworkName)) { + throw new IllegalStateException( + "network name must be non-empty."); + } + if (TextUtils.isEmpty(mPassphrase)) { + throw new IllegalStateException( + "passphrase must be non-empty."); + } + + WifiP2pConfig config = new WifiP2pConfig(); + config.deviceAddress = mDeviceAddress.toString(); + config.networkName = mNetworkName; + config.passphrase = mPassphrase; + config.groupOwnerBand = mGroupOwnerBand; + config.netId = mNetId; + return config; + } + } } diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java index f58a006278d2..b0ed11034810 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java @@ -16,6 +16,7 @@ package android.net.wifi.p2p; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -25,6 +26,7 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.UnsupportedAppUsage; import android.content.Context; +import android.net.NetworkInfo; import android.net.wifi.WpsInfo; import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo; import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceResponse; @@ -49,6 +51,8 @@ import com.android.internal.util.Protocol; import dalvik.system.CloseGuard; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -158,6 +162,14 @@ public class WifiP2pManager { */ public static final String EXTRA_WIFI_STATE = "wifi_p2p_state"; + /** @hide */ + @IntDef({ + WIFI_P2P_STATE_DISABLED, + WIFI_P2P_STATE_ENABLED}) + @Retention(RetentionPolicy.SOURCE) + public @interface WifiP2pState { + } + /** * Wi-Fi p2p is disabled. * @@ -250,6 +262,14 @@ public class WifiP2pManager { */ public static final String EXTRA_DISCOVERY_STATE = "discoveryState"; + /** @hide */ + @IntDef({ + WIFI_P2P_DISCOVERY_STOPPED, + WIFI_P2P_DISCOVERY_STARTED}) + @Retention(RetentionPolicy.SOURCE) + public @interface WifiP2pDiscoveryState { + } + /** * p2p discovery has stopped * @@ -502,6 +522,21 @@ public class WifiP2pManager { /** @hide */ public static final int SET_ONGOING_PEER_CONFIG_SUCCEEDED = BASE + 89; + /** @hide */ + public static final int REQUEST_P2P_STATE = BASE + 90; + /** @hide */ + public static final int RESPONSE_P2P_STATE = BASE + 91; + + /** @hide */ + public static final int REQUEST_DISCOVERY_STATE = BASE + 92; + /** @hide */ + public static final int RESPONSE_DISCOVERY_STATE = BASE + 93; + + /** @hide */ + public static final int REQUEST_NETWORK_INFO = BASE + 94; + /** @hide */ + public static final int RESPONSE_NETWORK_INFO = BASE + 95; + /** * Create a new WifiP2pManager instance. Applications use * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve @@ -690,6 +725,43 @@ public class WifiP2pManager { public void onHandoverMessageAvailable(String handoverMessage); } + /** Interface for callback invocation when p2p state is available + * in response to {@link #requestP2pState}. + */ + public interface P2pStateListener { + /** + * The requested p2p state is available. + * @param state Wi-Fi p2p state + * @see #WIFI_P2P_STATE_DISABLED + * @see #WIFI_P2P_STATE_ENABLED + */ + void onP2pStateAvailable(@WifiP2pState int state); + } + + /** Interface for callback invocation when p2p state is available + * in response to {@link #requestDiscoveryState}. + */ + public interface DiscoveryStateListener { + /** + * The requested p2p discovery state is available. + * @param state Wi-Fi p2p discovery state + * @see #WIFI_P2P_DISCOVERY_STARTED + * @see #WIFI_P2P_DISCOVERY_STOPPED + */ + void onDiscoveryStateAvailable(@WifiP2pDiscoveryState int state); + } + + /** Interface for callback invocation when {@link android.net.NetworkInfo} is available + * in response to {@link #requestNetworkInfo}. + */ + public interface NetworkInfoListener { + /** + * The requested {@link android.net.NetworkInfo} is available + * @param networkInfo Wi-Fi p2p {@link android.net.NetworkInfo} + */ + void onNetworkInfoAvailable(NetworkInfo networkInfo); + } + /** * Interface for callback invocation when ongoing peer info is available * @hide @@ -889,6 +961,24 @@ public class WifiP2pManager { .onOngoingPeerAvailable(peerConfig); } break; + case RESPONSE_P2P_STATE: + if (listener != null) { + ((P2pStateListener) listener) + .onP2pStateAvailable(message.arg1); + } + break; + case RESPONSE_DISCOVERY_STATE: + if (listener != null) { + ((DiscoveryStateListener) listener) + .onDiscoveryStateAvailable(message.arg1); + } + break; + case RESPONSE_NETWORK_INFO: + if (listener != null) { + ((NetworkInfoListener) listener) + .onNetworkInfoAvailable((NetworkInfo) message.obj); + } + break; default: Log.d(TAG, "Ignored " + message); break; @@ -1126,6 +1216,38 @@ public class WifiP2pManager { } /** + * Create a p2p group with the current device as the group owner. This essentially creates + * an access point that can accept connections from legacy clients as well as other p2p + * devices. + * + * <p> An app should use {@link WifiP2pConfig.Builder} to build the configuration + * for a group. + * + * <p class="note"><strong>Note:</strong> + * This function would normally not be used unless the current device needs + * to form a p2p group as a Group Owner and allow peers to join it as either + * Group Clients or legacy Wi-Fi STAs. + * + * <p> The function call immediately returns after sending a group creation request + * to the framework. The application is notified of a success or failure to initiate + * group creation through listener callbacks {@link ActionListener#onSuccess} or + * {@link ActionListener#onFailure}. + * + * <p> Application can request for the group details with {@link #requestGroupInfo}. + * + * @param c is the channel created at {@link #initialize}. + * @param config the configuration of a p2p group. + * @param listener for callbacks on success or failure. Can be null. + */ + public void createGroup(@NonNull Channel c, + @Nullable WifiP2pConfig config, + @Nullable ActionListener listener) { + checkChannel(c); + c.mAsyncChannel.sendMessage(CREATE_GROUP, 0, + c.putListener(listener), config); + } + + /** * Remove the current p2p group. * * <p> The function call immediately returns after sending a group removal request @@ -1616,4 +1738,68 @@ public class WifiP2pManager { c.mAsyncChannel.sendMessage(SET_ONGOING_PEER_CONFIG, 0, c.putListener(listener), config); } + + /** + * Request p2p enabled state. + * + * <p> This state indicates whether Wi-Fi p2p is enabled or disabled. + * The valid value is one of {@link #WIFI_P2P_STATE_DISABLED} or + * {@link #WIFI_P2P_STATE_ENABLED}. The state is returned using the + * {@link P2pStateListener} listener. + * + * <p> This state is also included in the {@link #WIFI_P2P_STATE_CHANGED_ACTION} + * broadcast event with extra {@link #EXTRA_WIFI_STATE}. + * + * @param c is the channel created at {@link #initialize}. + * @param listener for callback when p2p state is available.. + */ + public void requestP2pState(@NonNull Channel c, + @NonNull P2pStateListener listener) { + checkChannel(c); + if (listener == null) throw new IllegalArgumentException("This listener cannot be null."); + c.mAsyncChannel.sendMessage(REQUEST_P2P_STATE, 0, c.putListener(listener)); + } + + /** + * Request p2p discovery state. + * + * <p> This state indicates whether p2p discovery has started or stopped. + * The valid value is one of {@link #WIFI_P2P_DISCOVERY_STARTED} or + * {@link #WIFI_P2P_DISCOVERY_STOPPED}. The state is returned using the + * {@link DiscoveryStateListener} listener. + * + * <p> This state is also included in the {@link #WIFI_P2P_DISCOVERY_CHANGED_ACTION} + * broadcast event with extra {@link #EXTRA_DISCOVERY_STATE}. + * + * @param c is the channel created at {@link #initialize}. + * @param listener for callback when discovery state is available.. + */ + public void requestDiscoveryState(@NonNull Channel c, + @NonNull DiscoveryStateListener listener) { + checkChannel(c); + if (listener == null) throw new IllegalArgumentException("This listener cannot be null."); + c.mAsyncChannel.sendMessage(REQUEST_DISCOVERY_STATE, 0, c.putListener(listener)); + } + + /** + * Request network info. + * + * <p> This method provides the network info in the form of a {@link android.net.NetworkInfo}. + * {@link android.net.NetworkInfo#isAvailable()} indicates the p2p availability and + * {@link android.net.NetworkInfo#getDetailedState()} reports the current fine-grained state + * of the network. This {@link android.net.NetworkInfo} is returned using the + * {@link NetworkInfoListener} listener. + * + * <p> This information is also included in the {@link #WIFI_P2P_CONNECTION_CHANGED_ACTION} + * broadcast event with extra {@link #EXTRA_NETWORK_INFO}. + * + * @param c is the channel created at {@link #initialize}. + * @param listener for callback when network info is available.. + */ + public void requestNetworkInfo(@NonNull Channel c, + @NonNull NetworkInfoListener listener) { + checkChannel(c); + if (listener == null) throw new IllegalArgumentException("This listener cannot be null."); + c.mAsyncChannel.sendMessage(REQUEST_NETWORK_INFO, 0, c.putListener(listener)); + } } diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/AbstractWifiService.java index 04bc55710dfd..aa526d248d14 100644 --- a/wifi/java/com/android/server/wifi/AbstractWifiService.java +++ b/wifi/java/com/android/server/wifi/AbstractWifiService.java @@ -73,7 +73,7 @@ public abstract class AbstractWifiService extends IWifiManager.Stub { } @Override - public ParceledListSlice getConfiguredNetworks() { + public ParceledListSlice getConfiguredNetworks(String packageName) { throw new UnsupportedOperationException(); } @@ -188,17 +188,17 @@ public abstract class AbstractWifiService extends IWifiManager.Stub { } @Override - public void disconnect(String packageName) { + public boolean disconnect(String packageName) { throw new UnsupportedOperationException(); } @Override - public void reconnect(String packageName) { + public boolean reconnect(String packageName) { throw new UnsupportedOperationException(); } @Override - public void reassociate(String packageName) { + public boolean reassociate(String packageName) { throw new UnsupportedOperationException(); } |