diff options
592 files changed, 15948 insertions, 5080 deletions
diff --git a/Android.bp b/Android.bp index 25e738ccb3cf..41d5f28a2680 100644 --- a/Android.bp +++ b/Android.bp @@ -158,9 +158,9 @@ java_defaults { "core/java/android/hardware/IConsumerIrService.aidl", "core/java/android/hardware/ISerialManager.aidl", "core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl", - "core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl", "core/java/android/hardware/biometrics/IBiometricService.aidl", "core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl", + "core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl", "core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl", "core/java/android/hardware/display/IDisplayManager.aidl", "core/java/android/hardware/display/IDisplayManagerCallback.aidl", diff --git a/Android.mk b/Android.mk index 770ec20f151e..b7dda9a45ed8 100644 --- a/Android.mk +++ b/Android.mk @@ -73,55 +73,37 @@ $(OUT_DOCS)/offline-sdk-timestamp: $(OUT_DOCS)/offline-sdk-docs-docs.zip ( unzip -qo $< -d $(OUT_DOCS)/offline-sdk && touch -f $@ ) || exit 1 # ==== hiddenapi lists ======================================= -.KATI_RESTAT: \ - $(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST) \ - $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \ - $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \ - $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) -$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST): \ - .KATI_IMPLICIT_OUTPUTS := \ - $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \ - $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \ - $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) -$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST): \ +.KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS) +$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \ frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \ frameworks/base/config/hiddenapi-light-greylist.txt \ frameworks/base/config/hiddenapi-vendor-list.txt \ + frameworks/base/config/hiddenapi-greylist-max-o.txt \ frameworks/base/config/hiddenapi-max-sdk-p-blacklist.txt \ frameworks/base/config/hiddenapi-force-blacklist.txt \ $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \ $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \ $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \ - --input-public $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \ - --input-private $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \ - --input-whitelists $(PRIVATE_WHITELIST_INPUTS) \ - --input-greylists \ + --public $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \ + --private $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \ + --csv $(PRIVATE_FLAGS_INPUTS) \ + --greylist \ frameworks/base/config/hiddenapi-light-greylist.txt \ frameworks/base/config/hiddenapi-vendor-list.txt \ - frameworks/base/config/hiddenapi-max-sdk-p-blacklist.txt \ - <(comm -12 <(sort $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE)) \ - $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST)) \ - $(PRIVATE_GREYLIST_INPUTS) \ - --input-blacklists frameworks/base/config/hiddenapi-force-blacklist.txt \ - --output-whitelist $(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST).tmp \ - --output-light-greylist $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST).tmp \ - --output-dark-greylist $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST).tmp \ - --output-blacklist $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST).tmp - $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST)) - $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)) - $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)) - $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)) + --greylist-ignore-conflicts $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \ + --greylist-max-o-ignore-conflicts \ + frameworks/base/config/hiddenapi-greylist-max-o.txt \ + --blacklist frameworks/base/config/hiddenapi-force-blacklist.txt \ + --output $@.tmp + $(call commit-change-for-toc,$@) $(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA): \ frameworks/base/tools/hiddenapi/merge_csv.py \ $(PRIVATE_METADATA_INPUTS) frameworks/base/tools/hiddenapi/merge_csv.py $(PRIVATE_METADATA_INPUTS) > $@ -$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST)) -$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)) -$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)) -$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)) +$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS)) $(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA)) # Include subdirectory makefiles 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/api/current.txt b/api/current.txt index 9d46863f0102..079539e3c5f4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -150,6 +150,7 @@ package android { field public static final java.lang.String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS"; field public static final java.lang.String USE_BIOMETRIC = "android.permission.USE_BIOMETRIC"; field public static final deprecated java.lang.String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT"; + field public static final java.lang.String USE_FULL_SCREEN_INTENT = "android.permission.USE_FULL_SCREEN_INTENT"; field public static final java.lang.String USE_SIP = "android.permission.USE_SIP"; field public static final java.lang.String VIBRATE = "android.permission.VIBRATE"; field public static final java.lang.String WAKE_LOCK = "android.permission.WAKE_LOCK"; @@ -480,6 +481,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 @@ -1197,6 +1202,7 @@ package android { field public static final int selectableItemBackgroundBorderless = 16843868; // 0x101045c field public static final deprecated int selectedDateVerticalBar = 16843591; // 0x1010347 field public static final deprecated int selectedWeekBackgroundColor = 16843586; // 0x1010342 + field public static final int selectionDividerHeight = 16844190; // 0x101059e field public static final int sessionService = 16843837; // 0x101043d field public static final int settingsActivity = 16843301; // 0x1010225 field public static final int settingsSliceUri = 16844179; // 0x1010593 @@ -1312,7 +1318,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 +1501,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 @@ -6354,7 +6360,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; @@ -9553,6 +9558,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(); @@ -9763,6 +9769,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(); @@ -11173,8 +11180,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; @@ -11182,6 +11189,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; } @@ -11332,7 +11340,7 @@ package android.content.pm { method public abstract int checkSignatures(java.lang.String, java.lang.String); method public abstract int checkSignatures(int, int); method public abstract void clearInstantAppCookie(); - method public abstract void clearPackagePreferredActivities(java.lang.String); + method public abstract deprecated void clearPackagePreferredActivities(java.lang.String); method public abstract java.lang.String[] currentToCanonicalPackageNames(java.lang.String[]); method public abstract void extendVerificationTimeout(int, int, long); method public abstract android.graphics.drawable.Drawable getActivityBanner(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException; @@ -11376,8 +11384,8 @@ package android.content.pm { method public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int); method public abstract android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract android.content.pm.PermissionInfo getPermissionInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String); - method public abstract java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int); + method public abstract deprecated int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String); + method public abstract deprecated java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int); method public abstract android.content.pm.ProviderInfo getProviderInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract android.content.pm.ActivityInfo getReceiverInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract android.content.res.Resources getResourcesForActivity(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException; @@ -11657,6 +11665,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 { @@ -11836,6 +11845,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); @@ -13697,6 +13728,7 @@ package android.graphics { public abstract class ColorSpace { method public static android.graphics.ColorSpace adapt(android.graphics.ColorSpace, float[]); method public static android.graphics.ColorSpace adapt(android.graphics.ColorSpace, float[], android.graphics.ColorSpace.Adaptation); + method public static float[] chromaticAdaptation(android.graphics.ColorSpace.Adaptation, float[], float[]); method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace, android.graphics.ColorSpace); method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace, android.graphics.ColorSpace, android.graphics.ColorSpace.RenderIntent); method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace); @@ -14698,6 +14730,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(); @@ -15051,6 +15084,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(); @@ -29021,7 +29055,7 @@ package android.net.wifi { public class WifiManager { method public deprecated int addNetwork(android.net.wifi.WifiConfiguration); - method public boolean addNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>); + method public int addNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>); method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration); method public static int calculateSignalLevel(int, int); method public deprecated void cancelWps(android.net.wifi.WifiManager.WpsCallback); @@ -29035,6 +29069,7 @@ package android.net.wifi { method public deprecated java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks(); method public android.net.wifi.WifiInfo getConnectionInfo(); method public android.net.DhcpInfo getDhcpInfo(); + method public int getMaxNumberOfNetworkSuggestionsPerApp(); method public java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations(); method public java.util.List<android.net.wifi.ScanResult> getScanResults(); method public int getWifiState(); @@ -29053,7 +29088,7 @@ package android.net.wifi { method public deprecated boolean reassociate(); method public deprecated boolean reconnect(); method public deprecated boolean removeNetwork(int); - method public boolean removeNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>); + method public int removeNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>); method public void removePasspointConfiguration(java.lang.String); method public deprecated boolean saveConfiguration(); method public void setTdlsEnabled(java.net.InetAddress, boolean); @@ -29082,6 +29117,11 @@ package android.net.wifi { field public static final java.lang.String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE"; field public static final java.lang.String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED"; field public static final java.lang.String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS"; + field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 2; // 0x2 + field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 3; // 0x3 + field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1; // 0x1 + field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 4; // 0x4 + field public static final int STATUS_NETWORK_SUGGESTIONS_SUCCESS = 0; // 0x0 field public static final deprecated java.lang.String SUPPLICANT_CONNECTION_CHANGE_ACTION = "android.net.wifi.supplicant.CONNECTION_CHANGE"; field public static final deprecated java.lang.String SUPPLICANT_STATE_CHANGED_ACTION = "android.net.wifi.supplicant.STATE_CHANGE"; field public static final int WIFI_MODE_FULL = 1; // 0x1 @@ -29123,6 +29163,9 @@ package android.net.wifi { method public void setReferenceCounted(boolean); } + public static abstract class WifiManager.NetworkSuggestionsStatusCode implements java.lang.annotation.Annotation { + } + public class WifiManager.WifiLock { method public void acquire(); method public boolean isHeld(); @@ -29414,11 +29457,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); @@ -29485,6 +29541,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); @@ -40899,11 +40956,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(); @@ -43883,6 +43938,7 @@ package android.telephony { field public static final int NETWORK_TYPE_IDEN = 11; // 0xb field public static final int NETWORK_TYPE_IWLAN = 18; // 0x12 field public static final int NETWORK_TYPE_LTE = 13; // 0xd + field public static final int NETWORK_TYPE_NR = 20; // 0x14 field public static final int NETWORK_TYPE_TD_SCDMA = 17; // 0x11 field public static final int NETWORK_TYPE_UMTS = 3; // 0x3 field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0 @@ -49024,10 +49080,12 @@ package android.view { method protected int getTopPaddingOffset(); method public android.view.TouchDelegate getTouchDelegate(); method public java.util.ArrayList<android.view.View> getTouchables(); + method public float getTransitionAlpha(); method public java.lang.String getTransitionName(); method public float getTranslationX(); method public float getTranslationY(); method public float getTranslationZ(); + method public long getUniqueDrawingId(); method public int getVerticalFadingEdgeLength(); method public int getVerticalScrollbarPosition(); method public int getVerticalScrollbarWidth(); @@ -49232,6 +49290,7 @@ package android.view { method public void setActivated(boolean); method public void setAlpha(float); method public void setAnimation(android.view.animation.Animation); + method public void setAnimationMatrix(android.graphics.Matrix); method public void setAutofillHints(java.lang.String...); method public void setAutofillId(android.view.autofill.AutofillId); method public void setBackground(android.graphics.drawable.Drawable); @@ -49283,7 +49342,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 final void setLeftTopRightBottom(int, int, int, int); method public void setLongClickable(boolean); method protected final void setMeasuredDimension(int, int); method public void setMinimumHeight(int); @@ -49350,6 +49409,7 @@ package android.view { method public void setTooltipText(java.lang.CharSequence); method public final void setTop(int); method public void setTouchDelegate(android.view.TouchDelegate); + method public void setTransitionAlpha(float); method public final void setTransitionName(java.lang.String); method public void setTranslationX(float); method public void setTranslationY(float); @@ -49823,6 +49883,7 @@ package android.view { method public deprecated boolean isAnimationCacheEnabled(); method protected boolean isChildrenDrawingOrderEnabled(); method protected deprecated boolean isChildrenDrawnWithCacheEnabled(); + method public boolean isLayoutSuppressed(); method public boolean isMotionEventSplittingEnabled(); method public boolean isTransitionGroup(); method public final void layout(int, int, int, int); @@ -49888,6 +49949,7 @@ package android.view { method public android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int); method public void startLayoutAnimation(); method public void startViewTransition(android.view.View); + method public void suppressLayout(boolean); method public void updateViewLayout(android.view.View, android.view.ViewGroup.LayoutParams); field protected static final int CLIP_TO_PADDING_MASK = 34; // 0x22 field public static final int FOCUS_AFTER_DESCENDANTS = 262144; // 0x40000 @@ -54535,6 +54597,7 @@ package android.widget { method public java.lang.String[] getDisplayedValues(); method public int getMaxValue(); method public int getMinValue(); + method public int getSelectionDividerHeight(); method public int getValue(); method public boolean getWrapSelectorWheel(); method public void setDisplayedValues(java.lang.String[]); @@ -54544,6 +54607,7 @@ package android.widget { method public void setOnLongPressUpdateInterval(long); method public void setOnScrollListener(android.widget.NumberPicker.OnScrollListener); method public void setOnValueChangedListener(android.widget.NumberPicker.OnValueChangeListener); + method public void setSelectionDividerHeight(int); method public void setValue(int); method public void setWrapSelectorWheel(boolean); } @@ -55551,6 +55615,7 @@ package android.widget { method public boolean isElegantTextHeight(); method public boolean isFallbackLineSpacing(); method public boolean isInputMethodTarget(); + method public boolean isSingleLine(); method public boolean isSuggestionsEnabled(); method public boolean isTextSelectable(); method public int length(); diff --git a/api/system-current.txt b/api/system-current.txt index 257b4f636963..62d446f7254e 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"; @@ -216,6 +217,10 @@ package android { field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS"; } + public static final class Manifest.permission_group { + field public static final java.lang.String UNDEFINED = "android.permission-group.UNDEFINED"; + } + public static final class R.array { field public static final int config_keySystemUuidMapping = 17235973; // 0x1070005 } @@ -224,6 +229,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 } @@ -555,6 +561,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); @@ -1063,7 +1073,6 @@ package android.content { field public static final java.lang.String ACTION_INSTALL_INSTANT_APP_PACKAGE = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE"; field public static final java.lang.String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS"; field public static final java.lang.String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION"; - field public static final java.lang.String ACTION_MANAGE_APP_PERMISSION = "android.intent.action.MANAGE_APP_PERMISSION"; field public static final java.lang.String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS"; field public static final java.lang.String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS"; field public static final java.lang.String ACTION_MANAGE_PERMISSION_APPS = "android.intent.action.MANAGE_PERMISSION_APPS"; @@ -1235,7 +1244,7 @@ package android.content.pm { method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(android.content.Intent, int, android.os.UserHandle); method public abstract void registerDexModule(java.lang.String, android.content.pm.PackageManager.DexModuleRegisterCallback); 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 deprecated 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); @@ -2756,7 +2765,6 @@ package android.location { public class LocationManager { method public deprecated boolean addGpsMeasurementListener(android.location.GpsMeasurementsEvent.Listener); method public deprecated boolean addGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener); - method public android.app.PendingIntent createManageLocationPermissionIntent(java.lang.String, java.lang.String); method public void flushGnssBatch(); method public int getGnssBatchSize(); method public java.lang.String getNetworkProviderPackage(); @@ -2865,6 +2873,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 { @@ -3679,8 +3688,11 @@ 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(); method public android.net.wifi.WifiConfiguration getWifiApConfiguration(); method public int getWifiApState(); @@ -3690,6 +3702,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); @@ -3838,6 +3851,7 @@ package android.net.wifi { ctor public WifiScanner.ScanSettings(); field public int band; field public android.net.wifi.WifiScanner.ChannelSpec[] channels; + field public boolean ignoreLocationSettings; field public int maxPeriodInMs; field public int maxScansToCache; field public int numBssidsPerScan; @@ -4295,6 +4309,7 @@ package android.os { public class UserManager { method public void clearSeedAccountData(); + method public android.os.UserHandle getProfileParent(android.os.UserHandle); method public java.lang.String getSeedAccountName(); method public android.os.PersistableBundle getSeedAccountOptions(); method public java.lang.String getSeedAccountType(); @@ -4626,6 +4641,8 @@ package android.provider { field public static final java.lang.String HUSH_GESTURE_USED = "hush_gesture_used"; field public static final java.lang.String INSTANT_APPS_ENABLED = "instant_apps_enabled"; field public static final java.lang.String LAST_SETUP_SHOWN = "last_setup_shown"; + field public static final java.lang.String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis"; + field public static final java.lang.String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis"; field public static final java.lang.String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications"; field public static final java.lang.String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications"; field public static final java.lang.String MANUAL_RINGER_TOGGLE_COUNT = "manual_ringer_toggle_count"; @@ -5164,6 +5181,7 @@ package android.service.notification { package android.service.oemlock { public class OemLockManager { + method public java.lang.String getLockName(); method public boolean isOemUnlockAllowedByCarrier(); method public boolean isOemUnlockAllowedByUser(); method public void setOemUnlockAllowedByCarrier(boolean, byte[]); @@ -5334,6 +5352,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 { @@ -5903,6 +5930,7 @@ package android.telephony { field public static final int NETWORK_TYPE_BITMASK_HSUPA = 512; // 0x200 field public static final int NETWORK_TYPE_BITMASK_LTE = 8192; // 0x2000 field public static final int NETWORK_TYPE_BITMASK_LTE_CA = 524288; // 0x80000 + field public static final int NETWORK_TYPE_BITMASK_NR = 1048576; // 0x100000 field public static final int NETWORK_TYPE_BITMASK_TD_SCDMA = 131072; // 0x20000 field public static final int NETWORK_TYPE_BITMASK_UMTS = 8; // 0x8 field public static final int NETWORK_TYPE_BITMASK_UNKNOWN = 1; // 0x1 @@ -6215,6 +6243,7 @@ package android.telephony.ims { method public android.os.Bundle getCallExtras(); method public int getCallType(); method public static int getCallTypeFromVideoState(int); + method public int getEmergencyServiceCategories(); method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile(); method public int getRestrictCause(); method public int getServiceType(); @@ -6227,6 +6256,7 @@ package android.telephony.ims { method public void setCallExtraBoolean(java.lang.String, boolean); method public void setCallExtraInt(java.lang.String, int); method public void setCallRestrictCause(int); + method public void setEmergencyServiceCategories(int); method public void updateCallExtras(android.telephony.ims.ImsCallProfile); method public void updateCallType(android.telephony.ims.ImsCallProfile); method public void updateMediaProfile(android.telephony.ims.ImsCallProfile); diff --git a/api/test-current.txt b/api/test-current.txt index 8c710c1237c6..46cbb52f6efa 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); @@ -304,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 { @@ -947,6 +943,10 @@ package android.os.health { package android.os.storage { + public class StorageManager { + method public static boolean hasIsolatedStorage(); + } + public final class StorageVolume implements android.os.Parcelable { method public java.lang.String getPath(); } @@ -1002,6 +1002,7 @@ package android.provider { } public static final class Settings.Secure extends android.provider.Settings.NameValueTable { + method public static void resetToDefaults(android.content.ContentResolver, java.lang.String); field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled"; field public static final java.lang.String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service"; field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification"; @@ -1013,6 +1014,8 @@ package android.provider { field public static final java.lang.String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length"; field public static final java.lang.String DISABLED_PRINT_SERVICES = "disabled_print_services"; field public static final deprecated java.lang.String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages"; + field public static final java.lang.String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis"; + field public static final java.lang.String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis"; field public static final java.lang.String SYNC_PARENT_SOUNDS = "sync_parent_sounds"; field public static final java.lang.String USER_SETUP_COMPLETE = "user_setup_complete"; field public static final java.lang.String VOICE_INTERACTION_SERVICE = "voice_interaction_service"; diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp index 1c3ebd877695..b23c1eda87b3 100644 --- a/cmds/incidentd/src/IncidentService.cpp +++ b/cmds/incidentd/src/IncidentService.cpp @@ -24,6 +24,7 @@ #include "incidentd_util.h" #include "section_list.h" +#include <android/os/IncidentReportArgs.h> #include <binder/IPCThreadState.h> #include <binder/IResultReceiver.h> #include <binder/IServiceManager.h> @@ -41,6 +42,15 @@ enum { WHAT_RUN_REPORT = 1, WHAT_SEND_BACKLOG_TO_DROPBOX = 2 }; #define DEFAULT_BYTES_SIZE_LIMIT (20 * 1024 * 1024) // 20MB #define DEFAULT_REFACTORY_PERIOD_MS (24 * 60 * 60 * 1000) // 1 Day +// Skip logs (1100 - 1108) because they are already in the bug report +// Skip 1200, 1201, 1202, 3018 because they take too long +// TODO(120079956): Skip 3008, 3015 because of error +#define SKIPPED_SECTIONS { 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, /* Logs */ \ + 1200, 1201, 1202, /* Native, hal, java traces */ \ + 3008, /* "package --proto" */ \ + 3015, /* "activity --proto processes" */ \ + 3018 /* "meminfo -a --proto" */ } + namespace android { namespace os { namespace incidentd { @@ -391,6 +401,38 @@ status_t IncidentService::cmd_privacy(FILE* in, FILE* out, FILE* err, Vector<Str return NO_ERROR; } +status_t IncidentService::dump(int fd, const Vector<String16>& args) { + if (std::find(args.begin(), args.end(), String16("--proto")) == args.end()) { + ALOGD("Skip dumping incident. Only proto format is supported."); + dprintf(fd, "Incident dump only supports proto version.\n"); + return NO_ERROR; + } + + ALOGD("Dump incident proto"); + IncidentReportArgs incidentArgs; + incidentArgs.setDest(DEST_EXPLICIT); + int skipped[] = SKIPPED_SECTIONS; + for (const Section** section = SECTION_LIST; *section; section++) { + const int id = (*section)->id; + if (std::find(std::begin(skipped), std::end(skipped), id) == std::end(skipped)) { + incidentArgs.addSection(id); + } + } + + if (!checkIncidentPermissions(incidentArgs).isOk()) { + return PERMISSION_DENIED; + } + + int fd1 = dup(fd); + if (fd1 < 0) { + return -errno; + } + + mHandler->scheduleRunReport(new ReportRequest(incidentArgs, NULL, fd1)); + + return NO_ERROR; +} + } // namespace incidentd } // namespace os } // namespace android diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h index e176bfd95c5f..6252ad295224 100644 --- a/cmds/incidentd/src/IncidentService.h +++ b/cmds/incidentd/src/IncidentService.h @@ -112,6 +112,7 @@ public: virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override; virtual status_t command(FILE* in, FILE* out, FILE* err, Vector<String8>& args); + virtual status_t dump(int fd, const Vector<String16>& args); private: sp<ReportRequestQueue> mQueue; diff --git a/cmds/incidentd/src/main.cpp b/cmds/incidentd/src/main.cpp index 494882336611..098d74ecd786 100644 --- a/cmds/incidentd/src/main.cpp +++ b/cmds/incidentd/src/main.cpp @@ -45,7 +45,8 @@ int main(int /*argc*/, char** /*argv*/) { // Create the service sp<IncidentService> service = new IncidentService(looper); - if (defaultServiceManager()->addService(String16("incident"), service) != 0) { + if (defaultServiceManager()->addService(String16("incident"), service, false, + IServiceManager::DUMP_FLAG_PRIORITY_NORMAL | IServiceManager::DUMP_FLAG_PROTO) != 0) { ALOGE("Failed to add service"); return -1; } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index c8405a279f7d..41a202109713 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -2018,6 +2018,9 @@ message LmkKillOccurred { // SWAP optional int64 swap_in_bytes = 8; + + // The elapsed real time of start of the process. + optional int64 process_start_time_nanos = 9; } /* @@ -2543,10 +2546,7 @@ message ProcessMemoryState { // SWAP optional int64 swap_in_bytes = 8; - // RSS high watermark. - // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status or - // from memory.max_usage_in_bytes under /dev/memcg if the device uses per-app memory cgroups. - // Deprecated: use ProcessMemoryHighWaterMark atom instead. + // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0. optional int64 rss_high_watermark_in_bytes = 9 [deprecated = true]; // Elapsed real time when the process started. @@ -2573,9 +2573,7 @@ message NativeProcessMemoryState { // RSS optional int64 rss_in_bytes = 5; - // RSS high watermark. - // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status. - // Deprecated: use ProcessMemoryHighWaterMark atom instead. + // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0. optional int64 rss_high_watermark_in_bytes = 6 [deprecated = true]; // Elapsed real time when the process started. @@ -3331,10 +3329,27 @@ message CpuTimePerThreadFreq { optional string process_name = 4; // Name of the thread taken from `/proc/$PID/task/$TID/comm` optional string thread_name = 5; - // What frequency the CPU was running at, in KHz - optional int32 frequency_khz = 6; - // Time spent in frequency in milliseconds, since thread start. - optional int32 time_millis = 7; + + // Report eight different frequencies, and how much time is spent in each frequency. Frequencies + // are given in KHz, and time is given in milliseconds since the thread started. All eight + // frequencies are given here as the alternative is sending eight separate atoms. This method + // significantly reduces the amount of data created + optional int32 frequency1_khz = 6; + optional int32 time1_millis = 7; + optional int32 frequency2_khz = 8; + optional int32 time2_millis = 9; + optional int32 frequency3_khz = 10; + optional int32 time3_millis = 11; + optional int32 frequency4_khz = 12; + optional int32 time4_millis = 13; + optional int32 frequency5_khz = 14; + optional int32 time5_millis = 15; + optional int32 frequency6_khz = 16; + optional int32 time6_millis = 17; + optional int32 frequency7_khz = 18; + optional int32 time7_millis = 19; + optional int32 frequency8_khz = 20; + optional int32 time8_millis = 21; } /** diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index f9b79823741b..c070ca375d74 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -210,7 +210,7 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { 5 * NS_PER_SEC /* min cool-down in seconds*/, new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}}, {android::util::CPU_TIME_PER_THREAD_FREQ, - {{7}, + {{7, 9, 11, 13, 15, 17, 19, 21}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}}, // DeviceCalculatedPowerUse. diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index 661768914c69..3e5e82f7f4df 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -101,6 +101,7 @@ const int FIELD_ID_UID_MAP_DELETED_APPS = 4; const std::map<int, std::pair<size_t, size_t>> StatsdStats::kAtomDimensionKeySizeLimitMap = { {android::util::BINDER_CALLS, {6000, 10000}}, + {android::util::LOOPER_STATS, {1500, 2500}}, {android::util::CPU_TIME_PER_UID_FREQ, {6000, 10000}}, }; diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index b9732a596b2d..8d61aba432d4 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -679,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/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 90dfa87abeaa..3a5be43ed695 100644 --- a/cmds/statsd/tests/LogEvent_test.cpp +++ b/cmds/statsd/tests/LogEvent_test.cpp @@ -605,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/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java index 8e9b91dab95d..f3bf6e7b7231 100644 --- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java +++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java @@ -46,6 +46,10 @@ public final class Telecom extends BaseCommand { private static final String COMMAND_SET_PHONE_ACCOUNT_DISABLED = "set-phone-account-disabled"; private static final String COMMAND_REGISTER_PHONE_ACCOUNT = "register-phone-account"; private static final String COMMAND_REGISTER_SIM_PHONE_ACCOUNT = "register-sim-phone-account"; + private static final String COMMAND_SET_TEST_CALL_SCREENING_APP = "set-test-call-screening-app"; + private static final String COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP = + "add-or-remove-call-companion-app"; + private static final String COMMAND_SET_TEST_AUTO_MODE_APP = "set-test-auto-mode-app"; private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account"; private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer"; private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer"; @@ -64,6 +68,9 @@ public final class Telecom extends BaseCommand { "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n" + "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n" + "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n" + + "usage: telecom set-test-call-screening-app <PACKAGE>\n" + + "usage: telecom set-test-auto-mode-app <PACKAGE>\n" + + "usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n" + "usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN> <LABEL> <ADDRESS>\n" + "usage: telecom unregister-phone-account <COMPONENT> <ID> <USER_SN>\n" + "usage: telecom set-default-dialer <PACKAGE>\n" + @@ -113,6 +120,15 @@ public final class Telecom extends BaseCommand { case COMMAND_REGISTER_PHONE_ACCOUNT: runRegisterPhoneAccount(); break; + case COMMAND_SET_TEST_CALL_SCREENING_APP: + runSetTestCallScreeningApp(); + break; + case COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP: + runAddOrRemoveCallCompanionApp(); + break; + case COMMAND_SET_TEST_AUTO_MODE_APP: + runSetTestAutoModeApp(); + break; case COMMAND_REGISTER_SIM_PHONE_ACCOUNT: runRegisterSimPhoneAccount(); break; @@ -173,6 +189,23 @@ public final class Telecom extends BaseCommand { System.out.println("Success - " + handle + " registered."); } + private void runSetTestCallScreeningApp() throws RemoteException { + final String packageName = nextArg(); + mTelecomService.setTestDefaultCallScreeningApp(packageName); + } + + private void runAddOrRemoveCallCompanionApp() throws RemoteException { + final String packageName = nextArgRequired(); + String isAdded = nextArgRequired(); + boolean isAddedBool = "1".equals(isAdded); + mTelecomService.addOrRemoveTestCallCompanionApp(packageName, isAddedBool); + } + + private void runSetTestAutoModeApp() throws RemoteException { + final String packageName = nextArg(); + mTelecomService.setTestAutoModeApp(packageName); + } + private void runUnregisterPhoneAccount() throws RemoteException { final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs(); mTelecomService.unregisterPhoneAccount(handle); diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 25bd0330ad5c..e10f72913d22 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -2695,7 +2695,6 @@ Lcom/android/internal/telephony/dataconnection/DcTracker;->onRecordsLoadedOrSubI Lcom/android/internal/telephony/dataconnection/DcTracker;->onSetUserDataEnabled(Z)V Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Lcom/android/internal/telephony/dataconnection/ApnContext;)Z Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Ljava/lang/String;)Z -Lcom/android/internal/telephony/dataconnection/DcTracker;->registerForAllDataDisconnected(Landroid/os/Handler;ILjava/lang/Object;)V Lcom/android/internal/telephony/dataconnection/DcTracker;->registerSettingsObserver()V Lcom/android/internal/telephony/dataconnection/DcTracker;->resetPollStats()V Lcom/android/internal/telephony/dataconnection/DcTracker;->restartDataStallAlarm()V @@ -2709,7 +2708,6 @@ Lcom/android/internal/telephony/dataconnection/DcTracker;->startNetStatPoll()V Lcom/android/internal/telephony/dataconnection/DcTracker;->stopDataStallAlarm()V Lcom/android/internal/telephony/dataconnection/DcTracker;->stopNetStatPoll()V Lcom/android/internal/telephony/dataconnection/DcTracker;->unregisterForAllDataDisconnected(Landroid/os/Handler;)V -Lcom/android/internal/telephony/dataconnection/DcTracker;->updateRecords()V Lcom/android/internal/telephony/DctConstants$Activity;->DATAIN:Lcom/android/internal/telephony/DctConstants$Activity; Lcom/android/internal/telephony/DctConstants$Activity;->DATAINANDOUT:Lcom/android/internal/telephony/DctConstants$Activity; Lcom/android/internal/telephony/DctConstants$Activity;->DATAOUT:Lcom/android/internal/telephony/DctConstants$Activity; @@ -3130,7 +3128,6 @@ Lcom/android/internal/telephony/ITelephony;->getCallState()I Lcom/android/internal/telephony/ITelephony;->getDataActivity()I Lcom/android/internal/telephony/ITelephony;->getDataState()I Lcom/android/internal/telephony/ITelephony;->getNetworkType()I -Lcom/android/internal/telephony/ITelephony;->getVoiceMessageCount()I Lcom/android/internal/telephony/ITelephony;->handlePinMmi(Ljava/lang/String;)Z Lcom/android/internal/telephony/ITelephony;->handlePinMmiForSubscriber(ILjava/lang/String;)Z Lcom/android/internal/telephony/ITelephony;->hasIccCard()Z @@ -3201,7 +3198,6 @@ Lcom/android/internal/telephony/Phone;->isVolteEnabled()Z Lcom/android/internal/telephony/Phone;->isWifiCallingEnabled()Z Lcom/android/internal/telephony/Phone;->mCi:Lcom/android/internal/telephony/CommandsInterface; Lcom/android/internal/telephony/Phone;->mContext:Landroid/content/Context; -Lcom/android/internal/telephony/Phone;->mDcTracker:Lcom/android/internal/telephony/dataconnection/DcTracker; Lcom/android/internal/telephony/Phone;->mIccRecords:Ljava/util/concurrent/atomic/AtomicReference; Lcom/android/internal/telephony/Phone;->mImsPhone:Lcom/android/internal/telephony/Phone; Lcom/android/internal/telephony/Phone;->mMmiRegistrants:Landroid/os/RegistrantList; @@ -3294,7 +3290,6 @@ Lcom/android/internal/telephony/ProxyController;->mOldRadioAccessFamily:[I Lcom/android/internal/telephony/ProxyController;->mRadioCapabilitySessionId:I Lcom/android/internal/telephony/ProxyController;->mSetRadioAccessFamilyStatus:[I Lcom/android/internal/telephony/ProxyController;->mUniqueIdGenerator:Ljava/util/concurrent/atomic/AtomicInteger; -Lcom/android/internal/telephony/ProxyController;->registerForAllDataDisconnected(ILandroid/os/Handler;ILjava/lang/Object;)V Lcom/android/internal/telephony/ProxyController;->sendRadioCapabilityRequest(IIIILjava/lang/String;II)V Lcom/android/internal/telephony/ProxyController;->sProxyController:Lcom/android/internal/telephony/ProxyController; Lcom/android/internal/telephony/RadioCapability;->getRadioAccessFamily()I @@ -3394,7 +3389,6 @@ Lcom/android/internal/telephony/ServiceStateTracker;->mVoiceRoamingOnRegistrants Lcom/android/internal/telephony/ServiceStateTracker;->notifyDataRegStateRilRadioTechnologyChanged()V Lcom/android/internal/telephony/ServiceStateTracker;->notifySignalStrength()Z Lcom/android/internal/telephony/ServiceStateTracker;->pollState()V -Lcom/android/internal/telephony/ServiceStateTracker;->powerOffRadioSafely(Lcom/android/internal/telephony/dataconnection/DcTracker;)V Lcom/android/internal/telephony/ServiceStateTracker;->reRegisterNetwork(Landroid/os/Message;)V Lcom/android/internal/telephony/ServiceStateTracker;->resetServiceStateInIwlanMode()V Lcom/android/internal/telephony/ServiceStateTracker;->setOperatorIdd(Ljava/lang/String;)V 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/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/ActivityView.java b/core/java/android/app/ActivityView.java index c879db8967d3..446d98e97936 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -34,7 +34,9 @@ import android.view.InputDevice; import android.view.InputEvent; import android.view.MotionEvent; import android.view.Surface; +import android.view.SurfaceControl; import android.view.SurfaceHolder; +import android.view.SurfaceSession; import android.view.SurfaceView; import android.view.ViewGroup; import android.view.WindowManager; @@ -59,12 +61,16 @@ public class ActivityView extends ViewGroup { private VirtualDisplay mVirtualDisplay; private final SurfaceView mSurfaceView; - private Surface mSurface; + + /** + * This is the root surface for the VirtualDisplay. The VirtualDisplay child surfaces will be + * re-parented to this surface. This will also be a child of the SurfaceView's SurfaceControl. + */ + private SurfaceControl mRootSurfaceControl; private final SurfaceCallback mSurfaceCallback; private StateCallback mActivityViewCallback; - private IActivityManager mActivityManager; private IActivityTaskManager mActivityTaskManager; private IInputForwarder mInputForwarder; // Temp container to store view coordinates on screen. @@ -75,6 +81,9 @@ public class ActivityView extends ViewGroup { private final CloseGuard mGuard = CloseGuard.get(); private boolean mOpened; // Protected by mGuard. + private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction(); + private Surface mTmpSurface = new Surface(); + @UnsupportedAppUsage public ActivityView(Context context) { this(context, null /* attrs */); @@ -87,7 +96,6 @@ public class ActivityView extends ViewGroup { public ActivityView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - mActivityManager = ActivityManager.getService(); mActivityTaskManager = ActivityTaskManager.getService(); mSurfaceView = new SurfaceView(context); mSurfaceCallback = new SurfaceCallback(); @@ -297,14 +305,19 @@ public class ActivityView extends ViewGroup { private class SurfaceCallback implements SurfaceHolder.Callback { @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { - mSurface = mSurfaceView.getHolder().getSurface(); + mTmpSurface = new Surface(); if (mVirtualDisplay == null) { - initVirtualDisplay(); + initVirtualDisplay(new SurfaceSession(surfaceHolder.getSurface())); if (mVirtualDisplay != null && mActivityViewCallback != null) { mActivityViewCallback.onActivityViewReady(ActivityView.this); } } else { - mVirtualDisplay.setSurface(surfaceHolder.getSurface()); + // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by + // whether it has a surface. Setting a fake surface here so DisplayManager will + // consider this display on. + mVirtualDisplay.setSurface(mTmpSurface); + mTmpTransaction.reparent(mRootSurfaceControl, + mSurfaceView.getSurfaceControl().getHandle()).apply(); } updateLocation(); } @@ -319,8 +332,8 @@ public class ActivityView extends ViewGroup { @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { - mSurface.release(); - mSurface = null; + mTmpSurface.release(); + mTmpSurface = null; if (mVirtualDisplay != null) { mVirtualDisplay.setSurface(null); } @@ -328,7 +341,7 @@ public class ActivityView extends ViewGroup { } } - private void initVirtualDisplay() { + private void initVirtualDisplay(SurfaceSession surfaceSession) { if (mVirtualDisplay != null) { throw new IllegalStateException("Trying to initialize for the second time."); } @@ -336,9 +349,13 @@ public class ActivityView extends ViewGroup { final int width = mSurfaceView.getWidth(); final int height = mSurfaceView.getHeight(); final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); + + // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by + // whether it has a surface. Setting a fake surface here so DisplayManager will consider + // this display on. mVirtualDisplay = displayManager.createVirtualDisplay( DISPLAY_NAME + "@" + System.identityHashCode(this), - width, height, getBaseDisplayDensity(), mSurface, + width, height, getBaseDisplayDensity(), mTmpSurface, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY); if (mVirtualDisplay == null) { @@ -348,11 +365,20 @@ public class ActivityView extends ViewGroup { final int displayId = mVirtualDisplay.getDisplay().getDisplayId(); final IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); + + mRootSurfaceControl = new SurfaceControl.Builder(surfaceSession) + .setContainerLayer(true) + .setName(DISPLAY_NAME) + .build(); + try { + wm.reparentDisplayContent(displayId, mRootSurfaceControl.getHandle()); wm.dontOverrideDisplayInfo(displayId); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } + + mTmpTransaction.show(mRootSurfaceControl).apply(); mInputForwarder = InputManager.getInstance().createInputForwarder(displayId); mTaskStackListener = new TaskStackListenerImpl(); try { @@ -392,9 +418,9 @@ public class ActivityView extends ViewGroup { displayReleased = false; } - if (mSurface != null) { - mSurface.release(); - mSurface = null; + if (mTmpSurface != null) { + mTmpSurface.release(); + mTmpSurface = null; } if (displayReleased && mActivityViewCallback != null) { 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.java b/core/java/android/app/Notification.java index 5002a8125d44..0281e6a0631e 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); @@ -3848,6 +3867,9 @@ public class Notification implements Parcelable * The system UI may choose to display a heads-up notification, instead of * launching this intent, while the user is using the device. * </p> + * <p>Apps targeting {@link Build.VERSION_CODES#Q} and above will have to request + * a permission ({@link android.Manifest.permission#USE_FULL_SCREEN_INTENT}) in order to + * use full screen intents.</p> * * @param intent The pending intent to launch. * @param highPriority Passing true will cause this notification to be sent @@ -4449,6 +4471,14 @@ public class Notification implements Parcelable } } + private void bindAlertedIcon(RemoteViews contentView, StandardTemplateParams p) { + contentView.setDrawableTint( + R.id.alerted_icon, + false /* targetBackground */, + getNeutralColor(p), + PorterDuff.Mode.SRC_ATOP); + } + /** * @hide */ @@ -4851,6 +4881,7 @@ public class Notification implements Parcelable bindHeaderTextSecondary(contentView, p); bindHeaderChronometerAndTime(contentView, p); bindProfileBadge(contentView, p); + bindAlertedIcon(contentView, p); } bindActivePermissions(contentView, p); bindExpandButton(contentView, p); diff --git a/core/java/android/app/ProcessMemoryState.java b/core/java/android/app/ProcessMemoryState.java index 9df4fff1e776..95d5d193c29a 100644 --- a/core/java/android/app/ProcessMemoryState.java +++ b/core/java/android/app/ProcessMemoryState.java @@ -32,13 +32,11 @@ public final class ProcessMemoryState implements Parcelable { public final long rssInBytes; public final long cacheInBytes; public final long swapInBytes; - // TODO(rslawik): Delete this field once ProcessMemoryHighWaterMark is ready. - public final long rssHighWatermarkInBytes; public final long startTimeNanos; public ProcessMemoryState(int uid, String processName, int oomScore, long pgfault, long pgmajfault, long rssInBytes, long cacheInBytes, - long swapInBytes, long rssHighWatermarkInBytes, long startTimeNanos) { + long swapInBytes, long startTimeNanos) { this.uid = uid; this.processName = processName; this.oomScore = oomScore; @@ -47,7 +45,6 @@ public final class ProcessMemoryState implements Parcelable { this.rssInBytes = rssInBytes; this.cacheInBytes = cacheInBytes; this.swapInBytes = swapInBytes; - this.rssHighWatermarkInBytes = rssHighWatermarkInBytes; this.startTimeNanos = startTimeNanos; } @@ -60,7 +57,6 @@ public final class ProcessMemoryState implements Parcelable { rssInBytes = in.readLong(); cacheInBytes = in.readLong(); swapInBytes = in.readLong(); - rssHighWatermarkInBytes = in.readLong(); startTimeNanos = in.readLong(); } @@ -91,7 +87,6 @@ public final class ProcessMemoryState implements Parcelable { parcel.writeLong(rssInBytes); parcel.writeLong(cacheInBytes); parcel.writeLong(swapInBytes); - parcel.writeLong(rssHighWatermarkInBytes); parcel.writeLong(startTimeNanos); } } 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/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 27471cac10e9..27ae0b0314d0 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -1898,23 +1898,33 @@ public class WallpaperManager { * @hide */ public static ComponentName getDefaultWallpaperComponent(Context context) { + ComponentName cn = null; + String flat = SystemProperties.get(PROP_WALLPAPER_COMPONENT); if (!TextUtils.isEmpty(flat)) { - final ComponentName cn = ComponentName.unflattenFromString(flat); - if (cn != null) { - return cn; + cn = ComponentName.unflattenFromString(flat); + } + + if (cn == null) { + flat = context.getString(com.android.internal.R.string.default_wallpaper_component); + if (!TextUtils.isEmpty(flat)) { + cn = ComponentName.unflattenFromString(flat); } } - flat = context.getString(com.android.internal.R.string.default_wallpaper_component); - if (!TextUtils.isEmpty(flat)) { - final ComponentName cn = ComponentName.unflattenFromString(flat); - if (cn != null) { - return cn; + // Check if the package exists + if (cn != null) { + try { + final PackageManager packageManager = context.getPackageManager(); + packageManager.getPackageInfo(cn.getPackageName(), + PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); + } catch (PackageManager.NameNotFoundException e) { + cn = null; } } - return null; + return cn; } /** 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/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java index 237082e4cb66..2f0b44f76ffb 100644 --- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.os.Parcel; import android.os.Parcelable; +import android.util.EventLog; /** @@ -30,6 +31,8 @@ import android.os.Parcelable; */ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { + private static final int MAX_DESCRIPTOR_SIZE = 2048; + private final String mName; private final String mDescription; private final String mProvider; @@ -55,6 +58,12 @@ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { mDescription = description; mProvider = provider; mSubclass = subclass; + + if (descriptors == null || descriptors.length > MAX_DESCRIPTOR_SIZE) { + EventLog.writeEvent(0x534e4554, "119819889", -1, ""); + throw new IllegalArgumentException("descriptors must be not null and shorter than " + + MAX_DESCRIPTOR_SIZE); + } mDescriptors = descriptors.clone(); } diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java index 11f8ab7551c2..e3672a7e064f 100644 --- a/core/java/android/bluetooth/BluetoothManager.java +++ b/core/java/android/bluetooth/BluetoothManager.java @@ -52,8 +52,7 @@ import java.util.List; @RequiresFeature(PackageManager.FEATURE_BLUETOOTH) public final class BluetoothManager { private static final String TAG = "BluetoothManager"; - private static final boolean DBG = true; - private static final boolean VDBG = true; + private static final boolean DBG = false; private final BluetoothAdapter mAdapter; diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 437039dcbccf..7d5202d0dbce 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -97,8 +97,7 @@ public abstract class ContentResolver { * * @hide */ - public static final boolean DEPRECATE_DATA_COLUMNS = SystemProperties - .getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false); + public static final boolean DEPRECATE_DATA_COLUMNS = StorageManager.hasIsolatedStorage(); /** * Special filesystem path prefix which indicates that a path should be 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 6fd5061eee63..edfb3a72e933 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1757,33 +1757,6 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.MANAGE_APP_PERMISSIONS"; /** - * Activity action: Launch UI to manage a specific permissions of an app. - * <p> - * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose permission - * will be managed by the launched UI. - * </p> - * <p> - * Input: {@link #EXTRA_PERMISSION_NAME} specifies the (individual) permission - * that should be managed by the launched UI. - * </p> - * <p> - * <li> {@link #EXTRA_USER} specifies the UserHandle of the user that owns the app. - * </p> - * <p> - * Output: Nothing. - * </p> - * - * @see #EXTRA_PACKAGE_NAME - * @see #EXTRA_PERMISSION_NAME - * - * @hide - */ - @SystemApi - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_MANAGE_APP_PERMISSION = - "android.intent.action.MANAGE_APP_PERMISSION"; - - /** * Activity action: Launch UI to manage permissions. * <p> * Input: Nothing. 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 6421dc5a3f68..b7df2bf5a5f7 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -5372,6 +5372,10 @@ public abstract class PackageManager { public abstract void removePackageFromPreferred(String packageName); /** + * @deprecated This function no longer does anything; it was an old + * approach to managing preferred activities, which has been superseded + * by (and conflicts with) the modern activity-based preferences. + * * Retrieve the list of all currently configured preferred packages. The * first package on the list is the most preferred, the last is the least * preferred. @@ -5380,6 +5384,7 @@ public abstract class PackageManager { * @return A List of PackageInfo objects, one for each preferred * application, in order of preference. */ + @Deprecated public abstract List<PackageInfo> getPreferredPackages(@PackageInfoFlags int flags); /** @@ -5406,11 +5411,16 @@ public abstract class PackageManager { ComponentName[] set, ComponentName activity); /** + * @deprecated This is a protected API that should not have been available + * to third party applications. It is the platform's responsibility for + * assigning preferred activities and this cannot be directly modified. + * * Same as {@link #addPreferredActivity(IntentFilter, int, ComponentName[], ComponentName)}, but with a specific userId to apply the preference to. * @hide */ + @Deprecated @UnsupportedAppUsage public void addPreferredActivityAsUser(IntentFilter filter, int match, ComponentName[] set, ComponentName activity, @UserIdInt int userId) { @@ -5444,6 +5454,10 @@ public abstract class PackageManager { ComponentName[] set, ComponentName activity); /** + * @deprecated This is a protected API that should not have been available + * to third party applications. It is the platform's responsibility for + * assigning preferred activities and this cannot be directly modified. + * * Replaces an existing preferred activity mapping to the system, and if that were not present * adds a new preferred activity. This will be used to automatically select the given activity * component when {@link Context#startActivity(Intent) Context.startActivity()} finds multiple @@ -5459,6 +5473,7 @@ public abstract class PackageManager { * * @hide */ + @Deprecated @SystemApi public void replacePreferredActivity(@NonNull IntentFilter filter, int match, @NonNull List<ComponentName> set, @NonNull ComponentName activity) { @@ -5476,6 +5491,10 @@ public abstract class PackageManager { } /** + * @deprecated This function no longer does anything; it was an old + * approach to managing preferred activities, which has been superseded + * by (and conflicts with) the modern activity-based preferences. + * * Remove all preferred activity mappings, previously added with * {@link #addPreferredActivity}, from the * system whose activities are implemented in the given package name. @@ -5484,9 +5503,14 @@ public abstract class PackageManager { * @param packageName The name of the package whose preferred activity * mappings are to be removed. */ + @Deprecated public abstract void clearPackagePreferredActivities(String packageName); /** + * @deprecated This function no longer does anything; it was an old + * approach to managing preferred activities, which has been superseded + * by (and conflicts with) the modern activity-based preferences. + * * Retrieve all preferred activities, previously added with * {@link #addPreferredActivity}, that are * currently registered with the system. @@ -5503,6 +5527,7 @@ public abstract class PackageManager { * (the number of distinct IntentFilter records, not the number of unique * activity components) that were found. */ + @Deprecated public abstract int getPreferredActivities(@NonNull List<IntentFilter> outFilters, @NonNull List<ComponentName> outActivities, String packageName); diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 6f49cc42f6f6..b49c4476e82d 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -28,6 +28,7 @@ import android.content.pm.PackageManager.PackageInfoFlags; import android.content.pm.PackageManager.ResolveInfoFlags; import android.os.Bundle; import android.os.PersistableBundle; +import android.util.ArraySet; import android.util.SparseArray; import com.android.internal.util.function.TriFunction; @@ -37,6 +38,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.function.BiFunction; +import java.util.function.Consumer; /** * Package manager local system service interface. @@ -735,4 +737,22 @@ public abstract class PackageManagerInternal { /** Returns {@code true} if the given user requires extra badging for icons. */ public abstract boolean userNeedsBadging(int userId); + + /** + * Perform the given action for each package. + * Note that packages lock will be held while performin the actions. + * + * @param actionLocked action to be performed + */ + public abstract void forEachPackage(Consumer<PackageParser.Package> actionLocked); + + /** Returns the list of enabled components */ + public abstract ArraySet<String> getEnabledComponents(String packageName, int userId); + + /** Returns the list of disabled components */ + public abstract ArraySet<String> getDisabledComponents(String packageName, int userId); + + /** Returns whether the given package is enabled for the given user */ + public abstract @PackageManager.EnabledState int getApplicationEnabledState( + String packageName, int userId); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index d00c9a036a53..ac18dca74950 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); } } @@ -2508,7 +2513,7 @@ public class PackageParser { // If the storage model feature flag is disabled, we need to fiddle // around with permission definitions to return us to pre-Q behavior. // STOPSHIP(b/112545973): remove once feature enabled by default - if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) { + if (!StorageManager.hasIsolatedStorage()) { if ("android".equals(pkg.packageName)) { final ArraySet<String> newGroups = new ArraySet<>(); newGroups.add(android.Manifest.permission_group.MEDIA_AURAL); @@ -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/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java index 79e15a7a9a2d..0ec812fe0350 100644 --- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java +++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java @@ -30,19 +30,29 @@ import java.util.concurrent.Executor; public interface BiometricAuthenticator { /** + * No biometric methods or nothing has been enrolled. + * Move/expose these in BiometricPrompt if we ever want to allow applications to "blacklist" + * modalities when calling authenticate(). * @hide */ - int TYPE_FINGERPRINT = 1; + int TYPE_NONE = 0; + /** + * Constant representing fingerprint. + * @hide + */ + int TYPE_FINGERPRINT = 1 << 0; /** + * Constant representing iris. * @hide */ - int TYPE_IRIS = 2; + int TYPE_IRIS = 1 << 1; /** + * Constant representing face. * @hide */ - int TYPE_FACE = 3; + int TYPE_FACE = 1 << 2; /** * Container for biometric data diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index bd149fd05f59..b238d778f55a 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -251,27 +251,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan private Executor mExecutor; private AuthenticationCallback mAuthenticationCallback; - IBiometricPromptReceiver mDialogReceiver = new IBiometricPromptReceiver.Stub() { - @Override - public void onDialogDismissed(int reason) { - // Check the reason and invoke OnClickListener(s) if necessary - if (reason == DISMISSED_REASON_POSITIVE) { - mPositiveButtonInfo.executor.execute(() -> { - mPositiveButtonInfo.listener.onClick(null, DialogInterface.BUTTON_POSITIVE); - }); - } else if (reason == DISMISSED_REASON_NEGATIVE) { - mNegativeButtonInfo.executor.execute(() -> { - mNegativeButtonInfo.listener.onClick(null, DialogInterface.BUTTON_NEGATIVE); - }); - } - } - }; - - IBiometricServiceReceiver mBiometricServiceReceiver = + private final IBiometricServiceReceiver mBiometricServiceReceiver = new IBiometricServiceReceiver.Stub() { @Override - public void onAuthenticationSucceeded(long deviceId) throws RemoteException { + public void onAuthenticationSucceeded() throws RemoteException { mExecutor.execute(() -> { final AuthenticationResult result = new AuthenticationResult(mCryptoObject); mAuthenticationCallback.onAuthenticationSucceeded(result); @@ -279,26 +263,39 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } @Override - public void onAuthenticationFailed(long deviceId) throws RemoteException { + public void onAuthenticationFailed() throws RemoteException { mExecutor.execute(() -> { mAuthenticationCallback.onAuthenticationFailed(); }); } @Override - public void onError(long deviceId, int error, String message) - throws RemoteException { + public void onError(int error, String message) throws RemoteException { mExecutor.execute(() -> { mAuthenticationCallback.onAuthenticationError(error, message); }); } @Override - public void onAcquired(long deviceId, int acquireInfo, String message) { + public void onAcquired(int acquireInfo, String message) throws RemoteException { mExecutor.execute(() -> { mAuthenticationCallback.onAuthenticationHelp(acquireInfo, message); }); } + + @Override + public void onDialogDismissed(int reason) throws RemoteException { + // Check the reason and invoke OnClickListener(s) if necessary + if (reason == DISMISSED_REASON_POSITIVE) { + mPositiveButtonInfo.executor.execute(() -> { + mPositiveButtonInfo.listener.onClick(null, DialogInterface.BUTTON_POSITIVE); + }); + } else if (reason == DISMISSED_REASON_NEGATIVE) { + mNegativeButtonInfo.executor.execute(() -> { + mNegativeButtonInfo.listener.onClick(null, DialogInterface.BUTTON_NEGATIVE); + }); + } + } }; private BiometricPrompt(Context context, Bundle bundle, @@ -557,9 +554,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan mExecutor = executor; mAuthenticationCallback = callback; final long sessionId = crypto != null ? crypto.getOpId() : 0; - mService.authenticate(mToken, sessionId, userId, - mBiometricServiceReceiver, 0 /* flags */, mContext.getOpPackageName(), - mBundle, mDialogReceiver); + mService.authenticate(mToken, sessionId, userId, mBiometricServiceReceiver, + mContext.getOpPackageName(), mBundle); } catch (RemoteException e) { Log.e(TAG, "Remote exception while authenticating", e); mExecutor.execute(() -> { diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl index e17feff0160a..53a076135aaa 100644 --- a/core/java/android/hardware/biometrics/IBiometricService.aidl +++ b/core/java/android/hardware/biometrics/IBiometricService.aidl @@ -18,7 +18,6 @@ package android.hardware.biometrics; import android.os.Bundle; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; -import android.hardware.biometrics.IBiometricPromptReceiver; import android.hardware.biometrics.IBiometricServiceReceiver; /** @@ -32,8 +31,7 @@ interface IBiometricService { // Requests authentication. The service choose the appropriate biometric to use, and show // the corresponding BiometricDialog. void authenticate(IBinder token, long sessionId, int userId, - IBiometricServiceReceiver receiver, int flags, String opPackageName, - in Bundle bundle, IBiometricPromptReceiver dialogReceiver); + IBiometricServiceReceiver receiver, String opPackageName, in Bundle bundle); // Cancel authentication for the given sessionId void cancelAuthentication(IBinder token, String opPackageName); @@ -46,4 +44,8 @@ interface IBiometricService { // Explicitly set the active user. void setActiveUser(int userId); + + // Notify BiometricService when <Biometric>Service is ready to start the prepared client. + // Client lifecycle is still managed in <Biometric>Service. + void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId); } diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl index a6e3696eeb10..22ef33e86e17 100644 --- a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl +++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl @@ -16,12 +16,18 @@ package android.hardware.biometrics; /** - * Communication channel from the BiometricService back to BiometricPrompt. + * Communication channel from BiometricService back to BiometricPrompt * @hide */ oneway interface IBiometricServiceReceiver { - void onAuthenticationSucceeded(long deviceId); - void onAuthenticationFailed(long deviceId); - void onError(long deviceId, int error, String message); - void onAcquired(long deviceId, int acquiredInfo, String message); + // Notify BiometricPrompt that authentication was successful + void onAuthenticationSucceeded(); + // Noties that authentication failed. + void onAuthenticationFailed(); + // Notify BiometricPrompt that an error has occurred. + void onError(int error, String message); + // Notifies that a biometric has been acquired. + void onAcquired(int acquiredInfo, String message); + // Notifies that the SystemUI dialog has been dismissed. + void onDialogDismissed(int reason); } diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl new file mode 100644 index 000000000000..180daaf97ada --- /dev/null +++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl @@ -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 android.hardware.biometrics; + +/** + * Communication channel from + * 1) BiometricDialogImpl (SysUI) back to BiometricService + * 2) <Biometric>Service back to BiometricService + * Receives messages from the above and does some handling before forwarding to BiometricPrompt + * via IBiometricServiceReceiver. + * @hide + */ +oneway interface IBiometricServiceReceiverInternal { + // Notify BiometricService that authentication was successful. If user confirmation is required, + // the auth token must be submitted into KeyStore. + void onAuthenticationSucceeded(boolean requireConfirmation, in byte[] token); + // Notify BiometricService that an error has occurred. + void onAuthenticationFailed(int cookie, boolean requireConfirmation); + // Notify BiometricService than an error has occured. Forward to the correct receiver depending + // on the cookie. + void onError(int cookie, int error, String message); + // Notifies that a biometric has been acquired. + void onAcquired(int acquiredInfo, String message); + // Notifies that the SystemUI dialog has been dismissed. + void onDialogDismissed(int reason); + // Notifies that the user has pressed the "try again" button on SystemUI + void onTryAgainPressed(); +} 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/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index 47df8e8f9729..a15dcec3b276 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -15,9 +15,7 @@ */ package android.hardware.face; -import android.os.Bundle; -import android.hardware.biometrics.IBiometricPromptReceiver; -import android.hardware.biometrics.IBiometricServiceReceiver; +import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.hardware.face.IFaceServiceReceiver; import android.hardware.face.Face; @@ -32,19 +30,24 @@ interface IFaceService { void authenticate(IBinder token, long sessionId, IFaceServiceReceiver receiver, int flags, String opPackageName); - // This method invokes the BiometricDialog. The arguments are almost the same as above, - // but should only be called from (BiometricPromptService). - void authenticateFromService(boolean requireConfirmation, IBinder token, long sessionId, - int userId, IBiometricServiceReceiver receiver, int flags, String opPackageName, - in Bundle bundle, IBiometricPromptReceiver dialogReceiver, - int callingUid, int callingPid, int callingUserId); + // This method prepares the service to start authenticating, but doesn't start authentication. + // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be + // called from BiometricService. The additional uid, pid, userId arguments should be determined + // by BiometricService. To start authentication after the clients are ready, use + // startPreparedClient(). + void prepareForAuthentication(boolean requireConfirmation, IBinder token, long sessionId, + int userId, IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName, + int cookie, int callingUid, int callingPid, int callingUserId); + + // Starts authentication with the previously prepared client. + void startPreparedClient(int cookie); // Cancel authentication for the given sessionId void cancelAuthentication(IBinder token, String opPackageName); // Same as above, with extra arguments. void cancelAuthenticationFromService(IBinder token, String opPackageName, - int callingUid, int callingPid, int callingUserId); + int callingUid, int callingPid, int callingUserId, boolean fromClient); // Start face enrollment void enroll(IBinder token, in byte [] cryptoToken, int userId, IFaceServiceReceiver receiver, diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 2662a11c2dd4..dd6b29d87d67 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -15,9 +15,7 @@ */ package android.hardware.fingerprint; -import android.os.Bundle; -import android.hardware.biometrics.IBiometricPromptReceiver; -import android.hardware.biometrics.IBiometricServiceReceiver; +import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.hardware.fingerprint.IFingerprintClientActiveCallback; import android.hardware.fingerprint.IFingerprintServiceReceiver; @@ -35,22 +33,25 @@ interface IFingerprintService { void authenticate(IBinder token, long sessionId, int userId, IFingerprintServiceReceiver receiver, int flags, String opPackageName); - // This method invokes the BiometricDialog. The arguments are almost the same as above, except - // this is protected by the MANAGE_BIOMETRIC signature permission. This method should only be - // called from BiometricPromptService. The additional uid, pid, userId arguments should be - // determined by BiometricPromptService. - void authenticateFromService(IBinder token, long sessionId, int userId, - IBiometricServiceReceiver receiver, int flags, String opPackageName, - in Bundle bundle, IBiometricPromptReceiver dialogReceiver, + // This method prepares the service to start authenticating, but doesn't start authentication. + // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be + // called from BiometricService. The additional uid, pid, userId arguments should be determined + // by BiometricService. To start authentication after the clients are ready, use + // startPreparedClient(). + void prepareForAuthentication(IBinder token, long sessionId, int userId, + IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId); + // Starts authentication with the previously prepared client. + void startPreparedClient(int cookie); + // Cancel authentication for the given sessionId void cancelAuthentication(IBinder token, String opPackageName); // Same as above, except this is protected by the MANAGE_BIOMETRIC signature permission. Takes // an additional uid, pid, userid. void cancelAuthenticationFromService(IBinder token, String opPackageName, - int callingUid, int callingPid, int callingUserId); + int callingUid, int callingPid, int callingUserId, boolean fromClient); // Start fingerprint enrollment void enroll(IBinder token, in byte [] cryptoToken, int groupId, IFingerprintServiceReceiver receiver, diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 1468fe5bfca8..64314a7d8060 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -928,7 +928,9 @@ public class Binder implements IBinder { final long origWorkSource = ThreadLocalWorkSource.setUid(Binder.getCallingUid()); try { if (tracingEnabled) { - Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":" + code); + final String transactionName = getTransactionName(code); + Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":" + + (transactionName != null ? transactionName : code)); } res = onTransact(code, data, reply, flags); } catch (RemoteException|RuntimeException e) { 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/UserManager.java b/core/java/android/os/UserManager.java index 92b316914d82..abfcfaacde21 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -2463,6 +2463,27 @@ public class UserManager { } /** + * Get the parent of a user profile. + * + * @param user the handle of the user profile + * + * @return the parent of the user or {@code null} if the user is not profile + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + public @Nullable UserHandle getProfileParent(@NonNull UserHandle user) { + UserInfo info = getProfileParent(user.getIdentifier()); + + if (info == null) { + return null; + } + + return UserHandle.of(info.id); + } + + /** * Enables or disables quiet mode for a managed profile. If quiet mode is enabled, apps in a * managed profile don't run, generate notifications, or consume data or battery. * <p> diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index b42f1c4df4e3..8e11d858128a 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -25,6 +25,7 @@ import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.annotation.WorkerThread; import android.app.Activity; @@ -1533,6 +1534,12 @@ public class StorageManager { return SystemProperties.getBoolean(PROP_HAS_ADOPTABLE, false); } + /** {@hide} */ + @TestApi + public static boolean hasIsolatedStorage() { + return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false); + } + /** * @deprecated disabled now that FUSE has been replaced by sdcardfs * @hide diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index d7729037bfad..c297ef4f2a85 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5075,6 +5075,7 @@ public final class Settings { * @hide */ @SystemApi + @TestApi @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) public static void resetToDefaults(@NonNull ContentResolver resolver, @Nullable String tag) { @@ -8232,6 +8233,24 @@ public final class Settings { public static final String NOTIFICATION_NEW_INTERRUPTION_MODEL = "new_interruption_model"; /** + * How often to check for location access. + * @hide + */ + @SystemApi + @TestApi + public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = + "location_access_check_interval_millis"; + + /** + * Delay between granting location access and checking it. + * @hide + */ + @SystemApi + @TestApi + public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS = + "location_access_check_delay_millis"; + + /** * This are the settings to be backed up. * * NOTE: Settings are backed up and restored in the order they appear diff --git a/core/java/android/provider/TEST_MAPPING b/core/java/android/provider/TEST_MAPPING new file mode 100644 index 000000000000..8e67ce7b2aa9 --- /dev/null +++ b/core/java/android/provider/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.provider.SettingsBackupTest" + } + ] + } + ] +} diff --git a/core/java/android/service/oemlock/IOemLockService.aidl b/core/java/android/service/oemlock/IOemLockService.aidl index d5e10d6629ca..99cffc5c5b8b 100644 --- a/core/java/android/service/oemlock/IOemLockService.aidl +++ b/core/java/android/service/oemlock/IOemLockService.aidl @@ -22,6 +22,8 @@ package android.service.oemlock; * @hide */ interface IOemLockService { + String getLockName(); + void setOemUnlockAllowedByCarrier(boolean allowed, in byte[] signature); boolean isOemUnlockAllowedByCarrier(); diff --git a/core/java/android/service/oemlock/OemLockManager.java b/core/java/android/service/oemlock/OemLockManager.java index f0d660354167..029d645dda19 100644 --- a/core/java/android/service/oemlock/OemLockManager.java +++ b/core/java/android/service/oemlock/OemLockManager.java @@ -44,6 +44,23 @@ public class OemLockManager { } /** + * Returns a vendor specific name for the OEM lock. + * + * This value is used to identify the security protocol used by locks. + * + * @return The name of the OEM lock or {@code null} if failed to get the name. + */ + @RequiresPermission(android.Manifest.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE) + @Nullable + public String getLockName() { + try { + return mService.getLockName(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Sets whether the carrier has allowed this device to be OEM unlocked. * * Depending on the implementation, the validity of the request might need to be proved. This 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 45d53f340f35..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; @@ -442,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; } @@ -568,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) { } /** @@ -1049,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); } } } @@ -1320,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); } @@ -1394,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/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index dace388b66f6..c4be0e504c5b 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -304,7 +304,7 @@ interface IWindowManager /** * Get the position of the nav bar */ - int getNavBarPosition(); + int getNavBarPosition(int displayId); /** * Lock the device immediately with the specified options (can be null). @@ -551,4 +551,16 @@ interface IWindowManager * @see KeyguardManager#isDeviceLocked() */ void setShouldShowIme(int displayId, boolean shouldShow); + + /** + * Reparent the top layers for a display to the requested surfaceControl. The display that + * is going to be re-parented (the displayId passed in) needs to have been created by the same + * process that is requesting the re-parent. This is to ensure clients can't just re-parent + * display content info to any SurfaceControl, as this would be a security issue. + * + * @param displayId The id of the display. + * @param surfaceControlHandle The SurfaceControl handle that the top level layers for the + * display should be re-parented to. + */ + void reparentDisplayContent(int displayId, in IBinder surfaceControlHandle); } 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 46f396a0b66b..ab010855b896 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -104,6 +104,8 @@ public class SurfaceControl implements Parcelable { int flags, int mask); private static native void nativeSetWindowCrop(long transactionObj, long nativeObject, int l, int t, int r, int b); + private static native void nativeSetCornerRadius(long transactionObj, long nativeObject, + float cornerRadius); private static native void nativeSetLayerStack(long transactionObj, long nativeObject, int layerStack); @@ -1006,6 +1008,18 @@ public class SurfaceControl implements Parcelable { } } + /** + * Sets the corner radius of a {@link SurfaceControl}. + * + * @param cornerRadius Corner radius in pixels. + */ + public void setCornerRadius(float cornerRadius) { + checkNotReleased(); + synchronized (SurfaceControl.class) { + sGlobalTransaction.setCornerRadius(this, cornerRadius); + } + } + public void setLayerStack(int layerStack) { checkNotReleased(); synchronized(SurfaceControl.class) { @@ -1529,6 +1543,20 @@ public class SurfaceControl implements Parcelable { return this; } + /** + * Sets the corner radius of a {@link SurfaceControl}. + * @param sc SurfaceControl + * @param cornerRadius Corner radius in pixels. + * @return Itself. + */ + @UnsupportedAppUsage + public Transaction setCornerRadius(SurfaceControl sc, float cornerRadius) { + sc.checkNotReleased(); + nativeSetCornerRadius(mNativeObject, sc.mNativeObject, cornerRadius); + + return this; + } + @UnsupportedAppUsage public Transaction setLayerStack(SurfaceControl sc, int layerStack) { sc.checkNotReleased(); diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 3c4ce8f7cfad..797d1c5f47aa 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -1126,6 +1126,13 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb } }; + /** + * @hide + */ + public SurfaceControl getSurfaceControl() { + return mSurfaceControl; + } + class SurfaceControlWithBackground extends SurfaceControl { SurfaceControl mBackgroundControl; private boolean mOpaque = true; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 2767505aece3..c5d03741dbba 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -4159,7 +4159,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, float mAlpha = 1f; /** - * The opacity of the view as manipulated by the Fade transition. This is a hidden + * The opacity of the view as manipulated by the Fade transition. This is a * property only used by transitions, which is composited with the other alpha * values to calculate the final visual alpha value. */ @@ -15729,15 +15729,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * This property is hidden and intended only for use by the Fade transition, which - * animates it to produce a visual translucency that does not side-effect (or get - * affected by) the real alpha property. This value is composited with the other - * alpha value (and the AlphaAnimation value, when that is present) to produce - * a final visual translucency result, which is what is passed into the DisplayList. - * - * @hide + * This property is intended only for use by the Fade transition, which animates it + * to produce a visual translucency that does not side-effect (or get affected by) + * the real alpha property. This value is composited with the other alpha value + * (and the AlphaAnimation value, when that is present) to produce a final visual + * translucency result, which is what is passed into the DisplayList. */ - @UnsupportedAppUsage public void setTransitionAlpha(float alpha) { ensureTransformationInfo(); if (mTransformationInfo.mTransitionAlpha != alpha) { @@ -15760,16 +15757,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * This property is hidden and intended only for use by the Fade transition, which - * animates it to produce a visual translucency that does not side-effect (or get - * affected by) the real alpha property. This value is composited with the other - * alpha value (and the AlphaAnimation value, when that is present) to produce - * a final visual translucency result, which is what is passed into the DisplayList. - * - * @hide + * This property is intended only for use by the Fade transition, which animates + * it to produce a visual translucency that does not side-effect (or get affected + * by) the real alpha property. This value is composited with the other alpha + * value (and the AlphaAnimation value, when that is present) to produce a final + * visual translucency result, which is what is passed into the DisplayList. */ @ViewDebug.ExportedProperty(category = "drawing") - @UnsupportedAppUsage public float getTransitionAlpha() { return mTransformationInfo != null ? mTransformationInfo.mTransitionAlpha : 1; } @@ -16288,9 +16282,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } - /** @hide */ - @UnsupportedAppUsage - public void setAnimationMatrix(Matrix matrix) { + /** + * Changes the transformation matrix on the view. This is used in animation frameworks, + * such as {@link android.transition.Transition}. When the animation finishes, the matrix + * should be cleared by calling this method with <code>null</code> as the matrix parameter. + * Application developers should use transformation methods like {@link #setRotation(float)}, + * {@link #setScaleX(float)}, {@link #setScaleX(float)}, {@link #setTranslationX(float)}} + * and {@link #setTranslationY(float)} (float)}} instead. + * + * @param matrix The matrix, null indicates that the matrix should be cleared. + */ + public void setAnimationMatrix(@Nullable Matrix matrix) { invalidateViewProperty(true, false); mRenderNode.setAnimationMatrix(matrix); invalidateViewProperty(false, true); @@ -21460,7 +21462,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * previous ones * {@hide} */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) protected boolean setFrame(int left, int top, int right, int bottom) { boolean changed = false; @@ -21537,7 +21539,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #setLeft(int), #setRight(int), #setTop(int), #setBottom(int) */ - public void setLeftTopRightBottom(int left, int top, int right, int bottom) { + public final void setLeftTopRightBottom(int left, int top, int right, int bottom) { setFrame(left, top, right, bottom); } @@ -23545,6 +23547,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Get the identifier used for this view by the drawing system. + * + * @see RenderNode#getUniqueId() + * @return A long that uniquely identifies this view's drawing component + */ + public long getUniqueDrawingId() { + return mRenderNode.getUniqueId(); + } + + /** * Returns this view's tag. * * @return the Object stored in this view as a tag, or {@code null} if not diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 1e91aa87bfb7..741510e67130 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -7042,10 +7042,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * suppression is disabled with a later call to suppressLayout(false). * When layout suppression is disabled, a requestLayout() call is sent * if layout() was attempted while layout was being suppressed. - * - * @hide */ - @UnsupportedAppUsage public void suppressLayout(boolean suppress) { mSuppressLayout = suppress; if (!suppress) { @@ -7061,8 +7058,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * suppressed, due to an earlier call to {@link #suppressLayout(boolean)}. * * @return true if layout calls are currently suppressed, false otherwise. - * - * @hide */ public boolean isLayoutSuppressed() { return mSuppressLayout; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index d9d52c01924f..937e23813cec 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -459,7 +459,7 @@ public final class ViewRootImpl implements ViewParent, final DisplayCutout.ParcelableWrapper mPendingDisplayCutout = new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT); boolean mPendingAlwaysConsumeNavBar; - private InsetsState mPendingInsets; + private InsetsState mPendingInsets = new InsetsState(); final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets = new ViewTreeObserver.InternalInsetsInfo(); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 8d8a37061035..45c36516b885 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1752,15 +1752,6 @@ public interface WindowManager extends ViewManager { public static final int PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 0x00100000; /** - * If this flag is set on the window, window manager will acquire a sleep token that puts - * all activities to sleep as long as this window is visible. When this flag is set, the - * window needs to occlude all activity windows. - * @hide - */ - @RequiresPermission(permission.DEVICE_POWER) - public static final int PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN = 0x00200000; - - /** * Flag to indicate that this window should be considered a screen decoration similar to the * nav bar and status bar. This will cause this window to affect the window insets reported * to other windows when it is visible. @@ -1872,10 +1863,6 @@ public interface WindowManager extends ViewManager { equals = PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY, name = "IS_ROUNDED_CORNERS_OVERLAY"), @ViewDebug.FlagToString( - mask = PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN, - equals = PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN, - name = "ACQUIRES_SLEEP_TOKEN"), - @ViewDebug.FlagToString( mask = PRIVATE_FLAG_IS_SCREEN_DECOR, equals = PRIVATE_FLAG_IS_SCREEN_DECOR, name = "IS_SCREEN_DECOR"), diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java index 260e93890e3e..16bafe2c4f11 100644 --- a/core/java/android/view/WindowManagerPolicyConstants.java +++ b/core/java/android/view/WindowManagerPolicyConstants.java @@ -44,6 +44,7 @@ public interface WindowManagerPolicyConstants { int PRESENCE_EXTERNAL = 1 << 1; // Navigation bar position values + int NAV_BAR_INVALID = -1; int NAV_BAR_LEFT = 1 << 0; int NAV_BAR_RIGHT = 1 << 1; int NAV_BAR_BOTTOM = 1 << 2; diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index 9ccd3211768d..f78180276373 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; import android.graphics.Rect; +import android.os.Build; import android.os.Bundle; import android.os.Trace; import android.util.AttributeSet; @@ -107,7 +108,7 @@ public class GridView extends AbsListView { */ public static final int AUTO_FIT = -1; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521080) private int mNumColumns = AUTO_FIT; @UnsupportedAppUsage @@ -117,7 +118,7 @@ public class GridView extends AbsListView { @UnsupportedAppUsage private int mVerticalSpacing = 0; private int mStretchMode = STRETCH_COLUMN_WIDTH; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521079) private int mColumnWidth; @UnsupportedAppUsage private int mRequestedColumnWidth; diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index a28cc403ed56..157992a98a4a 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -18,6 +18,8 @@ package android.widget; import android.annotation.CallSuper; import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.Px; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.annotation.Widget; @@ -30,6 +32,7 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Bundle; import android.text.InputFilter; import android.text.InputType; @@ -88,6 +91,16 @@ import java.util.Locale; * of the current value. Tapping on the current value allows to type in a * desired value. * </li> + * <li> + * If the current theme is derived from {@link android.R.style#Theme_Material} + * the widget presents the current value as a scrolling vertical selector with + * the selected value in the center and the previous and following numbers above + * and below, separated by a divider. The value is changed by flinging vertically. + * The thickness of the divider can be changed by using the + * {@link android.R.attr#selectionDividerHeight} attribute and the color of the + * divider can be changed by using the + * {@link android.R.attr#colorControlNormal} attribute. + * </li> * </ul> * <p> * For an example of using this widget, see {@link android.widget.TimePicker}. @@ -436,14 +449,14 @@ public class NumberPicker extends LinearLayout { /** * Divider for showing item to be selected while scrolling */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private final Drawable mSelectionDivider; /** * The height of the selection divider. */ - @UnsupportedAppUsage - private final int mSelectionDividerHeight; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + private int mSelectionDividerHeight; /** * The current scroll state of the number picker. @@ -1556,6 +1569,24 @@ public class NumberPicker extends LinearLayout { return mSelectorIndexToStringCache.get(getValue()); } + /** + * Set the height for the divider that separates the currently selected value from the others. + * @param height The height to be set + */ + public void setSelectionDividerHeight(@IntRange(from = 0) @Px int height) { + mSelectionDividerHeight = height; + invalidate(); + } + + /** + * Retrieve the height for the divider that separates the currently selected value from the + * others. + * @return The height of the divider + */ + public int getSelectionDividerHeight() { + return mSelectionDividerHeight; + } + @Override protected float getTopFadingEdgeStrength() { return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 85d851a3116c..1deee8af3d75 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -6254,8 +6254,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mHint; } - @UnsupportedAppUsage - boolean isSingleLine() { + /** + * Returns if the text is constrained to a single horizontally scrolling line ignoring new + * line characters instead of letting it wrap onto multiple lines. + * + * @attr ref android.R.styleable#TextView_singleLine + */ + public boolean isSingleLine() { return mSingleLine; } diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index 34e8ed406200..875d7c9ee7a6 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -52,17 +52,23 @@ public class BinderCallsStats implements BinderInternal.Observer { public static final boolean ENABLED_DEFAULT = false; public static final boolean DETAILED_TRACKING_DEFAULT = true; public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 100; + public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 5000; + + private static class OverflowBinder extends Binder {} private static final String TAG = "BinderCallsStats"; private static final int CALL_SESSIONS_POOL_SIZE = 100; private static final int MAX_EXCEPTION_COUNT_SIZE = 50; private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow"; + private static final Class<? extends Binder> OVERFLOW_BINDER = OverflowBinder.class; + private static final int OVERFLOW_TRANSACTION_CODE = -1; // Whether to collect all the data: cpu + exceptions + reply/request sizes. private boolean mDetailedTracking = DETAILED_TRACKING_DEFAULT; // Sampling period to control how often to track CPU usage. 1 means all calls, 100 means ~1 out // of 100 requests. private int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT; + private int mMaxBinderCallStatsCount = MAX_BINDER_CALL_STATS_COUNT_DEFAULT; @GuardedBy("mLock") private final SparseArray<UidEntry> mUidEntries = new SparseArray<>(); @GuardedBy("mLock") @@ -71,6 +77,7 @@ public class BinderCallsStats implements BinderInternal.Observer { private final Object mLock = new Object(); private final Random mRandom; private long mStartTime = System.currentTimeMillis(); + private long mCallStatsCount = 0; private CachedDeviceState.Readonly mDeviceState; @@ -158,7 +165,13 @@ public class BinderCallsStats implements BinderInternal.Observer { final CallStat callStat = uidEntry.getOrCreate( callingUid, s.binderClass, s.transactionCode, - mDeviceState.isScreenInteractive()); + mDeviceState.isScreenInteractive(), + mCallStatsCount >= mMaxBinderCallStatsCount); + final boolean isNewCallStat = callStat.callCount == 0; + if (isNewCallStat) { + mCallStatsCount++; + } + callStat.callCount++; callStat.recordedCallCount++; callStat.cpuTimeMicros += duration; @@ -444,6 +457,24 @@ public class BinderCallsStats implements BinderInternal.Observer { } } + /** + * Sets the maximum number of items to track. + */ + public void setMaxBinderCallStats(int maxKeys) { + if (maxKeys <= 0) { + Slog.w(TAG, "Ignored invalid max value (value must be positive): " + + maxKeys); + return; + } + + synchronized (mLock) { + if (maxKeys != mMaxBinderCallStatsCount) { + mMaxBinderCallStatsCount = maxKeys; + reset(); + } + } + } + public void setSamplingInterval(int samplingInterval) { if (samplingInterval <= 0) { Slog.w(TAG, "Ignored invalid sampling interval (value must be positive): " @@ -461,6 +492,7 @@ public class BinderCallsStats implements BinderInternal.Observer { public void reset() { synchronized (mLock) { + mCallStatsCount = 0; mUidEntries.clear(); mExceptionCounts.clear(); mStartTime = System.currentTimeMillis(); @@ -595,10 +627,21 @@ public class BinderCallsStats implements BinderInternal.Observer { } CallStat getOrCreate(int callingUid, Class<? extends Binder> binderClass, - int transactionCode, boolean screenInteractive) { + int transactionCode, boolean screenInteractive, boolean maxCallStatsReached) { CallStat mapCallStat = get(callingUid, binderClass, transactionCode, screenInteractive); - // Only create CallStat if it's a new entry, otherwise update existing instance + // Only create CallStat if it's a new entry, otherwise update existing instance. if (mapCallStat == null) { + if (maxCallStatsReached) { + mapCallStat = get(callingUid, OVERFLOW_BINDER, OVERFLOW_TRANSACTION_CODE, + screenInteractive); + if (mapCallStat != null) { + return mapCallStat; + } + + binderClass = OVERFLOW_BINDER; + transactionCode = OVERFLOW_TRANSACTION_CODE; + } + mapCallStat = new CallStat(callingUid, binderClass, transactionCode, screenInteractive); CallStatKey key = new CallStatKey(); diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java index 2e901e4520e6..2742b7cdf872 100644 --- a/core/java/com/android/internal/os/KernelCpuThreadReader.java +++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java @@ -108,13 +108,6 @@ public class KernelCpuThreadReader { private static final Predicate<Integer> DEFAULT_UID_PREDICATE = uid -> uid == 1000; /** - * Do not report any threads that have a total CPU usage (across all frequencies) less than or - * equal to this number. This significantly reduces the amount of reported threads without - * losing any important information - */ - private static final int TOTAL_CPU_USAGE_THRESHOLD_MILLIS = 20; - - /** * Value returned when there was an error getting an integer ID value (e.g. PID, UID) */ private static final int ID_ERROR = -1; @@ -345,15 +338,6 @@ public class KernelCpuThreadReader { } int[] cpuUsages = mFrequencyBucketCreator.getBucketedValues(cpuUsagesLong); - // Filter threads that have low total CPU usage - int cpuUsageSum = 0; - for (int i = 0; i < cpuUsages.length; i++) { - cpuUsageSum += cpuUsages[i]; - } - if (cpuUsageSum <= TOTAL_CPU_USAGE_THRESHOLD_MILLIS) { - return null; - } - return new ThreadCpuUsage(threadId, threadName, cpuUsages); } diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 3b7ce0a22b18..488b9912ee49 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -41,6 +41,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Configuration; +import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Color; @@ -1891,7 +1892,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 +1993,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/policy/ScreenDecorationsUtils.java b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java new file mode 100644 index 000000000000..100c6ee6763b --- /dev/null +++ b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java @@ -0,0 +1,50 @@ +/* + * 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.policy; + +import com.android.internal.R; + +import android.content.res.Resources; + +/** + * Utility functions for screen decorations used by both window manager and System UI. + */ +public class ScreenDecorationsUtils { + + /** + * Corner radius that should be used on windows in order to cover the display. + * These values are expressed in pixels because they should not respect display or font + * scaling, this means that we don't have to reload them on config changes. + */ + public static float getWindowCornerRadius(Resources resources) { + // Radius that should be used in case top or bottom aren't defined. + float defaultRadius = resources.getDimension(R.dimen.rounded_corner_radius); + + float topRadius = resources.getDimension(R.dimen.rounded_corner_radius_top); + if (topRadius == 0) { + topRadius = defaultRadius; + } + float bottomRadius = resources.getDimension(R.dimen.rounded_corner_radius_bottom); + if (bottomRadius == 0) { + bottomRadius = defaultRadius; + } + + // Always use the smallest radius to make sure the rounded corners will + // completely cover the display. + return Math.min(topRadius, bottomRadius); + } +} diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 604537ffee03..600b1b324257 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -18,7 +18,7 @@ package com.android.internal.statusbar; import android.content.ComponentName; import android.graphics.Rect; -import android.hardware.biometrics.IBiometricPromptReceiver; +import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.os.Bundle; import android.service.notification.StatusBarNotification; @@ -141,7 +141,7 @@ oneway interface IStatusBar void showShutdownUi(boolean isReboot, String reason); // Used to show the dialog when BiometricService starts authentication - void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver, int type, + void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type, boolean requireConfirmation, int userId); // Used to hide the dialog when a biometric is authenticated void onBiometricAuthenticated(); @@ -151,4 +151,6 @@ oneway interface IStatusBar void onBiometricError(String error); // Used to hide the biometric dialog when the AuthenticationClient is stopped void hideBiometricDialog(); + // Used to request the "try again" button for authentications which requireConfirmation=true + void showBiometricTryAgain(); } diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index b7ffb5768a31..bf82dc610ad4 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -21,7 +21,7 @@ import android.content.ComponentName; import android.graphics.Rect; import android.os.Bundle; import android.service.notification.StatusBarNotification; -import android.hardware.biometrics.IBiometricPromptReceiver; +import android.hardware.biometrics.IBiometricServiceReceiverInternal; import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.StatusBarIcon; @@ -92,7 +92,7 @@ interface IStatusBarService void showPinningEscapeToast(); // Used to show the dialog when BiometricService starts authentication - void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver, int type, + void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type, boolean requireConfirmation, int userId); // Used to hide the dialog when a biometric is authenticated void onBiometricAuthenticated(); @@ -102,4 +102,6 @@ interface IStatusBarService void onBiometricError(String error); // Used to hide the biometric dialog when the AuthenticationClient is stopped void hideBiometricDialog(); + // Used to request the "try again" button for authentications which requireConfirmation=true + void showBiometricTryAgain(); } diff --git a/core/java/com/android/internal/util/BitUtils.java b/core/java/com/android/internal/util/BitUtils.java index 17d5a2e36974..61581458f98a 100644 --- a/core/java/com/android/internal/util/BitUtils.java +++ b/core/java/com/android/internal/util/BitUtils.java @@ -28,7 +28,7 @@ import java.util.function.IntFunction; /** * A utility class for handling unsigned integers and unsigned arithmetics, as well as syntactic - * sugar methods for ByteBuffer. Useful for networking and packet manipulations. + * sugar methods for {@link ByteBuffer}. Useful for networking and packet manipulations. * {@hide} */ public final class BitUtils { @@ -151,4 +151,11 @@ public final class BitUtils { TextUtils.wrap(builder, "[", "]"); return builder.toString(); } + + /** + * Converts long to byte array + */ + public static byte[] toBytes(long l) { + return ByteBuffer.allocate(8).putLong(l).array(); + } } diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java index 083c0c9736f9..151901be7b5b 100644 --- a/core/java/com/android/internal/util/CollectionUtils.java +++ b/core/java/com/android/internal/util/CollectionUtils.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; @@ -190,6 +191,13 @@ public class CollectionUtils { } /** + * Returns the size of the given map, or 0 if null + */ + public static int size(@Nullable Map<?, ?> cur) { + return cur != null ? cur.size() : 0; + } + + /** * Returns whether the given collection {@link Collection#isEmpty is empty} or {@code null} */ public static boolean isEmpty(@Nullable Collection<?> cur) { diff --git a/core/java/com/android/internal/util/function/NonaConsumer.java b/core/java/com/android/internal/util/function/NonaConsumer.java new file mode 100644 index 000000000000..3e7ce2b405a7 --- /dev/null +++ b/core/java/com/android/internal/util/function/NonaConsumer.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 9-argument {@link Consumer} + * + * @hide + */ +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/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/OctFunction.java index 27d25b86b859..cb16624725b7 100644 --- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl +++ b/core/java/com/android/internal/util/function/OctFunction.java @@ -13,12 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.hardware.biometrics; + +package com.android.internal.util.function; + +import java.util.function.Function; /** - * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient. + * A 8-argument {@link Function} + * * @hide */ -oneway interface IBiometricPromptReceiver { - void onDialogDismissed(int reason); +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/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index 849585004bc3..b97a9fa8d1cc 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -934,7 +934,7 @@ public class SystemConfig { // If the storage model feature flag is disabled, we need to fiddle // around with permission definitions to return us to pre-Q behavior. // STOPSHIP(b/112545973): remove once feature enabled by default - if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) { + if (!StorageManager.hasIsolatedStorage()) { if (newPermissions.contains(android.Manifest.permission.READ_MEDIA_AUDIO) || newPermissions.contains(android.Manifest.permission.READ_MEDIA_VIDEO) || newPermissions.contains(android.Manifest.permission.READ_MEDIA_IMAGES)) { 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_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_text_AndroidCharacter.cpp b/core/jni/android_text_AndroidCharacter.cpp index 8885aac49064..c6ea4e10f63e 100644 --- a/core/jni/android_text_AndroidCharacter.cpp +++ b/core/jni/android_text_AndroidCharacter.cpp @@ -25,9 +25,10 @@ #include "unicode/uchar.h" #define PROPERTY_UNDEFINED (-1) +#define JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY 18 // ICU => JDK mapping -static int directionality_map[U_CHAR_DIRECTION_COUNT] = { +static int directionality_map[JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY + 1] = { 0, // U_LEFT_TO_RIGHT (0) => DIRECTIONALITY_LEFT_TO_RIGHT (0) 1, // U_RIGHT_TO_LEFT (1) => DIRECTIONALITY_RIGHT_TO_LEFT (1) 3, // U_EUROPEAN_NUMBER (2) => DIRECTIONALITY_EUROPEAN_NUMBER (3) @@ -75,7 +76,8 @@ static void getDirectionalities(JNIEnv* env, jobject obj, jcharArray srcArray, int c = 0x00010000 + ((src[i] - 0xD800) << 10) + (src[i + 1] & 0x3FF); int dir = u_charDirection(c); - if (dir < 0 || dir >= U_CHAR_DIRECTION_COUNT) + if (dir < 0 || dir > JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY + || u_charType(c) == U_UNASSIGNED) dir = PROPERTY_UNDEFINED; else dir = directionality_map[dir]; @@ -85,7 +87,8 @@ static void getDirectionalities(JNIEnv* env, jobject obj, jcharArray srcArray, } else { int c = src[i]; int dir = u_charDirection(c); - if (dir < 0 || dir >= U_CHAR_DIRECTION_COUNT) + if (dir < 0 || dir > JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY + || u_charType(c) == U_UNASSIGNED) dest[i] = PROPERTY_UNDEFINED; else dest[i] = directionality_map[dir]; @@ -96,7 +99,7 @@ static void getDirectionalities(JNIEnv* env, jobject obj, jcharArray srcArray, static jint getEastAsianWidth(JNIEnv* env, jobject obj, jchar input) { int width = u_getIntPropertyValue(input, UCHAR_EAST_ASIAN_WIDTH); - if (width < 0 || width >= U_EA_COUNT) + if (width < 0 || width > u_getIntPropertyMaxValue(UCHAR_EAST_ASIAN_WIDTH)) width = PROPERTY_UNDEFINED; return width; @@ -121,6 +124,7 @@ static void getEastAsianWidths(JNIEnv* env, jobject obj, jcharArray srcArray, return; } + int maxWidth = u_getIntPropertyMaxValue(UCHAR_EAST_ASIAN_WIDTH); for (int i = 0; i < count; i++) { const int srci = start + i; if (src[srci] >= 0xD800 && src[srci] <= 0xDBFF && @@ -129,7 +133,7 @@ static void getEastAsianWidths(JNIEnv* env, jobject obj, jcharArray srcArray, int c = 0x00010000 + ((src[srci] - 0xD800) << 10) + (src[srci + 1] & 0x3FF); int width = u_getIntPropertyValue(c, UCHAR_EAST_ASIAN_WIDTH); - if (width < 0 || width >= U_EA_COUNT) + if (width < 0 || width > maxWidth) width = PROPERTY_UNDEFINED; dest[i++] = width; @@ -137,7 +141,7 @@ static void getEastAsianWidths(JNIEnv* env, jobject obj, jcharArray srcArray, } else { int c = src[srci]; int width = u_getIntPropertyValue(c, UCHAR_EAST_ASIAN_WIDTH); - if (width < 0 || width >= U_EA_COUNT) + if (width < 0 || width > maxWidth) width = PROPERTY_UNDEFINED; dest[i] = width; 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/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index ec9c8606e8df..ea6e0178bd9c 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -377,6 +377,14 @@ static void nativeSetWindowCrop(JNIEnv* env, jclass clazz, jlong transactionObj, transaction->setCrop_legacy(ctrl, crop); } +static void nativeSetCornerRadius(JNIEnv* env, jclass clazz, jlong transactionObj, + jlong nativeObject, jfloat cornerRadius) { + auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); + + SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); + transaction->setCornerRadius(ctrl, cornerRadius); +} + static void nativeSetLayerStack(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject, jint layerStack) { auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); @@ -883,6 +891,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetFlags }, {"nativeSetWindowCrop", "(JJIIII)V", (void*)nativeSetWindowCrop }, + {"nativeSetCornerRadius", "(JJF)V", + (void*)nativeSetCornerRadius }, {"nativeSetLayerStack", "(JJI)V", (void*)nativeSetLayerStack }, {"nativeGetBuiltInDisplay", "(I)Landroid/os/IBinder;", diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 6e661e1ce5b2..b465fb4f7eba 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -234,6 +234,14 @@ message SecureSettingsProto { } optional Location location = 31; + message LocationAccessCheck { + option (android.msg_privacy).dest = DEST_EXPLICIT; + + optional SettingProto interval_millis = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto delay_millis = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; + } + optional LocationAccessCheck location_access_check = 73; + message LockScreen { option (android.msg_privacy).dest = DEST_EXPLICIT; @@ -515,5 +523,5 @@ message SecureSettingsProto { // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 73; + // Next tag = 74; } diff --git a/core/proto/android/telephony/enums.proto b/core/proto/android/telephony/enums.proto index fba2e51937b3..477716982008 100644 --- a/core/proto/android/telephony/enums.proto +++ b/core/proto/android/telephony/enums.proto @@ -51,6 +51,7 @@ enum NetworkTypeEnum { NETWORK_TYPE_TD_SCDMA = 17; NETWORK_TYPE_IWLAN = 18; NETWORK_TYPE_LTE_CA = 19; + NETWORK_TYPE_NR = 20; } // Signal strength levels, primarily used by android/telephony/SignalStrength.java. diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 6c3085ff2919..b4d5f67e945e 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -627,6 +627,13 @@ <!-- ====================================================================== --> <eat-comment /> + <!-- Grouping for platform runtime permissions is not accessible to apps + @hide + @SystemApi + --> + <permission-group android:name="android.permission-group.UNDEFINED" + android:priority="100" /> + <!-- ====================================================================== --> <!-- Permissions for accessing user's contacts including personal profile --> <!-- ====================================================================== --> @@ -645,14 +652,17 @@ <p>Protection level: dangerous --> <permission android:name="android.permission.READ_CONTACTS" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_readContacts" android:description="@string/permdesc_readContacts" - android:protectionLevel="dangerous" /> + android:protectionLevel="dangerous" + android:usageInfoRequired="true" /> <!-- Allows an application to write the user's contacts data. <p>Protection level: dangerous --> <permission android:name="android.permission.WRITE_CONTACTS" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_writeContacts" android:description="@string/permdesc_writeContacts" android:protectionLevel="dangerous" /> @@ -674,14 +684,17 @@ <p>Protection level: dangerous --> <permission android:name="android.permission.READ_CALENDAR" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_readCalendar" android:description="@string/permdesc_readCalendar" - android:protectionLevel="dangerous" /> + android:protectionLevel="dangerous" + android:usageInfoRequired="true" /> <!-- Allows an application to write the user's calendar data. <p>Protection level: dangerous --> <permission android:name="android.permission.WRITE_CALENDAR" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_writeCalendar" android:description="@string/permdesc_writeCalendar" android:protectionLevel="dangerous" /> @@ -703,6 +716,7 @@ <p>Protection level: dangerous --> <permission android:name="android.permission.SEND_SMS" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_sendSms" android:description="@string/permdesc_sendSms" android:permissionFlags="costsMoney" @@ -712,33 +726,41 @@ <p>Protection level: dangerous --> <permission android:name="android.permission.RECEIVE_SMS" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_receiveSms" android:description="@string/permdesc_receiveSms" - android:protectionLevel="dangerous"/> + android:protectionLevel="dangerous" + android:usageInfoRequired="true" /> <!-- Allows an application to read SMS messages. <p>Protection level: dangerous --> <permission android:name="android.permission.READ_SMS" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_readSms" android:description="@string/permdesc_readSms" - android:protectionLevel="dangerous" /> + android:protectionLevel="dangerous" + android:usageInfoRequired="true" /> <!-- Allows an application to receive WAP push messages. <p>Protection level: dangerous --> <permission android:name="android.permission.RECEIVE_WAP_PUSH" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_receiveWapPush" android:description="@string/permdesc_receiveWapPush" - android:protectionLevel="dangerous" /> + android:protectionLevel="dangerous" + android:usageInfoRequired="true" /> <!-- Allows an application to monitor incoming MMS messages. <p>Protection level: dangerous --> <permission android:name="android.permission.RECEIVE_MMS" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_receiveMms" android:description="@string/permdesc_receiveMms" - android:protectionLevel="dangerous" /> + android:protectionLevel="dangerous" + android:usageInfoRequired="true" /> <!-- @SystemApi @TestApi Allows an application to read previously received cell broadcast messages and to register a content observer to get notifications when @@ -753,9 +775,11 @@ <p>Protection level: dangerous @hide Pending API council approval --> <permission android:name="android.permission.READ_CELL_BROADCASTS" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_readCellBroadcasts" android:description="@string/permdesc_readCellBroadcasts" - android:protectionLevel="dangerous" /> + android:protectionLevel="dangerous" + android:usageInfoRequired="true" /> <!-- ====================================================================== --> <!-- Permissions for accessing external storage --> @@ -794,6 +818,7 @@ @deprecated replaced by new strongly-typed permission groups in Q. --> <permission android:name="android.permission.READ_EXTERNAL_STORAGE" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_sdcardRead" android:description="@string/permdesc_sdcardRead" android:protectionLevel="dangerous" @@ -815,6 +840,7 @@ @deprecated replaced by new strongly-typed permission groups in Q. --> <permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_sdcardWrite" android:description="@string/permdesc_sdcardWrite" android:protectionLevel="dangerous" @@ -831,9 +857,11 @@ <!-- Allows an application to read the user's shared audio collection. --> <permission android:name="android.permission.READ_MEDIA_AUDIO" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_audioRead" android:description="@string/permdesc_audioRead" - android:protectionLevel="dangerous" /> + android:protectionLevel="dangerous" + android:usageInfoRequired="true" /> <!-- Runtime permission controlling access to the user's shared visual media collection, including images and videos. --> @@ -846,22 +874,28 @@ <!-- Allows an application to read the user's shared images collection. --> <permission android:name="android.permission.READ_MEDIA_IMAGES" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_imagesRead" android:description="@string/permdesc_imagesRead" - android:protectionLevel="dangerous" /> + android:protectionLevel="dangerous" + android:usageInfoRequired="true" /> <!-- Allows an application to read the user's shared video collection. --> <permission android:name="android.permission.READ_MEDIA_VIDEO" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_videoRead" android:description="@string/permdesc_videoRead" - android:protectionLevel="dangerous" /> + android:protectionLevel="dangerous" + android:usageInfoRequired="true" /> <!-- Allows an application to access any geographic locations persisted in the user's shared collection. --> <permission android:name="android.permission.ACCESS_MEDIA_LOCATION" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_mediaLocation" android:description="@string/permdesc_mediaLocation" - android:protectionLevel="dangerous" /> + android:protectionLevel="dangerous" + android:usageInfoRequired="true" /> <!-- @hide @SystemApi Allows an application to modify OBB files visible to other apps. --> @@ -889,20 +923,24 @@ <p>Protection level: dangerous --> <permission android:name="android.permission.ACCESS_FINE_LOCATION" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_accessFineLocation" android:description="@string/permdesc_accessFineLocation" android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION" - android:protectionLevel="dangerous|instant" /> + android:protectionLevel="dangerous|instant" + android:usageInfoRequired="true" /> <!-- Allows an app to access approximate location. Alternatively, you might want {@link #ACCESS_FINE_LOCATION}. <p>Protection level: dangerous --> <permission android:name="android.permission.ACCESS_COARSE_LOCATION" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_accessCoarseLocation" android:description="@string/permdesc_accessCoarseLocation" android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION" - android:protectionLevel="dangerous|instant" /> + android:protectionLevel="dangerous|instant" + android:usageInfoRequired="true" /> <!-- Allows an app to access location in the background. If you are requesting this, you should also request {@link #ACCESS_FINE_LOCATION}. @@ -911,9 +949,11 @@ <p>Protection level: dangerous --> <permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_accessBackgroundLocation" android:description="@string/permdesc_accessBackgroundLocation" - android:protectionLevel="dangerous|instant" /> + android:protectionLevel="dangerous|instant" + android:usageInfoRequired="true" /> <!-- ====================================================================== --> <!-- Permissions for accessing the call log --> @@ -951,9 +991,11 @@ <p>Protection level: dangerous --> <permission android:name="android.permission.READ_CALL_LOG" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_readCallLog" android:description="@string/permdesc_readCallLog" - android:protectionLevel="dangerous" /> + android:protectionLevel="dangerous" + android:usageInfoRequired="true" /> <!-- Allows an application to write (but not read) the user's call log data. @@ -969,6 +1011,7 @@ <p>Protection level: dangerous --> <permission android:name="android.permission.WRITE_CALL_LOG" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_writeCallLog" android:description="@string/permdesc_writeCallLog" android:protectionLevel="dangerous" /> @@ -979,9 +1022,11 @@ <p>Protection level: dangerous --> <permission android:name="android.permission.PROCESS_OUTGOING_CALLS" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_processOutgoingCalls" android:description="@string/permdesc_processOutgoingCalls" - android:protectionLevel="dangerous" /> + android:protectionLevel="dangerous" + android:usageInfoRequired="true" /> <!-- ====================================================================== --> <!-- Permissions for accessing the device telephony --> @@ -1010,23 +1055,28 @@ <p>Protection level: dangerous --> <permission android:name="android.permission.READ_PHONE_STATE" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_readPhoneState" android:description="@string/permdesc_readPhoneState" - android:protectionLevel="dangerous" /> + android:protectionLevel="dangerous" + android:usageInfoRequired="true" /> <!-- Allows read access to the device's phone number(s). This is a subset of the capabilities granted by {@link #READ_PHONE_STATE} but is exposed to instant applications. <p>Protection level: dangerous--> <permission android:name="android.permission.READ_PHONE_NUMBERS" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_readPhoneNumbers" android:description="@string/permdesc_readPhoneNumbers" - android:protectionLevel="dangerous|instant" /> + android:protectionLevel="dangerous|instant" + android:usageInfoRequired="true" /> <!-- Allows an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call. <p>Protection level: dangerous --> <permission android:name="android.permission.CALL_PHONE" + android:permissionGroup="android.permission-group.UNDEFINED" android:permissionFlags="costsMoney" android:label="@string/permlab_callPhone" android:description="@string/permdesc_callPhone" @@ -1036,6 +1086,7 @@ <p>Protection level: dangerous --> <permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_addVoicemail" android:description="@string/permdesc_addVoicemail" android:protectionLevel="dangerous" /> @@ -1044,6 +1095,7 @@ <p>Protection level: dangerous --> <permission android:name="android.permission.USE_SIP" + android:permissionGroup="android.permission-group.UNDEFINED" android:description="@string/permdesc_use_sip" android:label="@string/permlab_use_sip" android:protectionLevel="dangerous"/> @@ -1052,6 +1104,7 @@ <p>Protection level: dangerous --> <permission android:name="android.permission.ANSWER_PHONE_CALLS" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_answerPhoneCalls" android:description="@string/permdesc_answerPhoneCalls" android:protectionLevel="dangerous|runtime" /> @@ -1079,6 +1132,7 @@ <p>Protection level: dangerous --> <permission android:name="android.permission.ACCEPT_HANDOVER" + android:permissionGroup="android.permission-group.UNDEFINED" android.label="@string/permlab_acceptHandover" android:description="@string/permdesc_acceptHandovers" android:protectionLevel="dangerous" /> @@ -1102,9 +1156,11 @@ <p>Protection level: dangerous --> <permission android:name="android.permission.RECORD_AUDIO" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_recordAudio" android:description="@string/permdesc_recordAudio" - android:protectionLevel="dangerous|instant"/> + android:protectionLevel="dangerous|instant" + android:usageInfoRequired="true" /> <!-- ====================================================================== --> <!-- Permissions for activity recognition --> @@ -1123,9 +1179,11 @@ <p>Protection level: dangerous --> <permission android:name="android.permission.ACTIVITY_RECOGNITION" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_activityRecognition" android:description="@string/permdesc_activityRecognition" - android:protectionLevel="dangerous|instant" /> + android:protectionLevel="dangerous|instant" + android:usageInfoRequired="true" /> <!-- ====================================================================== --> <!-- Permissions for accessing the UCE Service --> @@ -1171,9 +1229,11 @@ <p>Protection level: dangerous --> <permission android:name="android.permission.CAMERA" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_camera" android:description="@string/permdesc_camera" - android:protectionLevel="dangerous|instant" /> + android:protectionLevel="dangerous|instant" + android:usageInfoRequired="true" /> <!-- ====================================================================== --> @@ -1194,9 +1254,11 @@ measure what is happening inside his/her body, such as heart rate. <p>Protection level: dangerous --> <permission android:name="android.permission.BODY_SENSORS" + android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_bodySensors" android:description="@string/permdesc_bodySensors" - android:protectionLevel="dangerous" /> + android:protectionLevel="dangerous" + android:usageInfoRequired="true" /> <!-- Allows an app to use fingerprint hardware. <p>Protection level: normal @@ -1680,9 +1742,11 @@ <p>Protection level: dangerous --> <permission android:name="android.permission.GET_ACCOUNTS" + android:permissionGroup="android.permission-group.UNDEFINED" android:protectionLevel="dangerous" android:description="@string/permdesc_getAccounts" - android:label="@string/permlab_getAccounts" /> + android:label="@string/permlab_getAccounts" + android:usageInfoRequired="true" /> <uses-permission android:name="android.permission.GET_ACCOUNTS"/> <!-- @SystemApi Allows applications to call into AccountAuthenticators. @@ -4206,6 +4270,12 @@ <permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS" android:protectionLevel="signature|appop" /> + <!-- Required for apps targeting {@link android.os.Build.VERSION_CODES#P} that want to use + {@link android.app.Notification.Builder#setFullScreenIntent notification full screen + intents}. --> + <permission android:name="android.permission.USE_FULL_SCREEN_INTENT" + android:protectionLevel="normal" /> + <!-- @SystemApi Allows requesting the framework broadcast the {@link Intent#ACTION_DEVICE_CUSTOMIZATION_READY} intent. @hide --> @@ -4217,6 +4287,10 @@ @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" diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index 433ae399c88c..4bf1ad6651ee 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -126,7 +126,6 @@ android:visibility="gone" android:contentDescription="@string/notification_alerted_content_description" android:src="@drawable/ic_notifications_alerted" - android:tint="@color/notification_secondary_text_color_light" /> <ImageView android:id="@+id/profile_badge" diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 470e1ccf36e8..244728954aa0 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -5532,7 +5532,7 @@ <attr name="solidColor" format="color|reference" /> <!-- @hide The divider for making the selection area. --> <attr name="selectionDivider" format="reference" /> - <!-- @hide The height of the selection divider. --> + <!-- The height of the selection divider. --> <attr name="selectionDividerHeight" format="dimension" /> <!-- @hide The distance between the two selection dividers. --> <attr name="selectionDividersDistance" format="dimension" /> @@ -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. --> 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/dimens.xml b/core/res/res/values/dimens.xml index f7b9961c39e8..05a156b69371 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -430,7 +430,7 @@ <dimen name="notification_badge_size">12dp</dimen> <!-- Size of the alerted icon for notifications --> - <dimen name="notification_alerted_size">18dp</dimen> + <dimen name="notification_alerted_size">12dp</dimen> <!-- Keyguard dimensions --> <!-- TEMP --> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 63cac5172f8a..5e8af62d1da9 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,12 @@ <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 name="selectionDividerHeight" /> </public-group> <public-group type="drawable" first-id="0x010800b4"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index bd6d97622a4d..a33f6b2fbf9c 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1444,9 +1444,14 @@ <!-- Title shown when the system-provided biometric dialog is shown, asking the user to authenticate. [CHAR LIMIT=40] --> <string name="biometric_dialog_default_title">Application <xliff:g id="app" example="Gmail">%s</xliff:g> wants to authenticate.</string> - <!-- Message shown when biometric hardware is not available [CHAR LIMIT=50] --> <string name="biometric_error_hw_unavailable">Biometric hardware unavailable</string> + <!-- Message shown when biometric authentication was canceled by the user [CHAR LIMIT=50] --> + <string name="biometric_error_user_canceled">Authentication canceled</string> + <!-- Message shown by the biometric dialog when biometric is not recognized --> + <string name="biometric_not_recognized">Not recognized</string> + <!-- Message shown when biometric authentication has been canceled [CHAR LIMIT=50] --> + <string name="biometric_error_canceled">Authentication canceled</string> <!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized --> <string name="fingerprint_acquired_partial">Partial fingerprint detected. Please try again.</string> @@ -1462,8 +1467,6 @@ <string-array name="fingerprint_acquired_vendor"> </string-array> - <!-- Message shown by the biometric dialog when biometric is not recognized --> - <string name="biometric_not_recognized">Not recognized</string> <!-- Accessibility message announced when a fingerprint has been authenticated [CHAR LIMIT=NONE] --> <string name="fingerprint_authenticated">Fingerprint authenticated</string> <!-- Accessibility message announced when a face has been authenticated [CHAR LIMIT=NONE] --> @@ -1585,14 +1588,14 @@ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_readSyncStats">Allows an app to read the sync stats for an account, including the history of sync events and how much data is synced. </string> - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] --> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] --> <string name="permlab_sdcardRead">read the contents of your shared storage</string> - <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] --> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] --> <string name="permdesc_sdcardRead">Allows the app to read the contents of your shared storage.</string> - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] --> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] --> <string name="permlab_sdcardWrite">modify or delete the contents of your shared storage</string> - <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] --> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] --> <string name="permdesc_sdcardWrite">Allows the app to write the contents of your shared storage.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> @@ -2983,7 +2986,7 @@ <!-- Label for item in the text selection menu to translate selected text with a translation app. Should be a verb. [CHAR LIMIT=30] --> <string name="translate">Translate</string> - + <!-- Accessibility description for an item in the text selection menu to translate selected text with a translation app. [CHAR LIMIT=NONE] --> <string name="translate_desc">Translate selected text</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e251e27a5496..783f1f355e87 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2406,7 +2406,9 @@ <!-- Biometric messages --> <java-symbol type="string" name="biometric_dialog_default_title" /> <java-symbol type="string" name="biometric_error_hw_unavailable" /> + <java-symbol type="string" name="biometric_error_user_canceled" /> <java-symbol type="string" name="biometric_not_recognized" /> + <java-symbol type="string" name="biometric_error_canceled" /> <!-- Fingerprint messages --> <java-symbol type="string" name="fingerprint_error_unable_to_process" /> @@ -3499,4 +3501,8 @@ <java-symbol type="string" name="config_defaultAssistantComponentName" /> <java-symbol type="id" name="transition_overlay_view_tag" /> + + <java-symbol type="dimen" name="rounded_corner_radius" /> + <java-symbol type="dimen" name="rounded_corner_radius_top" /> + <java-symbol type="dimen" name="rounded_corner_radius_bottom" /> </resources> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 305d2af2122d..a8f9e8a5891b 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -664,7 +664,9 @@ public class SettingsBackupTest { Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE, Settings.Secure.FLASHLIGHT_AVAILABLE, Settings.Secure.FLASHLIGHT_ENABLED, - Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED); + Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, + Settings.Secure.LOCATION_ACCESS_CHECK_INTERVAL_MILLIS, + Settings.Secure.LOCATION_ACCESS_CHECK_DELAY_MILLIS); @Test public void systemSettingsBackedUpOrBlacklisted() { diff --git a/core/tests/coretests/src/android/text/AndroidCharacterTest.java b/core/tests/coretests/src/android/text/AndroidCharacterTest.java new file mode 100644 index 000000000000..0c7e730e78e4 --- /dev/null +++ b/core/tests/coretests/src/android/text/AndroidCharacterTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS,d + * WITHOUT 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.text; + +import static org.junit.Assert.assertArrayEquals; + +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.SmallTest; + +import org.junit.Test; + +@Presubmit +@SmallTest +public class AndroidCharacterTest { + + @Test + public void testGetDirectionalities_nonSupplementaryCharacters() { + int size = Character.MAX_VALUE + 1 + - (Character.MAX_SURROGATE - Character.MIN_SURROGATE + 1); + char[] chars = new char[size]; + byte[] java_lang_results = new byte[size]; + int index = 0; + for (int cp = 0; cp <= Character.MAX_VALUE; cp++) { + if (cp < Character.MIN_SURROGATE || cp > Character.MAX_SURROGATE) { + chars[index] = (char) cp; + java_lang_results[index] = Character.getDirectionality(cp); + index++; + } + } + + byte[] android_text_results = new byte[size]; + AndroidCharacter.getDirectionalities(chars, android_text_results, index); + assertArrayEquals(java_lang_results, android_text_results); + } + + @Test + public void testGetDirectionalities_supplementaryCharacters() { + int maxNumberOfChars = Character.MAX_CODE_POINT - Character.MIN_SUPPLEMENTARY_CODE_POINT + + 1; + int size = maxNumberOfChars * 2; + char[] chars = new char[size]; + byte[] java_lang_results = new byte[size]; + int index = 0; + for (int cp = Character.MIN_SUPPLEMENTARY_CODE_POINT; cp <= Character.MAX_CODE_POINT; + cp++) { + chars[index] = Character.highSurrogate(cp); + chars[index + 1] = Character.lowSurrogate(cp); + java_lang_results[index] = java_lang_results[index + 1] = Character + .getDirectionality(cp); + index += 2; + } + + byte[] android_text_results = new byte[size]; + AndroidCharacter.getDirectionalities(chars, android_text_results, index); + assertArrayEquals(java_lang_results, android_text_results); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java index 30309cf64e96..8691e73f82fb 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java @@ -557,6 +557,62 @@ public class BinderCallsStatsTest { assertEquals(0, bcs.getExceptionCounts().size()); } + @Test + public void testOverflow_sameEntry() { + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setDetailedTracking(true); + bcs.setSamplingInterval(1); + bcs.setMaxBinderCallStats(2); + + Binder binder = new Binder(); + CallSession callSession = bcs.callStarted(binder, 1); + bcs.time += 10; + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + callSession = bcs.callStarted(binder, 1); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + callSession = bcs.callStarted(binder, 1); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + BinderCallsStats.UidEntry uidEntry = bcs.getUidEntries().get(WORKSOURCE_UID); + List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList()); + assertEquals(1, callStatsList.size()); + BinderCallsStats.CallStat callStats = callStatsList.get(0); + assertEquals(3, callStats.callCount); + } + + @Test + public void testOverflow_overflowEntry() { + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setDetailedTracking(true); + bcs.setSamplingInterval(1); + bcs.setMaxBinderCallStats(1); + + Binder binder = new Binder(); + CallSession callSession = bcs.callStarted(binder, 1); + bcs.time += 10; + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + callSession = bcs.callStarted(binder, 2); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + List<BinderCallsStats.ExportedCallStat> callStatsList = bcs.getExportedCallStats(); + assertEquals(2, callStatsList.size()); + BinderCallsStats.ExportedCallStat callStats = callStatsList.get(0); + assertEquals(1, callStats.callCount); + assertEquals("1", callStats.methodName); + assertEquals("android.os.Binder", callStats.className); + assertEquals(CALLING_UID, callStats.callingUid); + + callStats = callStatsList.get(1); + assertEquals(1, callStats.callCount); + assertEquals("-1", callStats.methodName); + assertEquals("com.android.internal.os.BinderCallsStats$OverflowBinder", + callStats.className); + assertEquals(CALLING_UID, callStats.callingUid); + } + class TestBinderCallsStats extends BinderCallsStats { public int callingUid = CALLING_UID; public int workSourceUid = WORKSOURCE_UID; diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java index 385bad5ab3f9..b242a34cc703 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java @@ -39,9 +39,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Comparator; -import java.util.List; import java.util.function.Predicate; -import java.util.stream.Collectors; @SmallTest @RunWith(AndroidJUnit4.class) @@ -58,8 +56,8 @@ public class KernelCpuThreadReaderTest { 1000, 2000, 3000, 4000, }; private static final int[][] THREAD_CPU_TIMES = { - {100, 0, 0, 100}, - {0, 0, 9999999, 0}, + {1, 0, 0, 1}, + {0, 0, 0, 0}, {1000, 1000, 1000, 1000}, {0, 1, 2, 3}, }; @@ -110,42 +108,6 @@ public class KernelCpuThreadReaderTest { } @Test - public void testReader_filtersLowTotalCpuUsage() throws IOException { - KernelCpuThreadReader.Injector processUtils = - new KernelCpuThreadReader.Injector() { - @Override - public int myPid() { - return PROCESS_ID; - } - - @Override - public int myUid() { - return UID; - } - - @Override - public int getUidForPid(int pid) { - return 0; - } - }; - setupDirectory(mProcDirectory.toPath().resolve("self"), new int[]{1, 2}, PROCESS_NAME, - THREAD_NAMES, new int[]{1000, 2000}, new int[][]{{0, 1}, {100, 0}}); - - final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader( - mProcDirectory.toPath(), - mProcDirectory.toPath().resolve("self/task/1/time_in_state"), - processUtils); - final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = - kernelCpuThreadReader.getCurrentProcessCpuUsage(); - - List<Integer> threadIds = processCpuUsage.threadCpuUsages.stream() - .map(t -> t.threadId) - .collect(Collectors.toList()); - assertEquals(1, threadIds.size()); - assertEquals(2, (long) threadIds.get(0)); - } - - @Test public void testReader_byUids() throws IOException { int[] uids = new int[]{0, 2, 3, 4, 5, 6000}; Predicate<Integer> uidPredicate = uid -> uid == 0 || uid >= 4; @@ -172,7 +134,7 @@ public class KernelCpuThreadReaderTest { setupDirectory(mProcDirectory.toPath().resolve(String.valueOf(uid)), new int[]{uid * 10}, "process" + uid, new String[]{"thread" + uid}, new int[]{1000}, - new int[][]{{uid + 100}}); + new int[][]{{uid}}); } final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader( mProcDirectory.toPath(), @@ -189,7 +151,7 @@ public class KernelCpuThreadReaderTest { int uid = expectedUids[i]; checkResults(processCpuUsage, kernelCpuThreadReader.getCpuFrequenciesKhz(), uid, uid, new int[]{uid * 10}, "process" + uid, new String[]{"thread" + uid}, - new int[]{1000}, new int[][]{{uid + 100}}); + new int[]{1000}, new int[][]{{uid}}); } } 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/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index 2e1d81a294e9..bf114b969b67 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -1779,6 +1779,40 @@ public abstract class ColorSpace { } /** + * <p>Computes the chromatic adaptation transform from the specified + * source white point to the specified destination white point.</p> + * + * <p>The transform is computed using the von Kries method, described + * in more details in the documentation of {@link Adaptation}. The + * {@link Adaptation} enum provides different matrices that can be + * used to perform the adaptation.</p> + * + * @param adaptation The adaptation method + * @param srcWhitePoint The white point to adapt from + * @param dstWhitePoint The white point to adapt to + * @return A 3x3 matrix as a non-null array of 9 floats + */ + @NonNull + @Size(9) + public static float[] chromaticAdaptation(@NonNull Adaptation adaptation, + @NonNull @Size(min = 2, max = 3) float[] srcWhitePoint, + @NonNull @Size(min = 2, max = 3) float[] dstWhitePoint) { + float[] srcXyz = srcWhitePoint.length == 3 ? + Arrays.copyOf(srcWhitePoint, 3) : xyYToXyz(srcWhitePoint); + float[] dstXyz = dstWhitePoint.length == 3 ? + Arrays.copyOf(dstWhitePoint, 3) : xyYToXyz(dstWhitePoint); + + if (compare(srcXyz, dstXyz)) { + return new float[] { + 1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 1.0f + }; + } + return chromaticAdaptation(adaptation.mTransform, srcXyz, dstXyz); + } + + /** * Implementation of the CIE XYZ color space. Assumes the white point is D50. */ @AnyThread 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..dbe6e8f4eebb 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -163,9 +163,6 @@ public class Typeface { private int[] mSupportedAxes; private static final int[] EMPTY_AXES = {}; - // The underlying font families. - private final FontFamily[] mFamilies; - @UnsupportedAppUsage private static void setDefault(Typeface t) { sDefaultTypeface = t; @@ -655,10 +652,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; } @@ -731,21 +729,17 @@ public class Typeface { public Typeface build() { final int userFallbackSize = mFamilies.size(); final FontFamily[] fallback = SystemFonts.getSystemFallback(mFallbackName); - final FontFamily[] fullFamilies = new FontFamily[fallback.length + userFallbackSize]; final long[] ptrArray = new long[fallback.length + userFallbackSize]; for (int i = 0; i < userFallbackSize; ++i) { ptrArray[i] = mFamilies.get(i).getNativePtr(); - fullFamilies[i] = mFamilies.get(i); } for (int i = 0; i < fallback.length; ++i) { ptrArray[i + userFallbackSize] = fallback[i].getNativePtr(); - fullFamilies[i + userFallbackSize] = fallback[i]; } final int weight = mStyle == null ? 400 : mStyle.getWeight(); final int italic = (mStyle == null || mStyle.getSlant() == FontStyle.FONT_SLANT_UPRIGHT) ? 0 : 1; - - return new Typeface(nativeCreateFromArray(ptrArray, weight, italic), fullFamilies); + return new Typeface(nativeCreateFromArray(ptrArray, weight, italic)); } } @@ -810,7 +804,7 @@ public class Typeface { } } - typeface = new Typeface(nativeCreateFromTypeface(ni, style), family.mFamilies); + typeface = new Typeface(nativeCreateFromTypeface(ni, style)); styles.put(style, typeface); } return typeface; @@ -878,8 +872,7 @@ public class Typeface { } typeface = new Typeface( - nativeCreateFromTypefaceWithExactStyle( - base.native_instance, weight, italic), base.mFamilies); + nativeCreateFromTypefaceWithExactStyle(base.native_instance, weight, italic)); innerCache.put(key, typeface); } return typeface; @@ -889,8 +882,7 @@ public class Typeface { public static Typeface createFromTypefaceWithVariation(@Nullable Typeface family, @NonNull List<FontVariationAxis> axes) { final Typeface base = family == null ? Typeface.DEFAULT : family; - return new Typeface(nativeCreateFromTypefaceWithVariation(base.native_instance, axes), - base.mFamilies); + return new Typeface(nativeCreateFromTypefaceWithVariation(base.native_instance, axes)); } /** @@ -996,7 +988,7 @@ public class Typeface { ptrArray[i] = families[i].getNativePtr(); } return new Typeface(nativeCreateFromArray(ptrArray, - RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE), families); + RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); } /** @@ -1044,19 +1036,6 @@ public class Typeface { } native_instance = ni; - mFamilies = new FontFamily[0]; - sRegistry.registerNativeAllocation(this, native_instance); - mStyle = nativeGetStyle(ni); - mWeight = nativeGetWeight(ni); - } - - private Typeface(long ni, @NonNull FontFamily[] families) { - if (ni == 0) { - throw new IllegalStateException("native typeface cannot be made"); - } - - native_instance = ni; - mFamilies = families; sRegistry.registerNativeAllocation(this, native_instance); mStyle = nativeGetStyle(ni); mWeight = nativeGetWeight(ni); @@ -1083,8 +1062,7 @@ public class Typeface { final Typeface base = systemFontMap.get(alias.getToName()); final int weight = alias.getWeight(); final Typeface newFace = weight == 400 ? base : - new Typeface(nativeCreateWeightAlias(base.native_instance, weight), - base.mFamilies); + new Typeface(nativeCreateWeightAlias(base.native_instance, weight)); systemFontMap.put(alias.getName(), newFace); } } 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/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/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index d84ec8508ee4..9a6df75fedd9 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -263,6 +263,15 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe tailPNext = &blend->pNext; } + VkPhysicalDeviceSamplerYcbcrConversionFeatures* ycbcrFeature; + ycbcrFeature = (VkPhysicalDeviceSamplerYcbcrConversionFeatures*) malloc( + sizeof(VkPhysicalDeviceSamplerYcbcrConversionFeatures)); + LOG_ALWAYS_FATAL_IF(!ycbcrFeature); + ycbcrFeature->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES; + ycbcrFeature->pNext = nullptr; + *tailPNext = ycbcrFeature; + tailPNext = &ycbcrFeature->pNext; + // query to get the physical device features mGetPhysicalDeviceFeatures2(mPhysicalDevice, &features); // this looks like it would slow things down, diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index ff2fad443bcb..ae87998a1615 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -121,18 +121,4 @@ interface ILocationManager // used by gts tests to verify throttling whitelist String[] getBackgroundThrottlingWhitelist(); - - /** - * Allow the {@link android.location.LocationManager#getNetworkProviderPackage location - * provider} to start the UI to modify the location permission for a package. - * - * <p>Can only be called by the location provider. - * - * @param packageName The package the permission belongs to - * @param permission The (individual) permission to switch - * - * @return A pending intent that starts the permission management UI or {@code null} if the - * intent cannot be created - */ - PendingIntent createManageLocationPermissionIntent(in String packageName, in String permission); } diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index b66ceef29ceb..3bf98b352b40 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -22,7 +22,6 @@ import static android.Manifest.permission.LOCATION_HARDWARE; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; import android.Manifest; -import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; @@ -2401,29 +2400,4 @@ public class LocationManager { return null; } } - - /** - * Allow the {@link android.location.LocationManager#getNetworkProviderPackage location - * provider} to start the UI to modify the location permission for a package. - * - * <p>Can only be called by the location provider. - * - * @param packageName The package the permission belongs to - * @param permission The (individual) location permission to switch - * - * @return A one-shot pending intent that starts the permission management UI or {@code null} if - * the intent cannot be created - * - * @hide - */ - @SystemApi - public @Nullable PendingIntent createManageLocationPermissionIntent(@NonNull String packageName, - @NonNull String permission) { - try { - return mService.createManageLocationPermissionIntent(packageName, permission); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - return null; - } - } } 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/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java index b047f8de515d..111dd0f0b63e 100644 --- a/media/java/android/media/MediaPlayer2.java +++ b/media/java/android/media/MediaPlayer2.java @@ -29,6 +29,7 @@ import android.content.Context; import android.content.res.AssetFileDescriptor; import android.graphics.Rect; import android.graphics.SurfaceTexture; +import android.media.MediaPlayer2.DrmInfo; import android.media.MediaPlayer2Proto.PlayerMessage; import android.media.MediaPlayer2Proto.Value; import android.net.Uri; @@ -72,7 +73,11 @@ import java.util.Map; import java.util.Queue; import java.util.UUID; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -300,21 +305,7 @@ public class MediaPlayer2 implements AutoCloseable private volatile float mVolume = 1.0f; private VideoSize mVideoSize = new VideoSize(0, 0); - // TODO: create per-source drm fields in SourceInfo - // Modular DRM - private final Object mDrmLock = new Object(); - //--- guarded by |mDrmLock| start - private UUID mDrmUUID; - private DrmInfo mDrmInfo; - private MediaDrm mDrmObj; - private byte[] mDrmSessionId; - private boolean mDrmInfoResolved; - private boolean mActiveDrmScheme; - private boolean mDrmConfigAllowed; - private boolean mDrmProvisioningInProgress; - private boolean mPrepareDrmInProgress; - private ProvisioningThread mDrmProvisioningThread; - //--- guarded by |mDrmLock| end + private ExecutorService mDrmThreadPool = Executors.newCachedThreadPool(); // Creating a dummy audio track, used for keeping session id alive private final Object mSessionIdLock = new Object(); @@ -328,6 +319,7 @@ public class MediaPlayer2 implements AutoCloseable private final List<Task> mPendingTasks = new LinkedList<>(); @GuardedBy("mTaskLock") private Task mCurrentTask; + private final AtomicLong mTaskIdGenerator = new AtomicLong(0); @GuardedBy("mTaskLock") boolean mIsPreviousCommandSeekTo = false; @@ -413,15 +405,13 @@ public class MediaPlayer2 implements AutoCloseable mHandlerThread = null; } - setCurrentSourceInfo(null); - clearNextSourceInfos(); + clearSourceInfos(); // Modular DRM clean up mOnDrmConfigHelper = null; synchronized (mDrmEventCbLock) { mDrmEventCallbackRecords.clear(); } - resetDrmState(); native_release(); @@ -461,13 +451,8 @@ public class MediaPlayer2 implements AutoCloseable synchronized (mDrmEventCbLock) { mDrmEventCallbackRecords.clear(); } - setCurrentSourceInfo(null); - clearNextSourceInfos(); - synchronized (mTaskLock) { - mPendingTasks.clear(); - mIsPreviousCommandSeekTo = false; - } + clearSourceInfos(); stayAwake(false); native_reset(); @@ -481,7 +466,6 @@ public class MediaPlayer2 implements AutoCloseable mTaskHandler.removeCallbacksAndMessages(null); } - resetDrmState(); } private native void native_reset(); @@ -706,13 +690,14 @@ public class MediaPlayer2 implements AutoCloseable } synchronized (mSrcLock) { - setCurrentSourceInfo(new SourceInfo(dsd)); + setCurrentSourceInfo_l(new SourceInfo(dsd)); handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId); } } finally { dsd.close(); } } + }); } @@ -732,7 +717,7 @@ public class MediaPlayer2 implements AutoCloseable void process() { Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null"); synchronized (mSrcLock) { - clearNextSourceInfos(); + clearNextSourceInfos_l(); mNextSourceInfos.add(new SourceInfo(dsd)); } prepareNextDataSource(); @@ -758,7 +743,7 @@ public class MediaPlayer2 implements AutoCloseable } synchronized (mSrcLock) { - clearNextSourceInfos(); + clearNextSourceInfos_l(); for (DataSourceDesc dsd : dsds) { if (dsd != null) { mNextSourceInfos.add(new SourceInfo(dsd)); @@ -781,7 +766,9 @@ public class MediaPlayer2 implements AutoCloseable return addTask(new Task(CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES, false) { @Override void process() { - clearNextSourceInfos(); + synchronized (mSrcLock) { + clearNextSourceInfos_l(); + } } }); } @@ -1073,7 +1060,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. - setCurrentSourceInfo(mNextSourceInfos.poll()); + setCurrentSourceInfo_l(mNextSourceInfos.poll()); long srcId = mCurrentSourceInfo.mId; try { @@ -2173,7 +2160,7 @@ public class MediaPlayer2 implements AutoCloseable final int what = msg.arg1; final int extra = msg.arg2; - final SourceInfo sourceInfo = getSourceInfoById(srcId); + final SourceInfo sourceInfo = getSourceInfo(srcId); if (sourceInfo == null) { return; } @@ -2227,11 +2214,11 @@ public class MediaPlayer2 implements AutoCloseable Log.w(TAG, "MEDIA_DRM_INFO msg.obj=NULL"); } else if (msg.obj instanceof byte[]) { // The PlayerMessage was parsed already in postEventFromNative - final DrmInfo drmInfo; - synchronized (mDrmLock) { - if (mDrmInfo != null) { - drmInfo = mDrmInfo.makeCopy(); + final DrmInfo drmInfo; + synchronized (sourceInfo) { + if (sourceInfo.mDrmInfo != null) { + drmInfo = sourceInfo.mDrmInfo.makeCopy(); } else { drmInfo = null; } @@ -2303,7 +2290,7 @@ public class MediaPlayer2 implements AutoCloseable } }); - SourceInfo src = getSourceInfoById(srcId); + SourceInfo src = getSourceInfo(srcId); if (src != null) { src.mBufferedPercentage.set(percent); } @@ -2504,6 +2491,7 @@ public class MediaPlayer2 implements AutoCloseable return; } + final SourceInfo sourceInfo = mp.getSourceInfo(srcId); switch (what) { case MEDIA_DRM_INFO: // We need to derive mDrmInfo before prepare() returns so processing it here @@ -2511,7 +2499,7 @@ public class MediaPlayer2 implements AutoCloseable // notification looper so its handleMessage might process the event after prepare() // has returned. Log.v(TAG, "postEventFromNative MEDIA_DRM_INFO"); - if (obj != null) { + if (obj != null && sourceInfo != null) { PlayerMessage playerMsg; try { playerMsg = PlayerMessage.parseFrom(obj); @@ -2520,11 +2508,12 @@ public class MediaPlayer2 implements AutoCloseable break; } DrmInfo drmInfo = new DrmInfo(playerMsg); - synchronized (mp.mDrmLock) { - mp.mDrmInfo = drmInfo; + synchronized (sourceInfo) { + sourceInfo.mDrmInfo = drmInfo; } } else { - Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + obj); + Log.w(TAG, "MEDIA_DRM_INFO sourceInfo " + sourceInfo + + " msg.obj of unexpected type " + obj); } break; @@ -2533,8 +2522,10 @@ public class MediaPlayer2 implements AutoCloseable // mainly for prepare() use case. For prepare(), this still can run to a race // condition b/c MediaPlayerNative releases the prepare() lock before calling notify // so we also set mDrmInfoResolved in prepare(). - synchronized (mp.mDrmLock) { - mp.mDrmInfoResolved = true; + if (sourceInfo != null) { + synchronized (sourceInfo) { + sourceInfo.mDrmInfoResolved = true; + } } break; } @@ -3211,9 +3202,7 @@ public class MediaPlayer2 implements AutoCloseable */ // This is a synchronous call. public void setOnDrmConfigHelper(OnDrmConfigHelper listener) { - synchronized (mDrmLock) { - mOnDrmConfigHelper = listener; - } + mOnDrmConfigHelper = listener; } private OnDrmConfigHelper mOnDrmConfigHelper; @@ -3358,24 +3347,27 @@ public class MediaPlayer2 implements AutoCloseable * @throws IllegalStateException if called before being prepared */ public DrmInfo getDrmInfo(@NonNull DataSourceDesc dsd) { - // TODO: this implementation only works when dsd is the only data source - DrmInfo drmInfo = null; - - // there is not much point if the app calls getDrmInfo within an OnDrmInfoListenet; - // regardless below returns drmInfo anyway instead of raising an exception - synchronized (mDrmLock) { - if (!mDrmInfoResolved && mDrmInfo == null) { - final String msg = "The Player has not been prepared yet"; - Log.v(TAG, msg); - throw new IllegalStateException(msg); - } + final SourceInfo sourceInfo = getSourceInfo(dsd); + if (sourceInfo != null) { + DrmInfo drmInfo = null; + + // there is not much point if the app calls getDrmInfo within an OnDrmInfoListener; + // regardless below returns drmInfo anyway instead of raising an exception + synchronized (sourceInfo) { + if (!sourceInfo.mDrmInfoResolved && sourceInfo.mDrmInfo == null) { + final String msg = "The Player has not been prepared yet"; + Log.v(TAG, msg); + throw new IllegalStateException(msg); + } - if (mDrmInfo != null) { - drmInfo = mDrmInfo.makeCopy(); - } - } // synchronized + if (sourceInfo.mDrmInfo != null) { + drmInfo = sourceInfo.mDrmInfo.makeCopy(); + } + } // synchronized - return drmInfo; + return drmInfo; + } + return null; } /** @@ -3411,15 +3403,28 @@ public class MediaPlayer2 implements AutoCloseable */ // This is an asynchronous call. public Object prepareDrm(@NonNull DataSourceDesc dsd, @NonNull UUID uuid) { - // TODO: this implementation only works when dsd is the only data source return addTask(new Task(CALL_COMPLETED_PREPARE_DRM, true) { @Override void process() { - int status = PREPARE_DRM_STATUS_SUCCESS; + final SourceInfo sourceInfo = getSourceInfo(dsd); + int status = PREPARE_DRM_STATUS_PREPARATION_ERROR; boolean sendEvent = true; + if (sourceInfo == null) { + Log.e(TAG, "prepareDrm(): DataSource not found."); + } else if (sourceInfo.mDrmInfo == null) { + // only allowing if tied to a protected source; + // might relax for releasing offline keys + Log.e(TAG, "prepareDrm(): Wrong usage: The player must be prepared and " + + "DRM info be retrieved before this call."); + } else { + status = PREPARE_DRM_STATUS_SUCCESS; + } + try { - doPrepareDrm(dsd, uuid); + if (status == PREPARE_DRM_STATUS_SUCCESS) { + sourceInfo.mDrmHandle.prepare(uuid); + } } catch (ResourceBusyException e) { status = PREPARE_DRM_STATUS_RESOURCE_BUSY; } catch (UnsupportedSchemeException e) { @@ -3428,14 +3433,14 @@ public class MediaPlayer2 implements AutoCloseable Log.w(TAG, "prepareDrm: NotProvisionedException"); // handle provisioning internally; it'll reset mPrepareDrmInProgress - status = handleProvisioninig(dsd, uuid); + status = sourceInfo.mDrmHandle.handleProvisioninig(uuid, mTaskId); if (status == PREPARE_DRM_STATUS_SUCCESS) { // DrmEventCallback will be fired in provisioning sendEvent = false; } else { - synchronized (mDrmLock) { - cleanDrmObj(); + synchronized (sourceInfo.mDrmHandle) { + sourceInfo.mDrmHandle.cleanDrmObj(); } switch (status) { @@ -3478,95 +3483,6 @@ public class MediaPlayer2 implements AutoCloseable }); } - private void doPrepareDrm(@NonNull DataSourceDesc dsd, @NonNull UUID uuid) - throws UnsupportedSchemeException, ResourceBusyException, - NotProvisionedException { - Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + mOnDrmConfigHelper); - - synchronized (mDrmLock) { - // only allowing if tied to a protected source; might relax for releasing offline keys - if (mDrmInfo == null) { - final String msg = "prepareDrm(): Wrong usage: The player must be prepared and " - + "DRM info be retrieved before this call."; - Log.e(TAG, msg); - throw new IllegalStateException(msg); - } - - if (mActiveDrmScheme) { - final String msg = "prepareDrm(): Wrong usage: There is already " - + "an active DRM scheme with " + mDrmUUID; - Log.e(TAG, msg); - throw new IllegalStateException(msg); - } - - if (mPrepareDrmInProgress) { - final String msg = "prepareDrm(): Wrong usage: There is already " - + "a pending prepareDrm call."; - Log.e(TAG, msg); - throw new IllegalStateException(msg); - } - - if (mDrmProvisioningInProgress) { - final String msg = "prepareDrm(): Unexpectd: Provisioning is already in progress."; - Log.e(TAG, msg); - throw new IllegalStateException(msg); - } - - // shouldn't need this; just for safeguard - cleanDrmObj(); - - mPrepareDrmInProgress = true; - - try { - // only creating the DRM object to allow pre-openSession configuration - prepareDrm_createDrmStep(uuid); - } catch (Exception e) { - Log.w(TAG, "prepareDrm(): Exception ", e); - mPrepareDrmInProgress = false; - throw e; - } - - mDrmConfigAllowed = true; - } // synchronized - - // call the callback outside the lock - if (mOnDrmConfigHelper != null) { - mOnDrmConfigHelper.onDrmConfig(this, dsd); - } - - synchronized (mDrmLock) { - mDrmConfigAllowed = false; - boolean earlyExit = false; - - try { - prepareDrm_openSessionStep(uuid); - - mDrmUUID = uuid; - mActiveDrmScheme = true; - mPrepareDrmInProgress = false; - } catch (IllegalStateException e) { - final String msg = "prepareDrm(): Wrong usage: The player must be " - + "in the prepared state to call prepareDrm()."; - Log.e(TAG, msg); - earlyExit = true; - mPrepareDrmInProgress = false; - throw new IllegalStateException(msg); - } catch (NotProvisionedException e) { - Log.w(TAG, "prepareDrm: NotProvisionedException", e); - throw e; - } catch (Exception e) { - Log.e(TAG, "prepareDrm: Exception " + e); - earlyExit = true; - mPrepareDrmInProgress = false; - throw e; - } finally { - if (earlyExit) { // clean up object if didn't succeed - cleanDrmObj(); - } - } // finally - } // synchronized - } - /** * Releases the DRM session for the given data source * <p> @@ -3581,35 +3497,10 @@ public class MediaPlayer2 implements AutoCloseable // This is a synchronous call. public void releaseDrm(@NonNull DataSourceDesc dsd) throws NoDrmSchemeException { - // TODO: this implementation only works when dsd is the only data source - synchronized (mDrmLock) { - Log.v(TAG, "releaseDrm:"); - - if (!mActiveDrmScheme) { - Log.e(TAG, "releaseDrm(): No active DRM scheme to release."); - throw new NoDrmSchemeException( - "releaseDrm: No active DRM scheme to release."); - } - - try { - // we don't have the player's state in this layer. The below call raises - // exception if we're in a non-stopped/prepared state. - - // for cleaning native/mediaserver crypto object - native_releaseDrm(); - - // for cleaning client-side MediaDrm object; only called if above has succeeded - cleanDrmObj(); - - mActiveDrmScheme = false; - } catch (IllegalStateException e) { - Log.w(TAG, "releaseDrm: Exception ", e); - throw new IllegalStateException( - "releaseDrm: The player is not in a valid state."); - } catch (Exception e) { - Log.e(TAG, "releaseDrm: Exception ", e); - } - } // synchronized + final SourceInfo sourceInfo = getSourceInfo(dsd); + if (sourceInfo != null) { + sourceInfo.mDrmHandle.release(); + } } private native void native_releaseDrm(); @@ -3653,51 +3544,22 @@ public class MediaPlayer2 implements AutoCloseable * * @throws NoDrmSchemeException if there is no active DRM session */ - @NonNull public MediaDrm.KeyRequest getDrmKeyRequest( @NonNull DataSourceDesc dsd, @Nullable byte[] keySetId, @Nullable byte[] initData, @Nullable String mimeType, @MediaDrmKeyType int keyType, @Nullable Map<String, String> optionalParameters) throws NoDrmSchemeException { - // TODO: this implementation only works when dsd is the only data source - Log.v(TAG, "getDrmKeyRequest: " - + " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType - + " keyType: " + keyType + " optionalParameters: " + optionalParameters); - - synchronized (mDrmLock) { - if (!mActiveDrmScheme) { - Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException"); - throw new NoDrmSchemeException( - "getDrmKeyRequest: Has to set a DRM scheme first."); - } - - try { - byte[] scope = (keyType != MediaDrm.KEY_TYPE_RELEASE) - ? mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE - keySetId; // keySetId for KEY_TYPE_RELEASE - - HashMap<String, String> hmapOptionalParameters = - (optionalParameters != null) - ? new HashMap<String, String>(optionalParameters) : - null; - - MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scope, initData, mimeType, - keyType, hmapOptionalParameters); - Log.v(TAG, "getDrmKeyRequest: --> request: " + request); - - return request; - - } catch (NotProvisionedException e) { - Log.w(TAG, "getDrmKeyRequest NotProvisionedException: " - + "Unexpected. Shouldn't have reached here."); - throw new IllegalStateException("getDrmKeyRequest: Unexpected provisioning error."); - } catch (Exception e) { - Log.w(TAG, "getDrmKeyRequest Exception " + e); - throw e; - } - - } // synchronized + Log.v(TAG, "getDrmKeyRequest: " + + " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType + + " keyType: " + keyType + " optionalParameters: " + optionalParameters); + + final SourceInfo sourceInfo = getSourceInfo(dsd); + if (sourceInfo != null) { + return sourceInfo.mDrmHandle.getDrmKeyRequest( + keySetId, initData, mimeType, keyType, optionalParameters); + } + return null; } /** @@ -3727,40 +3589,13 @@ public class MediaPlayer2 implements AutoCloseable @NonNull DataSourceDesc dsd, @Nullable byte[] keySetId, @NonNull byte[] response) throws NoDrmSchemeException, DeniedByServerException { - // TODO: this implementation only works when dsd is the only data source Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId + " response: " + response); - synchronized (mDrmLock) { - - if (!mActiveDrmScheme) { - Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException"); - throw new NoDrmSchemeException( - "getDrmKeyRequest: Has to set a DRM scheme first."); - } - - try { - byte[] scope = (keySetId == null) - ? mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE - keySetId; // keySetId for KEY_TYPE_RELEASE - - byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response); - - Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId + " response: " + response - + " --> " + keySetResult); - - - return keySetResult; - - } catch (NotProvisionedException e) { - Log.w(TAG, "provideDrmKeyResponse NotProvisionedException: " - + "Unexpected. Shouldn't have reached here."); - throw new IllegalStateException("provideDrmKeyResponse: " - + "Unexpected provisioning error."); - } catch (Exception e) { - Log.w(TAG, "provideDrmKeyResponse Exception " + e); - throw e; - } - } // synchronized + final SourceInfo sourceInfo = getSourceInfo(dsd); + if (sourceInfo != null) { + return sourceInfo.mDrmHandle.provideDrmKeyResponse(keySetId, response); + } + return null; } /** @@ -3779,23 +3614,12 @@ public class MediaPlayer2 implements AutoCloseable @NonNull DataSourceDesc dsd, @NonNull byte[] keySetId) throws NoDrmSchemeException { - // TODO: this implementation only works when dsd is the only data source Log.v(TAG, "restoreDrmKeys: keySetId: " + keySetId); - synchronized (mDrmLock) { - if (!mActiveDrmScheme) { - Log.w(TAG, "restoreDrmKeys NoDrmSchemeException"); - throw new NoDrmSchemeException( - "restoreDrmKeys: Has to set a DRM scheme first."); - } - - try { - mDrmObj.restoreKeys(mDrmSessionId, keySetId); - } catch (Exception e) { - Log.w(TAG, "restoreKeys Exception " + e); - throw e; - } - } // synchronized + final SourceInfo sourceInfo = getSourceInfo(dsd); + if (sourceInfo != null) { + sourceInfo.mDrmHandle.restoreDrmKeys(keySetId); + } } /** @@ -3812,34 +3636,17 @@ public class MediaPlayer2 implements AutoCloseable * * @throws NoDrmSchemeException if there is no active DRM session */ - @NonNull public String getDrmPropertyString( @NonNull DataSourceDesc dsd, @NonNull @MediaDrmStringProperty String propertyName) throws NoDrmSchemeException { - // TODO: this implementation only works when dsd is the only data source Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName); - String value; - synchronized (mDrmLock) { - - if (!mActiveDrmScheme && !mDrmConfigAllowed) { - Log.w(TAG, "getDrmPropertyString NoDrmSchemeException"); - throw new NoDrmSchemeException( - "getDrmPropertyString: Has to prepareDrm() first."); - } - - try { - value = mDrmObj.getPropertyString(propertyName); - } catch (Exception e) { - Log.w(TAG, "getDrmPropertyString Exception " + e); - throw e; - } - } // synchronized - - Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName + " --> value: " + value); - - return value; + final SourceInfo sourceInfo = getSourceInfo(dsd); + if (sourceInfo != null) { + return sourceInfo.mDrmHandle.getDrmPropertyString(propertyName); + } + return null; } /** @@ -3863,21 +3670,10 @@ public class MediaPlayer2 implements AutoCloseable // TODO: this implementation only works when dsd is the only data source Log.v(TAG, "setDrmPropertyString: propertyName: " + propertyName + " value: " + value); - synchronized (mDrmLock) { - - if (!mActiveDrmScheme && !mDrmConfigAllowed) { - Log.w(TAG, "setDrmPropertyString NoDrmSchemeException"); - throw new NoDrmSchemeException( - "setDrmPropertyString: Has to prepareDrm() first."); - } - - try { - mDrmObj.setPropertyString(propertyName, value); - } catch (Exception e) { - Log.w(TAG, "setDrmPropertyString Exception " + e); - throw e; - } - } // synchronized + final SourceInfo sourceInfo = getSourceInfo(dsd); + if (sourceInfo != null) { + sourceInfo.mDrmHandle.setDrmPropertyString(propertyName, value); + } } /** @@ -4029,43 +3825,6 @@ public class MediaPlayer2 implements AutoCloseable private native void native_prepareDrm(@NonNull byte[] uuid, @NonNull byte[] drmSessionId); - // Modular DRM helpers - - private void prepareDrm_createDrmStep(@NonNull UUID uuid) - throws UnsupportedSchemeException { - Log.v(TAG, "prepareDrm_createDrmStep: UUID: " + uuid); - - try { - mDrmObj = new MediaDrm(uuid); - Log.v(TAG, "prepareDrm_createDrmStep: Created mDrmObj=" + mDrmObj); - } catch (Exception e) { // UnsupportedSchemeException - Log.e(TAG, "prepareDrm_createDrmStep: MediaDrm failed with " + e); - throw e; - } - } - - private void prepareDrm_openSessionStep(@NonNull UUID uuid) - throws NotProvisionedException, ResourceBusyException { - Log.v(TAG, "prepareDrm_openSessionStep: uuid: " + uuid); - - // TODO: don't need an open session for a future specialKeyReleaseDrm mode but we should do - // it anyway so it raises provisioning error if needed. We'd rather handle provisioning - // at prepareDrm/openSession rather than getDrmKeyRequest/provideDrmKeyResponse - try { - mDrmSessionId = mDrmObj.openSession(); - Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId=" + mDrmSessionId); - - // Sending it down to native/mediaserver to create the crypto object - // This call could simply fail due to bad player state, e.g., after play(). - native_prepareDrm(getByteArrayFromUUID(uuid), mDrmSessionId); - Log.v(TAG, "prepareDrm_openSessionStep: native_prepareDrm/Crypto succeeded"); - - } catch (Exception e) { //ResourceBusyException, NotProvisionedException - Log.e(TAG, "prepareDrm_openSessionStep: open/crypto failed with " + e); - throw e; - } - } - // Instantiated from the native side @SuppressWarnings("unused") private static class StreamEventCallback extends AudioTrack.StreamEventCallback { @@ -4097,227 +3856,28 @@ public class MediaPlayer2 implements AutoCloseable } } - private class ProvisioningThread extends Thread { - public static final int TIMEOUT_MS = 60000; - - private final DataSourceDesc mDSD; - private UUID mUuid; - private String mUrlStr; - private Object mDrmLock; - private MediaPlayer2 mMediaPlayer; - private int mStatus; - public int status() { - return mStatus; - } - - public ProvisioningThread(MediaDrm.ProvisionRequest request, - DataSourceDesc dsd, - UUID uuid, MediaPlayer2 mediaPlayer) { - // lock is held by the caller - mDSD = dsd; - mDrmLock = mediaPlayer.mDrmLock; - mMediaPlayer = mediaPlayer; - - mUrlStr = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData()); - mUuid = uuid; - - mStatus = PREPARE_DRM_STATUS_PREPARATION_ERROR; - - Log.v(TAG, "handleProvisioninig: Thread is initialised url: " + mUrlStr); - } - - public void run() { - - byte[] response = null; - boolean provisioningSucceeded = false; - try { - URL url = new URL(mUrlStr); - final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - try { - connection.setRequestMethod("POST"); - connection.setDoOutput(false); - connection.setDoInput(true); - connection.setConnectTimeout(TIMEOUT_MS); - connection.setReadTimeout(TIMEOUT_MS); - - connection.connect(); - response = readInputStreamFully(connection.getInputStream()); - - Log.v(TAG, "handleProvisioninig: Thread run: response " - + response.length + " " + response); - } catch (Exception e) { - mStatus = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR; - Log.w(TAG, "handleProvisioninig: Thread run: connect " + e + " url: " + url); - } finally { - connection.disconnect(); - } - } catch (Exception e) { - mStatus = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR; - Log.w(TAG, "handleProvisioninig: Thread run: openConnection " + e); - } - - if (response != null) { - try { - mDrmObj.provideProvisionResponse(response); - Log.v(TAG, "handleProvisioninig: Thread run: " - + "provideProvisionResponse SUCCEEDED!"); - - provisioningSucceeded = true; - } catch (Exception e) { - mStatus = PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR; - Log.w(TAG, "handleProvisioninig: Thread run: " - + "provideProvisionResponse " + e); - } - } - - boolean succeeded = false; - - synchronized (mDrmLock) { - // continuing with prepareDrm - if (provisioningSucceeded) { - succeeded = mMediaPlayer.resumePrepareDrm(mUuid); - mStatus = (succeeded) - ? PREPARE_DRM_STATUS_SUCCESS : - PREPARE_DRM_STATUS_PREPARATION_ERROR; - } - mMediaPlayer.mDrmProvisioningInProgress = false; - mMediaPlayer.mPrepareDrmInProgress = false; - if (!succeeded) { - cleanDrmObj(); // cleaning up if it hasn't gone through while in the lock - } - } // synchronized - - // calling the callback outside the lock - sendDrmEvent(new DrmEventNotifier() { - @Override - public void notify(DrmEventCallback callback) { - callback.onDrmPrepared( - mMediaPlayer, mDSD, mStatus); - } - }); - - synchronized (mTaskLock) { - if (mCurrentTask != null - && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE_DRM - && mCurrentTask.mNeedToWaitForEventToComplete) { - mCurrentTask = null; - processPendingTask_l(); - } - } - } - - /** - * Returns a byte[] containing the remainder of 'in', closing it when done. - */ - private byte[] readInputStreamFully(InputStream in) throws IOException { - try { - return readInputStreamFullyNoClose(in); - } finally { - in.close(); - } - } - - /** - * Returns a byte[] containing the remainder of 'in'. - */ - private byte[] readInputStreamFullyNoClose(InputStream in) throws IOException { - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int count; - while ((count = in.read(buffer)) != -1) { - bytes.write(buffer, 0, count); - } - return bytes.toByteArray(); - } - } // ProvisioningThread - - private int handleProvisioninig(DataSourceDesc dsd, UUID uuid) { - synchronized (mDrmLock) { - if (mDrmProvisioningInProgress) { - Log.e(TAG, "handleProvisioninig: Unexpected mDrmProvisioningInProgress"); - return PREPARE_DRM_STATUS_PREPARATION_ERROR; - } - - MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest(); - if (provReq == null) { - Log.e(TAG, "handleProvisioninig: getProvisionRequest returned null."); - return PREPARE_DRM_STATUS_PREPARATION_ERROR; - } - - Log.v(TAG, "handleProvisioninig provReq " - + " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl()); - - // networking in a background thread - mDrmProvisioningInProgress = true; - - mDrmProvisioningThread = new ProvisioningThread(provReq, dsd, uuid, this); - mDrmProvisioningThread.start(); - - return PREPARE_DRM_STATUS_SUCCESS; - } - } - - private boolean resumePrepareDrm(UUID uuid) { - Log.v(TAG, "resumePrepareDrm: uuid: " + uuid); - - // mDrmLock is guaranteed to be held - boolean success = false; + /** + * Returns a byte[] containing the remainder of 'in', closing it when done. + */ + private static byte[] readInputStreamFully(InputStream in) throws IOException { try { - // resuming - prepareDrm_openSessionStep(uuid); - - mDrmUUID = uuid; - mActiveDrmScheme = true; - - success = true; - } catch (Exception e) { - Log.w(TAG, "handleProvisioninig: Thread run native_prepareDrm resume failed with " + e); - // mDrmObj clean up is done by the caller + return readInputStreamFullyNoClose(in); + } finally { + in.close(); } - - return success; - } - - private void resetDrmState() { - synchronized (mDrmLock) { - Log.v(TAG, "resetDrmState:" - + " mDrmInfo=" + mDrmInfo - + " mDrmProvisioningThread=" + mDrmProvisioningThread - + " mPrepareDrmInProgress=" + mPrepareDrmInProgress - + " mActiveDrmScheme=" + mActiveDrmScheme); - - mDrmInfoResolved = false; - mDrmInfo = null; - - if (mDrmProvisioningThread != null) { - // timeout; relying on HttpUrlConnection - try { - mDrmProvisioningThread.join(); - } catch (InterruptedException e) { - Log.w(TAG, "resetDrmState: ProvThread.join Exception " + e); - } - mDrmProvisioningThread = null; - } - - mPrepareDrmInProgress = false; - mActiveDrmScheme = false; - - cleanDrmObj(); - } // synchronized } - private void cleanDrmObj() { - // the caller holds mDrmLock - Log.v(TAG, "cleanDrmObj: mDrmObj=" + mDrmObj + " mDrmSessionId=" + mDrmSessionId); - - if (mDrmSessionId != null) { - mDrmObj.closeSession(mDrmSessionId); - mDrmSessionId = null; - } - if (mDrmObj != null) { - mDrmObj.release(); - mDrmObj = null; + /** + * Returns a byte[] containing the remainder of 'in'. + */ + private static byte[] readInputStreamFullyNoClose(InputStream in) throws IOException { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int count; + while ((count = in.read(buffer)) != -1) { + bytes.write(buffer, 0, count); } + return bytes.toByteArray(); } private static byte[] getByteArrayFromUUID(@NonNull UUID uuid) { @@ -4333,8 +3893,6 @@ public class MediaPlayer2 implements AutoCloseable return uuidBytes; } - // Modular DRM end - private static class TimedTextUtil { // These keys must be in sync with the keys in TextDescription2.h private static final int KEY_START_TIME = 7; // int @@ -4410,6 +3968,7 @@ public class MediaPlayer2 implements AutoCloseable } private abstract class Task implements Runnable { + final long mTaskId = mTaskIdGenerator.getAndIncrement(); private final int mMediaCallType; private final boolean mNeedToWaitForEventToComplete; private DataSourceDesc mDSD; @@ -4501,7 +4060,503 @@ public class MediaPlayer2 implements AutoCloseable } }; - private final class SourceInfo { + // Modular DRM + final class DrmHandle { + + static final int PROVISION_TIMEOUT_MS = 60000; + + final DataSourceDesc mDSD; + + //--- guarded by |this| start + MediaDrm mDrmObj; + byte[] mDrmSessionId; + UUID mActiveDrmUUID; + boolean mDrmConfigAllowed; + boolean mDrmProvisioningInProgress; + boolean mPrepareDrmInProgress; + Future<?> mProvisionResult; + //--- guarded by |this| end + + DrmHandle(DataSourceDesc dsd) { + mDSD = dsd; + } + + void prepare(UUID uuid) throws UnsupportedSchemeException, + ResourceBusyException, NotProvisionedException { + final OnDrmConfigHelper onDrmConfigHelper = mOnDrmConfigHelper; + Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + onDrmConfigHelper); + + synchronized (this) { + if (mActiveDrmUUID != null) { + final String msg = "prepareDrm(): Wrong usage: There is already " + + "an active DRM scheme with " + uuid; + Log.e(TAG, msg); + throw new IllegalStateException(msg); + } + + if (mPrepareDrmInProgress) { + final String msg = "prepareDrm(): Wrong usage: There is already " + + "a pending prepareDrm call."; + Log.e(TAG, msg); + throw new IllegalStateException(msg); + } + + if (mDrmProvisioningInProgress) { + final String msg = "prepareDrm(): Unexpectd: Provisioning already in progress"; + Log.e(TAG, msg); + throw new IllegalStateException(msg); + } + + // shouldn't need this; just for safeguard + cleanDrmObj(); + + mPrepareDrmInProgress = true; + + try { + // only creating the DRM object to allow pre-openSession configuration + prepareDrm_createDrmStep(uuid); + } catch (Exception e) { + Log.w(TAG, "prepareDrm(): Exception ", e); + mPrepareDrmInProgress = false; + throw e; + } + + mDrmConfigAllowed = true; + } // synchronized + + // call the callback outside the lock + if (onDrmConfigHelper != null) { + onDrmConfigHelper.onDrmConfig(MediaPlayer2.this, mDSD); + } + + synchronized (this) { + mDrmConfigAllowed = false; + boolean earlyExit = false; + + try { + prepareDrm_openSessionStep(uuid); + + this.mActiveDrmUUID = uuid; + mPrepareDrmInProgress = false; + } catch (IllegalStateException e) { + final String msg = "prepareDrm(): Wrong usage: The player must be " + + "in the prepared state to call prepareDrm()."; + Log.e(TAG, msg); + earlyExit = true; + mPrepareDrmInProgress = false; + throw new IllegalStateException(msg); + } catch (NotProvisionedException e) { + Log.w(TAG, "prepareDrm: NotProvisionedException", e); + throw e; + } catch (Exception e) { + Log.e(TAG, "prepareDrm: Exception " + e); + earlyExit = true; + mPrepareDrmInProgress = false; + throw e; + } finally { + if (earlyExit) { // clean up object if didn't succeed + cleanDrmObj(); + } + } // finally + } // synchronized + } + + void prepareDrm_createDrmStep(UUID uuid) + throws UnsupportedSchemeException { + Log.v(TAG, "prepareDrm_createDrmStep: UUID: " + uuid); + + try { + mDrmObj = new MediaDrm(uuid); + Log.v(TAG, "prepareDrm_createDrmStep: Created mDrmObj=" + mDrmObj); + } catch (Exception e) { // UnsupportedSchemeException + Log.e(TAG, "prepareDrm_createDrmStep: MediaDrm failed with " + e); + throw e; + } + } + + void prepareDrm_openSessionStep(UUID uuid) + throws NotProvisionedException, ResourceBusyException { + Log.v(TAG, "prepareDrm_openSessionStep: uuid: " + uuid); + + // TODO: + // don't need an open session for a future specialKeyReleaseDrm mode but we should do + // it anyway so it raises provisioning error if needed. We'd rather handle provisioning + // at prepareDrm/openSession rather than getDrmKeyRequest/provideDrmKeyResponse + try { + mDrmSessionId = mDrmObj.openSession(); + Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId=" + mDrmSessionId); + + // Sending it down to native/mediaserver to create the crypto object + // This call could simply fail due to bad player state, e.g., after play(). + MediaPlayer2.this.native_prepareDrm(getByteArrayFromUUID(uuid), mDrmSessionId); + Log.v(TAG, "prepareDrm_openSessionStep: native_prepareDrm/Crypto succeeded"); + + } catch (Exception e) { //ResourceBusyException, NotProvisionedException + Log.e(TAG, "prepareDrm_openSessionStep: open/crypto failed with " + e); + throw e; + } + + } + + int handleProvisioninig(UUID uuid, long taskId) { + synchronized (this) { + if (mDrmProvisioningInProgress) { + Log.e(TAG, "handleProvisioninig: Unexpected mDrmProvisioningInProgress"); + return PREPARE_DRM_STATUS_PREPARATION_ERROR; + } + + MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest(); + if (provReq == null) { + Log.e(TAG, "handleProvisioninig: getProvisionRequest returned null."); + return PREPARE_DRM_STATUS_PREPARATION_ERROR; + } + + Log.v(TAG, "handleProvisioninig provReq " + + " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl()); + + // networking in a background thread + mDrmProvisioningInProgress = true; + + mProvisionResult = mDrmThreadPool.submit(newProvisioningTask(uuid, taskId)); + + return PREPARE_DRM_STATUS_SUCCESS; + } + } + + void provision(UUID uuid, long taskId) { + + MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest(); + String urlStr = provReq.getDefaultUrl(); + urlStr += "&signedRequest=" + new String(provReq.getData()); + Log.v(TAG, "handleProvisioninig: Thread is initialised url: " + urlStr); + + byte[] response = null; + boolean provisioningSucceeded = false; + int status = PREPARE_DRM_STATUS_PREPARATION_ERROR; + try { + URL url = new URL(urlStr); + final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + try { + connection.setRequestMethod("POST"); + connection.setDoOutput(false); + connection.setDoInput(true); + connection.setConnectTimeout(PROVISION_TIMEOUT_MS); + connection.setReadTimeout(PROVISION_TIMEOUT_MS); + + connection.connect(); + response = readInputStreamFully(connection.getInputStream()); + + Log.v(TAG, "handleProvisioninig: Thread run: response " + + response.length + " " + response); + } catch (Exception e) { + status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR; + Log.w(TAG, "handleProvisioninig: Thread run: connect " + e + " url: " + url); + } finally { + connection.disconnect(); + } + } catch (Exception e) { + status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR; + Log.w(TAG, "handleProvisioninig: Thread run: openConnection " + e); + } + + if (response != null) { + try { + mDrmObj.provideProvisionResponse(response); + Log.v(TAG, "handleProvisioninig: Thread run: " + + "provideProvisionResponse SUCCEEDED!"); + + provisioningSucceeded = true; + } catch (Exception e) { + status = PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR; + Log.w(TAG, "handleProvisioninig: Thread run: " + + "provideProvisionResponse " + e); + } + } + + boolean succeeded = false; + + synchronized (this) { + // continuing with prepareDrm + if (provisioningSucceeded) { + succeeded = resumePrepare(uuid); + status = (succeeded) ? + PREPARE_DRM_STATUS_SUCCESS : + PREPARE_DRM_STATUS_PREPARATION_ERROR; + } + mDrmProvisioningInProgress = false; + mPrepareDrmInProgress = false; + if (!succeeded) { + cleanDrmObj(); // cleaning up if it hasn't gone through while in the lock + } + } // synchronized + + // calling the callback outside the lock + final int finalStatus = status; + sendDrmEvent(new DrmEventNotifier() { + @Override + public void notify(DrmEventCallback callback) { + callback.onDrmPrepared( + MediaPlayer2.this, mDSD, finalStatus); + } + }); + + synchronized (mTaskLock) { + if (mCurrentTask != null + && mCurrentTask.mTaskId == taskId + && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE_DRM + && mCurrentTask.mNeedToWaitForEventToComplete) { + mCurrentTask = null; + processPendingTask_l(); + } + } + } + + Runnable newProvisioningTask(UUID uuid, long taskId) { + return new Runnable() { + @Override + public void run() { + provision(uuid, taskId); + } + }; + } + + boolean resumePrepare(UUID uuid) { + Log.v(TAG, "resumePrepareDrm: uuid: " + uuid); + + // mDrmLock is guaranteed to be held + boolean success = false; + try { + // resuming + prepareDrm_openSessionStep(uuid); + + this.mActiveDrmUUID = uuid; + + success = true; + } catch (Exception e) { + Log.w(TAG, "handleProvisioninig: Thread run native_prepareDrm resume failed:" + e); + // mDrmObj clean up is done by the caller + } + + return success; + } + + void cleanDrmObj() { + // the caller holds mDrmLock + Log.v(TAG, "cleanDrmObj: mDrmObj=" + mDrmObj + " mDrmSessionId=" + mDrmSessionId); + + if (mDrmSessionId != null) { + mDrmObj.closeSession(mDrmSessionId); + mDrmSessionId = null; + } + if (mDrmObj != null) { + mDrmObj.close(); + mDrmObj = null; + } + } + + void release() throws NoDrmSchemeException { + synchronized (this) { + Log.v(TAG, "releaseDrm:"); + + if (mActiveDrmUUID == null) { + Log.e(TAG, "releaseDrm(): No active DRM scheme to release."); + throw new NoDrmSchemeException( + "releaseDrm: No active DRM scheme to release."); + } + + try { + // we don't have the player's state in this layer. The below call raises + // exception if we're in a non-stopped/prepared state. + + // for cleaning native/mediaserver crypto object + native_releaseDrm(); + + // for cleaning client-side MediaDrm object; only called if above has succeeded + cleanDrmObj(); + + this.mActiveDrmUUID = null; + } catch (IllegalStateException e) { + Log.w(TAG, "releaseDrm: Exception ", e); + throw new IllegalStateException( + "releaseDrm: The player is not in a valid state."); + } catch (Exception e) { + Log.e(TAG, "releaseDrm: Exception ", e); + } + } // synchronized + } + + void cleanup() { + synchronized (this) { + Log.v(TAG, "cleanupDrm: " + + " mProvisioningTask=" + mProvisionResult + + " mPrepareDrmInProgress=" + mPrepareDrmInProgress + + " mActiveDrmScheme=" + mActiveDrmUUID); + + if (mProvisionResult != null) { + // timeout; relying on HttpUrlConnection + try { + mProvisionResult.get(); + } + catch (InterruptedException | ExecutionException e) { + Log.w(TAG, "resetDrmState: ProvThread.join Exception " + e); + } + } + + // set to false to avoid duplicate release calls + this.mActiveDrmUUID = null; + + cleanDrmObj(); + } // synchronized + } + + Runnable newCleanupTask() { + return new Runnable() { + @Override + public void run() { + cleanup(); + } + }; + } + + MediaDrm.KeyRequest getDrmKeyRequest( + byte[] keySetId, byte[] initData, + String mimeType, int keyType, + Map<String, String> optionalParameters) + throws NoDrmSchemeException { + synchronized (this) { + if (mActiveDrmUUID == null) { + Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException"); + throw new NoDrmSchemeException( + "getDrmKeyRequest: Has to set a DRM scheme first."); + } + + try { + byte[] scope = (keyType != MediaDrm.KEY_TYPE_RELEASE) ? + mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE + keySetId; // keySetId for KEY_TYPE_RELEASE + + HashMap<String, String> hmapOptionalParameters = + (optionalParameters != null) + ? new HashMap<String, String>(optionalParameters) + : null; + + MediaDrm.KeyRequest request = mDrmObj.getKeyRequest( + scope, initData, mimeType, keyType, hmapOptionalParameters); + Log.v(TAG, "getDrmKeyRequest: --> request: " + request); + + return request; + + } catch (NotProvisionedException e) { + Log.w(TAG, "getDrmKeyRequest NotProvisionedException: " + + "Unexpected. Shouldn't have reached here."); + throw new IllegalStateException("getDrmKeyRequest: provisioning error."); + } catch (Exception e) { + Log.w(TAG, "getDrmKeyRequest Exception " + e); + throw e; + } + + } + } + + byte[] provideDrmKeyResponse(byte[] keySetId, byte[] response) + throws NoDrmSchemeException, DeniedByServerException { + synchronized (this) { + + if (mActiveDrmUUID == null) { + Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException"); + throw new NoDrmSchemeException( + "getDrmKeyRequest: Has to set a DRM scheme first."); + } + + try { + byte[] scope = (keySetId == null) ? + mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE + keySetId; // keySetId for KEY_TYPE_RELEASE + + byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response); + + Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId + + " response: " + response + " --> " + keySetResult); + + + return keySetResult; + + } catch (NotProvisionedException e) { + Log.w(TAG, "provideDrmKeyResponse NotProvisionedException: " + + "Unexpected. Shouldn't have reached here."); + throw new IllegalStateException("provideDrmKeyResponse: " + + "Unexpected provisioning error."); + } catch (Exception e) { + Log.w(TAG, "provideDrmKeyResponse Exception " + e); + throw e; + } + } + } + + void restoreDrmKeys(byte[] keySetId) + throws NoDrmSchemeException { + synchronized (this) { + if (mActiveDrmUUID == null) { + Log.w(TAG, "restoreDrmKeys NoDrmSchemeException"); + throw new NoDrmSchemeException( + "restoreDrmKeys: Has to set a DRM scheme first."); + } + + try { + mDrmObj.restoreKeys(mDrmSessionId, keySetId); + } catch (Exception e) { + Log.w(TAG, "restoreKeys Exception " + e); + throw e; + } + } + } + + String getDrmPropertyString(String propertyName) + throws NoDrmSchemeException { + String v; + synchronized (this) { + + if (mActiveDrmUUID == null && !mDrmConfigAllowed) { + Log.w(TAG, "getDrmPropertyString NoDrmSchemeException"); + throw new NoDrmSchemeException( + "getDrmPropertyString: Has to prepareDrm() first."); + } + + try { + v = mDrmObj.getPropertyString(propertyName); + } catch (Exception e) { + Log.w(TAG, "getDrmPropertyString Exception " + e); + throw e; + } + } // synchronized + + Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName + " --> value: " + v); + + return v; + } + + void setDrmPropertyString(String propertyName, String value) + throws NoDrmSchemeException { + synchronized (this) { + + if ( mActiveDrmUUID == null && !mDrmConfigAllowed ) { + Log.w(TAG, "setDrmPropertyString NoDrmSchemeException"); + throw new NoDrmSchemeException( + "setDrmPropertyString: Has to prepareDrm() first."); + } + + try { + mDrmObj.setPropertyString(propertyName, value); + } catch ( Exception e ) { + Log.w(TAG, "setDrmPropertyString Exception " + e); + throw e; + } + } + } + + } + + final class SourceInfo { final DataSourceDesc mDSD; final long mId = mSrcIdGenerator.getAndIncrement(); AtomicInteger mBufferedPercentage = new AtomicInteger(0); @@ -4513,8 +4568,14 @@ public class MediaPlayer2 implements AutoCloseable int mStateAsNextSource = NEXT_SOURCE_STATE_INIT; boolean mPlayPendingAsNextSource = false; + // Modular DRM + final DrmHandle mDrmHandle; + DrmInfo mDrmInfo; + boolean mDrmInfoResolved; + SourceInfo(DataSourceDesc dsd) { this.mDSD = dsd; + mDrmHandle = new DrmHandle(dsd); } void close() { @@ -4535,7 +4596,7 @@ public class MediaPlayer2 implements AutoCloseable } - private SourceInfo getSourceInfoById(long srcId) { + private SourceInfo getSourceInfo(long srcId) { synchronized (mSrcLock) { if (isCurrentSource(srcId)) { return mCurrentSourceInfo; @@ -4547,34 +4608,65 @@ public class MediaPlayer2 implements AutoCloseable return null; } + private SourceInfo getSourceInfo(DataSourceDesc dsd) { + synchronized (mSrcLock) { + if (isCurrentSource(dsd)) { + return mCurrentSourceInfo; + } + if (isNextSource(dsd)) { + return mNextSourceInfos.peek(); + } + } + return null; + } + private boolean isCurrentSource(long srcId) { synchronized (mSrcLock) { return mCurrentSourceInfo != null && mCurrentSourceInfo.mId == srcId; } } + private boolean isCurrentSource(DataSourceDesc dsd) { + synchronized (mSrcLock) { + return mCurrentSourceInfo != null && mCurrentSourceInfo.mDSD == dsd; + } + } + private boolean isNextSource(long srcId) { SourceInfo nextSourceInfo = mNextSourceInfos.peek(); return nextSourceInfo != null && nextSourceInfo.mId == srcId; } - private void setCurrentSourceInfo(SourceInfo newSourceInfo) { - synchronized (mSrcLock) { - if (mCurrentSourceInfo != null) { - mCurrentSourceInfo.close(); - } - mCurrentSourceInfo = newSourceInfo; + private boolean isNextSource(DataSourceDesc dsd) { + SourceInfo nextSourceInfo = mNextSourceInfos.peek(); + return nextSourceInfo != null && nextSourceInfo.mDSD == dsd; + } + + @GuardedBy("mSrcLock") + private void setCurrentSourceInfo_l(SourceInfo sourceInfo) { + cleanupSourceInfo(mCurrentSourceInfo); + mCurrentSourceInfo = sourceInfo; + } + + @GuardedBy("mSrcLock") + private void clearNextSourceInfos_l() { + while (!mNextSourceInfos.isEmpty()) { + cleanupSourceInfo(mNextSourceInfos.poll()); + } + } + + private void cleanupSourceInfo(SourceInfo sourceInfo) { + if (sourceInfo != null) { + sourceInfo.close(); + Runnable task = sourceInfo.mDrmHandle.newCleanupTask(); + mDrmThreadPool.submit(task); } } - private void clearNextSourceInfos() { + private void clearSourceInfos() { synchronized (mSrcLock) { - for (SourceInfo sourceInfo : mNextSourceInfos) { - if (sourceInfo != null) { - sourceInfo.close(); - } - } - mNextSourceInfos.clear(); + setCurrentSourceInfo_l(null); + clearNextSourceInfos_l(); } } 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/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index 436897fbf8b0..874f21ea8c28 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -18,6 +18,7 @@ package android.media; import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -1029,7 +1030,7 @@ public class RingtoneManager { * @throws FileNotFoundException if the provided URI could not be opened. * @see #getDefaultUri */ - public static AssetFileDescriptor openDefaultRingtoneUri( + public static @Nullable AssetFileDescriptor openDefaultRingtoneUri( @NonNull Context context, @NonNull Uri uri) throws FileNotFoundException { // Try cached ringtone first since the actual provider may not be // encryption aware, or it may be stored on CE media storage 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/native/webview/plat_support/draw_gl.h b/native/webview/plat_support/draw_gl.h index c8434b61eba5..de13ed0dec6f 100644 --- a/native/webview/plat_support/draw_gl.h +++ b/native/webview/plat_support/draw_gl.h @@ -43,9 +43,9 @@ struct AwDrawGLInfo { // Input: tells the draw function what action to perform. enum Mode { kModeDraw = 0, - kModeProcess, - kModeProcessNoContext, - kModeSync, + kModeProcess = 1, + kModeProcessNoContext = 2, + kModeSync = 3, } mode; // Input: current clip rect in surface coordinates. Reflects the current state @@ -93,9 +93,9 @@ typedef void (AwDrawGLFunction)(long view_context, AwDrawGLInfo* draw_info, void* spare); enum AwMapMode { - MAP_READ_ONLY, - MAP_WRITE_ONLY, - MAP_READ_WRITE, + MAP_READ_ONLY = 0, + MAP_WRITE_ONLY = 1, + MAP_READ_WRITE = 2, }; // Called to create a GraphicBuffer 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/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp deleted file mode 100644 index bc06cab7aef5..000000000000 --- a/packages/PackageInstaller/Android.bp +++ /dev/null @@ -1,14 +0,0 @@ -android_app { - name: "PackageInstaller", - - srcs: ["src/**/*.java"], - - static_libs: [ - "androidx.leanback_leanback", - "xz-java", - ], - - certificate: "platform", - privileged: true, - platform_apis: true, -}
\ No newline at end of file diff --git a/packages/PackageInstaller/Android.mk b/packages/PackageInstaller/Android.mk new file mode 100644 index 000000000000..ab5483c8afb8 --- /dev/null +++ b/packages/PackageInstaller/Android.mk @@ -0,0 +1,32 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_PACKAGE_NAME := PackageInstaller + +LOCAL_CERTIFICATE := platform +LOCAL_PRIVILEGED_MODULE := true +LOCAL_PRIVATE_PLATFORM_APIS := true + +LOCAL_STATIC_JAVA_LIBRARIES := xz-java +LOCAL_STATIC_ANDROID_LIBRARIES := androidx.leanback_leanback + +include $(BUILD_PACKAGE) + +include $(call all-makefiles-under, $(LOCAL_PATH)) diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml index 4801f62bae67..eb9ec82c9519 100644 --- a/packages/PackageInstaller/AndroidManifest.xml +++ b/packages/PackageInstaller/AndroidManifest.xml @@ -2,6 +2,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.packageinstaller"> + <original-package android:name="com.android.packageinstaller" /> + <uses-permission android:name="android.permission.MANAGE_USERS" /> <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.DELETE_PACKAGES" /> diff --git a/packages/SettingsLib/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp new file mode 100644 index 000000000000..e518e0b97c4b --- /dev/null +++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp @@ -0,0 +1,13 @@ +android_library { + name: "ActionButtonsPreference", + + srcs: ["src/**/*.java"], + resource_dirs: ["res"], + + static_libs: [ + "androidx.preference_preference", + ], + + sdk_version: "system_current", + min_sdk_version: "21", +} diff --git a/packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml b/packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml new file mode 100644 index 000000000000..4b9f1ab8d6cc --- /dev/null +++ b/packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.settingslib.widget"> + + <uses-sdk android:minSdkVersion="21" /> + +</manifest> diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout/settings_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout/settings_action_buttons.xml new file mode 100644 index 000000000000..4f47113bbafa --- /dev/null +++ b/packages/SettingsLib/ActionButtonsPreference/res/layout/settings_action_buttons.xml @@ -0,0 +1,52 @@ +<?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. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingStart="8dp" + android:orientation="horizontal"> + + <Button + android:id="@+id/button1" + style="@style/SettingsActionButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1"/> + + <Button + android:id="@+id/button2" + style="@style/SettingsActionButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1"/> + + <Button + android:id="@+id/button3" + style="@style/SettingsActionButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1"/> + + <Button + android:id="@+id/button4" + style="@style/SettingsActionButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1"/> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml b/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml new file mode 100644 index 000000000000..efa508dcde62 --- /dev/null +++ b/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + <style name="SettingsActionButton" parent="android:Widget.DeviceDefault.Button.Borderless.Colored"> + <item name="android:drawablePadding">4dp</item> + <item name="android:drawableTint">@*android:color/btn_colored_borderless_text_material</item> + <item name="android:layout_marginEnd">8dp</item> + <item name="android:paddingTop">20dp</item> + <item name="android:paddingBottom">20dp</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java new file mode 100644 index 000000000000..8b46cc608fd1 --- /dev/null +++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.widget; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.widget.Button; + +import androidx.annotation.DrawableRes; +import androidx.annotation.StringRes; +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + +/** + * This preference provides a four buttons layout with Settings style. + * It looks like below + * + * -------------------------------------------------- + * button1 | button2 | button3 | button4 | + * -------------------------------------------------- + * + * User can set title / icon / click listener for each button. + * + * By default, four buttons are visible. + * However, there are two cases which button should be invisible(View.GONE). + * + * 1. User sets invisible for button. ex: ActionButtonPreference.setButton1Visible(false) + * 2. User doesn't set any title or icon for button. + */ +public class ActionButtonsPreference extends Preference { + + private static final String TAG = "ActionButtonPreference"; + private final ButtonInfo mButton1Info = new ButtonInfo(); + private final ButtonInfo mButton2Info = new ButtonInfo(); + private final ButtonInfo mButton3Info = new ButtonInfo(); + private final ButtonInfo mButton4Info = new ButtonInfo(); + + public ActionButtonsPreference(Context context, AttributeSet attrs, + int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(); + } + + public ActionButtonsPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + public ActionButtonsPreference(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public ActionButtonsPreference(Context context) { + super(context); + init(); + } + + private void init() { + setLayoutResource(R.layout.settings_action_buttons); + setSelectable(false); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + holder.setDividerAllowedAbove(true); + holder.setDividerAllowedBelow(true); + + mButton1Info.mButton = (Button) holder.findViewById(R.id.button1); + mButton2Info.mButton = (Button) holder.findViewById(R.id.button2); + mButton3Info.mButton = (Button) holder.findViewById(R.id.button3); + mButton4Info.mButton = (Button) holder.findViewById(R.id.button4); + + mButton1Info.setUpButton(); + mButton2Info.setUpButton(); + mButton3Info.setUpButton(); + mButton4Info.setUpButton(); + } + + /** + * Set the visibility state of button1. + */ + public ActionButtonsPreference setButton1Visible(boolean isVisible) { + if (isVisible != mButton1Info.mIsVisible) { + mButton1Info.mIsVisible = isVisible; + notifyChanged(); + } + return this; + } + + /** + * Sets the text to be displayed in button1. + */ + public ActionButtonsPreference setButton1Text(@StringRes int textResId) { + final String newText = getContext().getString(textResId); + if (!TextUtils.equals(newText, mButton1Info.mText)) { + mButton1Info.mText = newText; + notifyChanged(); + } + return this; + } + + /** + * Sets the drawable to be displayed above of text in button1. + */ + public ActionButtonsPreference setButton1Icon(@DrawableRes int iconResId) { + if (iconResId == 0) { + return this; + } + + final Drawable icon; + try { + icon = getContext().getDrawable(iconResId); + mButton1Info.mIcon = icon; + notifyChanged(); + } catch (Resources.NotFoundException exception) { + Log.e(TAG, "Resource does not exist: " + iconResId); + } + return this; + } + + /** + * Set the enabled state of button1. + */ + public ActionButtonsPreference setButton1Enabled(boolean isEnabled) { + if (isEnabled != mButton1Info.mIsEnabled) { + mButton1Info.mIsEnabled = isEnabled; + notifyChanged(); + } + return this; + } + + /** + * Register a callback to be invoked when button1 is clicked. + */ + public ActionButtonsPreference setButton1OnClickListener( + View.OnClickListener listener) { + if (listener != mButton1Info.mListener) { + mButton1Info.mListener = listener; + notifyChanged(); + } + return this; + } + + /** + * Set the visibility state of button2. + */ + public ActionButtonsPreference setButton2Visible(boolean isVisible) { + if (isVisible != mButton2Info.mIsVisible) { + mButton2Info.mIsVisible = isVisible; + notifyChanged(); + } + return this; + } + + /** + * Sets the text to be displayed in button2. + */ + public ActionButtonsPreference setButton2Text(@StringRes int textResId) { + final String newText = getContext().getString(textResId); + if (!TextUtils.equals(newText, mButton2Info.mText)) { + mButton2Info.mText = newText; + notifyChanged(); + } + return this; + } + + /** + * Sets the drawable to be displayed above of text in button2. + */ + public ActionButtonsPreference setButton2Icon(@DrawableRes int iconResId) { + if (iconResId == 0) { + return this; + } + + final Drawable icon; + try { + icon = getContext().getDrawable(iconResId); + mButton2Info.mIcon = icon; + notifyChanged(); + } catch (Resources.NotFoundException exception) { + Log.e(TAG, "Resource does not exist: " + iconResId); + } + return this; + } + + /** + * Set the enabled state of button2. + */ + public ActionButtonsPreference setButton2Enabled(boolean isEnabled) { + if (isEnabled != mButton2Info.mIsEnabled) { + mButton2Info.mIsEnabled = isEnabled; + notifyChanged(); + } + return this; + } + + /** + * Register a callback to be invoked when button2 is clicked. + */ + public ActionButtonsPreference setButton2OnClickListener( + View.OnClickListener listener) { + if (listener != mButton2Info.mListener) { + mButton2Info.mListener = listener; + notifyChanged(); + } + return this; + } + + /** + * Set the visibility state of button3. + */ + public ActionButtonsPreference setButton3Visible(boolean isVisible) { + if (isVisible != mButton3Info.mIsVisible) { + mButton3Info.mIsVisible = isVisible; + notifyChanged(); + } + return this; + } + + /** + * Sets the text to be displayed in button3. + */ + public ActionButtonsPreference setButton3Text(@StringRes int textResId) { + final String newText = getContext().getString(textResId); + if (!TextUtils.equals(newText, mButton3Info.mText)) { + mButton3Info.mText = newText; + notifyChanged(); + } + return this; + } + + /** + * Sets the drawable to be displayed above of text in button3. + */ + public ActionButtonsPreference setButton3Icon(@DrawableRes int iconResId) { + if (iconResId == 0) { + return this; + } + + final Drawable icon; + try { + icon = getContext().getDrawable(iconResId); + mButton3Info.mIcon = icon; + notifyChanged(); + } catch (Resources.NotFoundException exception) { + Log.e(TAG, "Resource does not exist: " + iconResId); + } + return this; + } + + /** + * Set the enabled state of button3. + */ + public ActionButtonsPreference setButton3Enabled(boolean isEnabled) { + if (isEnabled != mButton3Info.mIsEnabled) { + mButton3Info.mIsEnabled = isEnabled; + notifyChanged(); + } + return this; + } + + /** + * Register a callback to be invoked when button3 is clicked. + */ + public ActionButtonsPreference setButton3OnClickListener( + View.OnClickListener listener) { + if (listener != mButton3Info.mListener) { + mButton3Info.mListener = listener; + notifyChanged(); + } + return this; + } + + /** + * Set the visibility state of button4. + */ + public ActionButtonsPreference setButton4Visible(boolean isVisible) { + if (isVisible != mButton4Info.mIsVisible) { + mButton4Info.mIsVisible = isVisible; + notifyChanged(); + } + return this; + } + + /** + * Sets the text to be displayed in button4. + */ + public ActionButtonsPreference setButton4Text(@StringRes int textResId) { + final String newText = getContext().getString(textResId); + if (!TextUtils.equals(newText, mButton4Info.mText)) { + mButton4Info.mText = newText; + notifyChanged(); + } + return this; + } + + /** + * Sets the drawable to be displayed above of text in button4. + */ + public ActionButtonsPreference setButton4Icon(@DrawableRes int iconResId) { + if (iconResId == 0) { + return this; + } + + final Drawable icon; + try { + icon = getContext().getDrawable(iconResId); + mButton4Info.mIcon = icon; + notifyChanged(); + } catch (Resources.NotFoundException exception) { + Log.e(TAG, "Resource does not exist: " + iconResId); + } + return this; + } + + /** + * Set the enabled state of button4. + */ + public ActionButtonsPreference setButton4Enabled(boolean isEnabled) { + if (isEnabled != mButton4Info.mIsEnabled) { + mButton4Info.mIsEnabled = isEnabled; + notifyChanged(); + } + return this; + } + + /** + * Register a callback to be invoked when button4 is clicked. + */ + public ActionButtonsPreference setButton4OnClickListener( + View.OnClickListener listener) { + if (listener != mButton4Info.mListener) { + mButton4Info.mListener = listener; + notifyChanged(); + } + return this; + } + + static class ButtonInfo { + private Button mButton; + private CharSequence mText; + private Drawable mIcon; + private View.OnClickListener mListener; + private boolean mIsEnabled = true; + private boolean mIsVisible = true; + + void setUpButton() { + mButton.setText(mText); + mButton.setOnClickListener(mListener); + mButton.setEnabled(mIsEnabled); + mButton.setCompoundDrawablesWithIntrinsicBounds( + null /* left */, mIcon /* top */, null /* right */, null /* bottom */); + + if (shouldBeVisible()) { + mButton.setVisibility(View.VISIBLE); + } else { + mButton.setVisibility(View.GONE); + } + } + + /** + * By default, four buttons are visible. + * However, there are two cases which button should be invisible. + * + * 1. User set invisible for this button. ex: mIsVisible = false. + * 2. User didn't set any title or icon. + */ + private boolean shouldBeVisible() { + return mIsVisible && (!TextUtils.isEmpty(mText) || mIcon != null); + } + } +} diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 444e72459510..cc17b25d9a40 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -17,6 +17,7 @@ android_library { "SettingsLibSearchWidget", "SettingsLibSettingsSpinner", "SettingsLayoutPreference", + "ActionButtonsPreference", ], // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml index 06782633a4de..01d9c00d94dd 100644 --- a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml +++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml @@ -35,7 +35,7 @@ android:id="@+id/entity_header_icon" android:layout_width="48dp" android:layout_height="48dp" - android:scaleType="fitXY" + android:scaleType="fitCenter" android:antialias="true"/> <TextView @@ -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/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index a97e05442b6d..9270d13002d7 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -208,6 +208,10 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> mHiSyncId = id; } + public boolean isHearingAidDevice() { + return mHiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID; + } + void onBondingDockConnect() { // Attempt to connect if UUIDs are available. Otherwise, // we will connect when the ACTION_UUID intent arrives. 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/ActionButtonsPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java new file mode 100644 index 000000000000..88fef08bfcb7 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.widget; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.widget.Button; + +import androidx.preference.PreferenceViewHolder; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class ActionButtonsPreferenceTest { + + private Context mContext; + private View mRootView; + private ActionButtonsPreference mPref; + private PreferenceViewHolder mHolder; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mRootView = View.inflate(mContext, R.layout.settings_action_buttons, null /* parent */); + mHolder = PreferenceViewHolder.createInstanceForTests(mRootView); + mPref = new ActionButtonsPreference(mContext); + } + + @Test + public void onBindViewHolder_setTitle_shouldShowButtonByDefault() { + mPref.setButton1Text(R.string.install_other_apps); + mPref.setButton2Text(R.string.install_other_apps); + mPref.setButton3Text(R.string.install_other_apps); + mPref.setButton4Text(R.string.install_other_apps); + + mPref.onBindViewHolder(mHolder); + + assertThat(mRootView.findViewById(R.id.button1).getVisibility()) + .isEqualTo(View.VISIBLE); + assertThat(mRootView.findViewById(R.id.button2).getVisibility()) + .isEqualTo(View.VISIBLE); + assertThat(mRootView.findViewById(R.id.button3).getVisibility()) + .isEqualTo(View.VISIBLE); + assertThat(mRootView.findViewById(R.id.button4).getVisibility()) + .isEqualTo(View.VISIBLE); + } + + @Test + public void onBindViewHolder_setIcon_shouldShowButtonByDefault() { + mPref.setButton1Icon(R.drawable.ic_plus); + mPref.setButton2Icon(R.drawable.ic_plus); + mPref.setButton3Icon(R.drawable.ic_plus); + mPref.setButton4Icon(R.drawable.ic_plus); + + mPref.onBindViewHolder(mHolder); + + assertThat(mRootView.findViewById(R.id.button1).getVisibility()) + .isEqualTo(View.VISIBLE); + assertThat(mRootView.findViewById(R.id.button2).getVisibility()) + .isEqualTo(View.VISIBLE); + assertThat(mRootView.findViewById(R.id.button3).getVisibility()) + .isEqualTo(View.VISIBLE); + assertThat(mRootView.findViewById(R.id.button4).getVisibility()) + .isEqualTo(View.VISIBLE); + } + + @Test + public void onBindViewHolder_notSetTitleOrIcon_shouldNotShowButtonByDefault() { + mPref.onBindViewHolder(mHolder); + + assertThat(mRootView.findViewById(R.id.button1).getVisibility()) + .isEqualTo(View.GONE); + assertThat(mRootView.findViewById(R.id.button2).getVisibility()) + .isEqualTo(View.GONE); + assertThat(mRootView.findViewById(R.id.button3).getVisibility()) + .isEqualTo(View.GONE); + assertThat(mRootView.findViewById(R.id.button4).getVisibility()) + .isEqualTo(View.GONE); + } + + @Test + public void onBindViewHolder_setVisibleIsGoneAndSetTitle_shouldNotShowButton() { + mPref.setButton1Text(R.string.install_other_apps).setButton1Visible(false); + mPref.setButton2Text(R.string.install_other_apps).setButton2Visible(false); + mPref.setButton3Text(R.string.install_other_apps).setButton3Visible(false); + mPref.setButton4Text(R.string.install_other_apps).setButton4Visible(false); + + mPref.onBindViewHolder(mHolder); + + assertThat(mRootView.findViewById(R.id.button1).getVisibility()) + .isEqualTo(View.GONE); + assertThat(mRootView.findViewById(R.id.button2).getVisibility()) + .isEqualTo(View.GONE); + assertThat(mRootView.findViewById(R.id.button3).getVisibility()) + .isEqualTo(View.GONE); + assertThat(mRootView.findViewById(R.id.button4).getVisibility()) + .isEqualTo(View.GONE); + } + + @Test + public void onBindViewHolder_setVisibleIsGoneAndSetIcon_shouldNotShowButton() { + mPref.setButton1Icon(R.drawable.ic_plus).setButton1Visible(false); + mPref.setButton2Icon(R.drawable.ic_plus).setButton2Visible(false); + mPref.setButton3Icon(R.drawable.ic_plus).setButton3Visible(false); + mPref.setButton4Icon(R.drawable.ic_plus).setButton4Visible(false); + + mPref.onBindViewHolder(mHolder); + + assertThat(mRootView.findViewById(R.id.button1).getVisibility()) + .isEqualTo(View.GONE); + assertThat(mRootView.findViewById(R.id.button2).getVisibility()) + .isEqualTo(View.GONE); + assertThat(mRootView.findViewById(R.id.button3).getVisibility()) + .isEqualTo(View.GONE); + assertThat(mRootView.findViewById(R.id.button4).getVisibility()) + .isEqualTo(View.GONE); + } + + @Test + public void onBindViewHolder_setVisibility_shouldUpdateButtonVisibility() { + mPref.setButton1Text(R.string.install_other_apps).setButton1Visible(false); + mPref.setButton2Text(R.string.install_other_apps).setButton2Visible(false); + mPref.setButton3Text(R.string.install_other_apps).setButton3Visible(false); + mPref.setButton4Text(R.string.install_other_apps).setButton4Visible(false); + + mPref.onBindViewHolder(mHolder); + + assertThat(mRootView.findViewById(R.id.button1).getVisibility()) + .isEqualTo(View.GONE); + assertThat(mRootView.findViewById(R.id.button2).getVisibility()) + .isEqualTo(View.GONE); + assertThat(mRootView.findViewById(R.id.button3).getVisibility()) + .isEqualTo(View.GONE); + assertThat(mRootView.findViewById(R.id.button4).getVisibility()) + .isEqualTo(View.GONE); + + mPref.setButton1Visible(true); + mPref.setButton2Visible(true); + mPref.setButton3Visible(true); + mPref.setButton4Visible(true); + + mPref.onBindViewHolder(mHolder); + + assertThat(mRootView.findViewById(R.id.button1).getVisibility()) + .isEqualTo(View.VISIBLE); + assertThat(mRootView.findViewById(R.id.button2).getVisibility()) + .isEqualTo(View.VISIBLE); + assertThat(mRootView.findViewById(R.id.button3).getVisibility()) + .isEqualTo(View.VISIBLE); + assertThat(mRootView.findViewById(R.id.button4).getVisibility()) + .isEqualTo(View.VISIBLE); + } + + @Test + public void onBindViewHolder_setEnabled_shouldEnableButton() { + mPref.setButton1Enabled(true); + mPref.setButton2Enabled(false); + mPref.setButton3Enabled(true); + mPref.setButton4Enabled(false); + + mPref.onBindViewHolder(mHolder); + + assertThat(mRootView.findViewById(R.id.button1).isEnabled()).isTrue(); + assertThat(mRootView.findViewById(R.id.button2).isEnabled()).isFalse(); + assertThat(mRootView.findViewById(R.id.button3).isEnabled()).isTrue(); + assertThat(mRootView.findViewById(R.id.button4).isEnabled()).isFalse(); + } + + @Test + public void onBindViewHolder_setText_shouldShowSameText() { + mPref.setButton1Text(R.string.install_other_apps); + mPref.setButton2Text(R.string.install_other_apps); + mPref.setButton3Text(R.string.install_other_apps); + mPref.setButton4Text(R.string.install_other_apps); + + mPref.onBindViewHolder(mHolder); + + assertThat(((Button) mRootView.findViewById(R.id.button1)).getText()) + .isEqualTo(mContext.getText(R.string.install_other_apps)); + assertThat(((Button) mRootView.findViewById(R.id.button2)).getText()) + .isEqualTo(mContext.getText(R.string.install_other_apps)); + assertThat(((Button) mRootView.findViewById(R.id.button3)).getText()) + .isEqualTo(mContext.getText(R.string.install_other_apps)); + assertThat(((Button) mRootView.findViewById(R.id.button4)).getText()) + .isEqualTo(mContext.getText(R.string.install_other_apps)); + } + + @Test + public void onBindViewHolder_setButtonIcon_iconMustDisplayAboveText() { + mPref.setButton1Text(R.string.install_other_apps); + mPref.setButton1Icon(R.drawable.ic_plus); + + mPref.onBindViewHolder(mHolder); + final Drawable[] drawablesAroundText = + ((Button) mRootView.findViewById(R.id.button1)) + .getCompoundDrawables(); + + assertThat(drawablesAroundText[1 /* top */]).isNotNull(); + } + + @Test + public void setButtonIcon_iconResourceIdIsZero_shouldNotDisplayIcon() { + mPref.setButton1Text(R.string.install_other_apps); + mPref.setButton1Icon(0); + + mPref.onBindViewHolder(mHolder); + final Drawable[] drawablesAroundText = + ((Button) mRootView.findViewById(R.id.button1)) + .getCompoundDrawables(); + + assertThat(drawablesAroundText[1 /* top */]).isNull(); + } + + @Test + public void setButtonIcon_iconResourceIdNotExisting_shouldNotDisplayIconAndCrash() { + mPref.setButton1Text(R.string.install_other_apps); + mPref.setButton1Icon(999999999 /* not existing id */); + // Should not crash here + mPref.onBindViewHolder(mHolder); + final Drawable[] drawablesAroundText = + ((Button) mRootView.findViewById(R.id.button1)) + .getCompoundDrawables(); + + assertThat(drawablesAroundText[1 /* top */]).isNull(); + } + + public static ActionButtonsPreference createMock() { + final ActionButtonsPreference pref = mock(ActionButtonsPreference.class); + when(pref.setButton1Text(anyInt())).thenReturn(pref); + when(pref.setButton1Icon(anyInt())).thenReturn(pref); + when(pref.setButton1Enabled(anyBoolean())).thenReturn(pref); + when(pref.setButton1Visible(anyBoolean())).thenReturn(pref); + when(pref.setButton1OnClickListener(any(View.OnClickListener.class))).thenReturn(pref); + + when(pref.setButton2Text(anyInt())).thenReturn(pref); + when(pref.setButton2Icon(anyInt())).thenReturn(pref); + when(pref.setButton2Enabled(anyBoolean())).thenReturn(pref); + when(pref.setButton2Visible(anyBoolean())).thenReturn(pref); + when(pref.setButton2OnClickListener(any(View.OnClickListener.class))).thenReturn(pref); + + when(pref.setButton3Text(anyInt())).thenReturn(pref); + when(pref.setButton3Icon(anyInt())).thenReturn(pref); + when(pref.setButton3Enabled(anyBoolean())).thenReturn(pref); + when(pref.setButton3Visible(anyBoolean())).thenReturn(pref); + when(pref.setButton3OnClickListener(any(View.OnClickListener.class))).thenReturn(pref); + + when(pref.setButton4Text(anyInt())).thenReturn(pref); + when(pref.setButton4Icon(anyInt())).thenReturn(pref); + when(pref.setButton4Enabled(anyBoolean())).thenReturn(pref); + when(pref.setButton4Visible(anyBoolean())).thenReturn(pref); + when(pref.setButton4OnClickListener(any(View.OnClickListener.class))).thenReturn(pref); + return pref; + } +} 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/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index b2c12b277dc0..56b768feee23 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1914,6 +1914,15 @@ class SettingsProtoDumpUtil { SecureSettingsProto.Location.CHANGER); p.end(locationToken); + final long locationAccessCheckToken = p.start(SecureSettingsProto.LOCATION_ACCESS_CHECK); + dumpSetting(s, p, + Settings.Secure.LOCATION_ACCESS_CHECK_INTERVAL_MILLIS, + SecureSettingsProto.LocationAccessCheck.INTERVAL_MILLIS); + dumpSetting(s, p, + Settings.Secure.LOCATION_ACCESS_CHECK_DELAY_MILLIS, + SecureSettingsProto.LocationAccessCheck.DELAY_MILLIS); + p.end(locationAccessCheckToken); + final long lockScreenToken = p.start(SecureSettingsProto.LOCK_SCREEN); // Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS intentionally excluded since it's deprecated. // Settings.Secure.LOCK_PATTERN_ENABLED intentionally excluded since it's deprecated. diff --git a/packages/SystemUI/res/drawable/ic_notification_block.xml b/packages/SystemUI/res/drawable/ic_notification_block.xml index 572e97b6badc..276907401d5a 100644 --- a/packages/SystemUI/res/drawable/ic_notification_block.xml +++ b/packages/SystemUI/res/drawable/ic_notification_block.xml @@ -20,6 +20,6 @@ Copyright (C) 2016 The Android Open Source Project android:viewportHeight="24.0"> <path - android:fillColor="#FFFFFFFF" + android:fillColor="#FF000000" android:pathData="M12.0,2.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0zM4.0,12.0c0.0,-4.42 3.58,-8.0 8.0,-8.0 1.85,0.0 3.5,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4.0,13.85 4.0,12.0zm8.0,8.0c-1.85,0.0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20.0,10.15 20.0,12.0c0.0,4.42 -3.58,8.0 -8.0,8.0z"/> </vector> diff --git a/packages/SystemUI/res/layout/biometric_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml index 5ca34b033f0d..1e8cd5a7e13b 100644 --- a/packages/SystemUI/res/layout/biometric_dialog.xml +++ b/packages/SystemUI/res/layout/biometric_dialog.xml @@ -160,6 +160,15 @@ android:maxLines="2" android:text="@string/biometric_dialog_confirm" android:visibility="gone"/> + <!-- Try Again Button --> + <Button android:id="@+id/button_try_again" + android:layout_width="wrap_content" + android:layout_height="match_parent" + style="@*android:style/Widget.DeviceDefault.Button.Colored" + android:gravity="center" + android:maxLines="2" + android:text="@string/biometric_dialog_try_again" + android:visibility="gone"/> <Space android:id="@+id/rightSpacer" android:layout_width="12dip" android:layout_height="match_parent" diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index eb3f70adc48b..d502baa956b0 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -144,6 +144,7 @@ <!-- Settings and Done buttons --> <LinearLayout + android:id="@+id/block_or_minimize" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/notification_guts_button_spacing" @@ -178,13 +179,6 @@ android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing" style="@style/TextAppearance.NotificationInfo.Button" /> <TextView - android:id="@+id/toggle_silent" - android:text="@string/inline_silent_button_silent" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing" - style="@style/TextAppearance.NotificationInfo.Button" /> - <TextView android:id="@+id/keep" android:minWidth="48dp" android:text="@string/inline_keep_button" @@ -193,6 +187,44 @@ android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing" style="@style/TextAppearance.NotificationInfo.Button"/> </LinearLayout> + <LinearLayout + android:id="@+id/interruptiveness_settings" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/notification_guts_button_spacing" + android:layout_marginStart="@dimen/notification_guts_button_side_margin" + android:layout_marginEnd="@dimen/notification_guts_button_side_margin" + android:gravity="center" + android:orientation="horizontal" + android:visibility="gone"> + <TextView + android:id="@+id/int_block" + android:text="@string/inline_block_button" + android:layout_width="0dp" + android:layout_height="match_parent" + android:drawableTop="@drawable/ic_notification_block" + android:drawableTint="?android:attr/colorAccent" + android:layout_weight="1" + style="@style/TextAppearance.NotificationInfo.Button"/> + <TextView + android:id="@+id/int_silent" + android:text="@string/inline_minimize_button" + android:layout_width="0dp" + android:layout_height="match_parent" + android:drawableTop="@drawable/ic_notifications_silence" + android:drawableTint="?android:attr/colorAccent" + android:layout_weight="1" + style="@style/TextAppearance.NotificationInfo.Button"/> + <TextView + android:id="@+id/int_alert" + android:text="@string/inline_keep_button" + android:layout_width="0dp" + android:layout_height="match_parent" + android:drawableTop="@drawable/ic_notifications_alert" + android:drawableTint="?android:attr/colorAccent" + android:layout_weight="1" + style="@style/TextAppearance.NotificationInfo.Button"/> + </LinearLayout> </LinearLayout> <com.android.systemui.statusbar.notification.row.NotificationUndoLayout android:id="@+id/confirmation" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index c5654f01e5fc..9917257bba8a 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -272,6 +272,8 @@ <string name="accessibility_biometric_dialog_help_area">Help message area</string> <!-- Message shown when a biometric is authenticated, asking the user to confirm authentication [CHAR LIMIT=30] --> <string name="biometric_dialog_confirm">Confirm</string> + <!-- Button name on BiometricPrompt shown when a biometric is detected but not authenticated. Tapping the button resumes authentication [CHAR_LIMIT=30] --> + <string name="biometric_dialog_try_again">Try again</string> <!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication --> <string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string> @@ -700,6 +702,8 @@ <string name="quick_settings_bluetooth_secondary_label_headset">Headset</string> <!-- QuickSettings: Bluetooth secondary label for an input/IO device being connected [CHAR LIMIT=20]--> <string name="quick_settings_bluetooth_secondary_label_input">Input</string> + <!-- QuickSettings: Bluetooth secondary label for a Hearing Aids device being connected [CHAR LIMIT=20]--> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids">Hearing Aids</string> <!-- QuickSettings: Bluetooth secondary label shown when bluetooth is being enabled [CHAR LIMIT=NONE] --> <string name="quick_settings_bluetooth_secondary_label_transient">Turning on…</string> <!-- QuickSettings: Brightness [CHAR LIMIT=NONE] --> @@ -1556,17 +1560,26 @@ <!-- Notification inline controls: block notifications button --> <string name="inline_stop_button">Stop notifications</string> + <!-- Notification inline controls: button to block notifications from this channel [CHAR_LIMIT=35] --> + <string name="inline_block_button">Block</string> + <!-- Notification inline controls: keep getting notifications button --> <string name="inline_keep_button">Keep showing</string> <!-- Notification inline controls: minimize notifications button --> <string name="inline_minimize_button">Minimize</string> - <!-- Notification inline controls: show notifications silently button [CHAR_LIMIT=25] --> + <!-- Notification inline controls: button to show notifications silently, without alerting the user [CHAR_LIMIT=35] --> <string name="inline_silent_button_silent">Show silently</string> - <!-- Notification inline controls: show and alert button [CHAR_LIMIT=25] --> - <string name="inline_silent_button_alert">Show and alert</string> + <!-- Notification inline controls: button to continue showing notifications silently [CHAR_LIMIT=35] --> + <string name="inline_silent_button_stay_silent">Stay silent</string> + + <!-- Notification inline controls: button to make notifications alert the user [CHAR_LIMIT=35] --> + <string name="inline_silent_button_alert">Alert me</string> + + <!-- Notification inline controls: button to continue alerting the user when notifications arrive [CHAR_LIMIT=35] --> + <string name="inline_silent_button_keep_alerting">Keep alerting</string> <!-- Notification Inline controls: continue receiving notifications prompt, app level --> <string name="inline_keep_showing_app">Keep showing notifications from this app?</string> @@ -2255,6 +2268,7 @@ <!-- Text on chip for multiple apps using a single app op [CHAR LIMIT=10] --> <plurals name="ongoing_privacy_chip_multiple_apps"> + <item quantity="one"><xliff:g id="num_apps" example="1">%d</xliff:g> app</item> <item quantity="few"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</item> <item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</item> </plurals> @@ -2267,6 +2281,7 @@ <!-- Content description for ongoing privacy chip. Use with multiple apps using same app op[CHAR LIMIT=NONE]--> <plurals name="ongoing_privacy_chip_content_multiple_apps_single_op"> + <item quantity="one"><xliff:g id="num_apps" example="1">%1$d</xliff:g> application is using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item> <item quantity="few"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item> <item quantity="other"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item> </plurals> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index ece2bb9a9507..f3bdbae97e42 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -71,4 +71,9 @@ interface ISystemUiProxy { */ void onStatusBarMotionEvent(in MotionEvent event) = 9; + /** + * Get the corner radius of windows in pixels. + */ + float getWindowCornerRadius() = 10; + } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java index 65c52200a7d8..a9cf857c3e50 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java @@ -83,6 +83,7 @@ public class SyncRtSurfaceTransactionApplier { t.setWindowCrop(params.surface, params.windowCrop); t.setAlpha(params.surface, params.alpha); t.setLayer(params.surface, params.layer); + t.setCornerRadius(params.surface, params.cornerRadius); t.show(params.surface); } @@ -98,12 +99,13 @@ public class SyncRtSurfaceTransactionApplier { * @param windowCrop Crop to apply. */ public SurfaceParams(SurfaceControlCompat surface, float alpha, Matrix matrix, - Rect windowCrop, int layer) { + Rect windowCrop, int layer, float cornerRadius) { this.surface = surface.mSurfaceControl; this.alpha = alpha; this.matrix = new Matrix(matrix); this.windowCrop = new Rect(windowCrop); this.layer = layer; + this.cornerRadius = cornerRadius; } final SurfaceControl surface; @@ -111,5 +113,6 @@ public class SyncRtSurfaceTransactionApplier { final Matrix matrix; final Rect windowCrop; final int layer; + final float cornerRadius; } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java index ff9e84ca29c1..8a251ae51f38 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java @@ -18,6 +18,7 @@ package com.android.systemui.shared.system; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID; import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; @@ -61,7 +62,7 @@ public class WindowManagerWrapper { public static final int TRANSIT_KEYGUARD_OCCLUDE = WindowManager.TRANSIT_KEYGUARD_OCCLUDE; public static final int TRANSIT_KEYGUARD_UNOCCLUDE = WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; - public static final int NAV_BAR_POS_INVALID = -1; + public static final int NAV_BAR_POS_INVALID = NAV_BAR_INVALID; public static final int NAV_BAR_POS_LEFT = NAV_BAR_LEFT; public static final int NAV_BAR_POS_RIGHT = NAV_BAR_RIGHT; public static final int NAV_BAR_POS_BOTTOM = NAV_BAR_BOTTOM; @@ -178,10 +179,9 @@ public class WindowManagerWrapper { * @see #NAV_BAR_POS_BOTTOM * @see #NAV_BAR_POS_INVALID */ - public int getNavBarPosition() { + public int getNavBarPosition(int displayId) { try { - // TODO: Use WindowManagerService.getNavBarPosition(int displayId) - return WindowManagerGlobal.getWindowManagerService().getNavBarPosition(); + return WindowManagerGlobal.getWindowManagerService().getNavBarPosition(displayId); } catch (RemoteException e) { Log.w(TAG, "Failed to get nav bar position"); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java index c0047c015813..a90a7d231dc1 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java @@ -21,7 +21,7 @@ import android.content.pm.PackageManager; import android.content.res.Configuration; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricPrompt; -import android.hardware.biometrics.IBiometricPromptReceiver; +import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -52,15 +52,20 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba private static final int MSG_BUTTON_NEGATIVE = 6; private static final int MSG_USER_CANCELED = 7; private static final int MSG_BUTTON_POSITIVE = 8; + private static final int MSG_BIOMETRIC_SHOW_TRY_AGAIN = 9; + private static final int MSG_TRY_AGAIN_PRESSED = 10; private Map<Integer, BiometricDialogView> mDialogs; // BiometricAuthenticator type, view private SomeArgs mCurrentDialogArgs; private BiometricDialogView mCurrentDialog; private WindowManager mWindowManager; - private IBiometricPromptReceiver mReceiver; + private IBiometricServiceReceiverInternal mReceiver; private boolean mDialogShowing; private Callback mCallback = new Callback(); + private boolean mTryAgainShowing; // No good place to save state before config change :/ + private boolean mConfirmShowing; // No good place to save state before config change :/ + private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -89,6 +94,15 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba case MSG_BUTTON_POSITIVE: handleButtonPositive(); break; + case MSG_BIOMETRIC_SHOW_TRY_AGAIN: + handleShowTryAgain(); + break; + case MSG_TRY_AGAIN_PRESSED: + handleTryAgainPressed(); + break; + default: + Log.w(TAG, "Unknown message: " + msg.what); + break; } } }; @@ -96,7 +110,7 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba private class Callback implements DialogViewCallback { @Override public void onUserCanceled() { - mHandler.obtainMessage(BiometricDialogImpl.MSG_USER_CANCELED).sendToTarget(); + mHandler.obtainMessage(MSG_USER_CANCELED).sendToTarget(); } @Override @@ -107,12 +121,17 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba @Override public void onNegativePressed() { - mHandler.obtainMessage(BiometricDialogImpl.MSG_BUTTON_NEGATIVE).sendToTarget(); + mHandler.obtainMessage(MSG_BUTTON_NEGATIVE).sendToTarget(); } @Override public void onPositivePressed() { - mHandler.obtainMessage(BiometricDialogImpl.MSG_BUTTON_POSITIVE).sendToTarget(); + mHandler.obtainMessage(MSG_BUTTON_POSITIVE).sendToTarget(); + } + + @Override + public void onTryAgainPressed() { + mHandler.obtainMessage(MSG_TRY_AGAIN_PRESSED).sendToTarget(); } } @@ -139,13 +158,14 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba } @Override - public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type, - boolean requireConfirmation, int userId) { + public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver, + int type, boolean requireConfirmation, int userId) { if (DEBUG) Log.d(TAG, "showBiometricDialog, type: " + type); // Remove these messages as they are part of the previous client mHandler.removeMessages(MSG_BIOMETRIC_ERROR); mHandler.removeMessages(MSG_BIOMETRIC_HELP); mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED); + mHandler.removeMessages(MSG_HIDE_DIALOG); SomeArgs args = SomeArgs.obtain(); args.arg1 = bundle; args.arg2 = receiver; @@ -179,6 +199,12 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba mHandler.obtainMessage(MSG_HIDE_DIALOG, false /* userCanceled */).sendToTarget(); } + @Override + public void showBiometricTryAgain() { + if (DEBUG) Log.d(TAG, "showBiometricTryAgain"); + mHandler.obtainMessage(MSG_BIOMETRIC_SHOW_TRY_AGAIN).sendToTarget(); + } + private void handleShowDialog(SomeArgs args, boolean skipAnimation) { mCurrentDialogArgs = args; final int type = args.argi1; @@ -193,11 +219,13 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba Log.w(TAG, "Dialog already showing"); return; } - mReceiver = (IBiometricPromptReceiver) args.arg2; + mReceiver = (IBiometricServiceReceiverInternal) args.arg2; mCurrentDialog.setBundle((Bundle)args.arg1); mCurrentDialog.setRequireConfirmation((boolean) args.arg3); mCurrentDialog.setUserId(args.argi2); mCurrentDialog.setSkipIntro(skipAnimation); + mCurrentDialog.setPendingTryAgain(mTryAgainShowing); + mCurrentDialog.setPendingConfirm(mConfirmShowing); mWindowManager.addView(mCurrentDialog, mCurrentDialog.getLayoutParams()); mDialogShowing = true; } @@ -209,7 +237,8 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba mContext.getResources() .getText(mCurrentDialog.getAuthenticatedAccessibilityResourceId())); if (mCurrentDialog.requiresConfirmation()) { - mCurrentDialog.showConfirmationButton(); + mConfirmShowing = true; + mCurrentDialog.showConfirmationButton(true /* show */); } else { handleHideDialog(false /* userCanceled */); } @@ -226,6 +255,7 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba if (DEBUG) Log.d(TAG, "Dialog already dismissed"); return; } + mTryAgainShowing = false; mCurrentDialog.showErrorMessage(error); } @@ -246,6 +276,8 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba } mReceiver = null; mDialogShowing = false; + mConfirmShowing = false; + mTryAgainShowing = false; mCurrentDialog.startDismiss(); } @@ -259,6 +291,7 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba } catch (RemoteException e) { Log.e(TAG, "Remote exception when handling negative button", e); } + mTryAgainShowing = false; handleHideDialog(false /* userCanceled */); } @@ -272,13 +305,31 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba } catch (RemoteException e) { Log.e(TAG, "Remote exception when handling positive button", e); } + mConfirmShowing = false; handleHideDialog(false /* userCanceled */); } private void handleUserCanceled() { + mTryAgainShowing = false; + mConfirmShowing = false; handleHideDialog(true /* userCanceled */); } + private void handleShowTryAgain() { + mCurrentDialog.showTryAgainButton(true /* show */); + mTryAgainShowing = true; + } + + private void handleTryAgainPressed() { + try { + mCurrentDialog.clearTemporaryMessage(); + mTryAgainShowing = false; + mReceiver.onTryAgainPressed(); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException when handling try again", e); + } + } + @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java index 38427adfd42a..e085f2368214 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java @@ -87,6 +87,9 @@ public abstract class BiometricDialogView extends LinearLayout { protected boolean mRequireConfirmation; private int mUserId; // used to determine if we should show work background + private boolean mPendingShowTryAgain; + private boolean mPendingShowConfirm; + protected abstract void updateIcon(int lastState, int newState); protected abstract int getHintStringResourceId(); protected abstract int getAuthenticatedAccessibilityResourceId(); @@ -178,6 +181,7 @@ public abstract class BiometricDialogView extends LinearLayout { final Button negative = mLayout.findViewById(R.id.button2); final Button positive = mLayout.findViewById(R.id.button1); final ImageView icon = mLayout.findViewById(R.id.biometric_icon); + final Button tryAgain = mLayout.findViewById(R.id.button_try_again); icon.setContentDescription(getResources().getString(getIconDescriptionResourceId())); @@ -193,6 +197,11 @@ public abstract class BiometricDialogView extends LinearLayout { mCallback.onPositivePressed(); }); + tryAgain.setOnClickListener((View v) -> { + showTryAgainButton(false /* show */); + mCallback.onTryAgainPressed(); + }); + mLayout.setFocusableInTouchMode(true); mLayout.requestFocus(); } @@ -207,7 +216,6 @@ public abstract class BiometricDialogView extends LinearLayout { final TextView subtitle = mLayout.findViewById(R.id.subtitle); final TextView description = mLayout.findViewById(R.id.description); final Button negative = mLayout.findViewById(R.id.button2); - final Button positive = mLayout.findViewById(R.id.button1); final ImageView backgroundView = mLayout.findViewById(R.id.background); if (mUserManager.isManagedProfile(mUserId)) { @@ -233,8 +241,6 @@ public abstract class BiometricDialogView extends LinearLayout { title.setText(titleText); title.setSelected(true); - positive.setVisibility(View.INVISIBLE); - final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE); if (TextUtils.isEmpty(subtitleText)) { subtitle.setVisibility(View.GONE); @@ -243,7 +249,8 @@ public abstract class BiometricDialogView extends LinearLayout { subtitle.setText(subtitleText); } - final CharSequence descriptionText = mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION); + final CharSequence descriptionText = + mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION); if (TextUtils.isEmpty(descriptionText)) { description.setVisibility(View.GONE); } else { @@ -253,6 +260,9 @@ public abstract class BiometricDialogView extends LinearLayout { negative.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT)); + showTryAgainButton(mPendingShowTryAgain); + showConfirmationButton(mPendingShowConfirm); + if (mWasForceRemoved || mSkipIntro) { // Show the dialog immediately mLayout.animate().cancel(); @@ -281,11 +291,17 @@ public abstract class BiometricDialogView extends LinearLayout { public void startDismiss() { mAnimatingAway = true; + // This is where final cleanup should occur. final Runnable endActionRunnable = new Runnable() { @Override public void run() { mWindowManager.removeView(BiometricDialogView.this); mAnimatingAway = false; + // Set the icons / text back to normal state + handleClearMessage(); + showTryAgainButton(false /* show */); + mPendingShowTryAgain = false; + mPendingShowConfirm = false; } }; @@ -345,9 +361,13 @@ public abstract class BiometricDialogView extends LinearLayout { return mRequireConfirmation; } - public void showConfirmationButton() { + public void showConfirmationButton(boolean show) { final Button positive = mLayout.findViewById(R.id.button1); - positive.setVisibility(View.VISIBLE); + if (show) { + positive.setVisibility(View.VISIBLE); + } else { + positive.setVisibility(View.GONE); + } } public void setUserId(int userId) { @@ -376,12 +396,18 @@ public abstract class BiometricDialogView extends LinearLayout { BiometricPrompt.HIDE_DIALOG_DELAY); } + public void clearTemporaryMessage() { + mHandler.removeMessages(MSG_CLEAR_MESSAGE); + mHandler.obtainMessage(MSG_CLEAR_MESSAGE).sendToTarget(); + } + public void showHelpMessage(String message) { showTemporaryMessage(message); } public void showErrorMessage(String error) { showTemporaryMessage(error); + showTryAgainButton(false /* show */); mCallback.onErrorShown(); } @@ -390,6 +416,25 @@ public abstract class BiometricDialogView extends LinearLayout { mLastState = newState; } + public void showTryAgainButton(boolean show) { + final Button tryAgain = mLayout.findViewById(R.id.button_try_again); + if (show) { + tryAgain.setVisibility(View.VISIBLE); + } else { + tryAgain.setVisibility(View.GONE); + } + } + + // Set the state before the window is attached, so we know if the dialog should be started + // with or without the button. This is because there's no good onPause signal + public void setPendingTryAgain(boolean show) { + mPendingShowTryAgain = show; + } + + public void setPendingConfirm(boolean show) { + mPendingShowConfirm = show; + } + public WindowManager.LayoutParams getLayoutParams() { final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java index f388d9c67718..24fd22e2ee80 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java @@ -43,4 +43,9 @@ public interface DialogViewCallback { * should be dismissed. */ void onPositivePressed(); + + /** + * Invoked when the "try again" button is pressed. + */ + void onTryAgainPressed(); } 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/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/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java index 591e9e015897..9d2be39b28fc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java @@ -26,6 +26,7 @@ import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Handler; +import android.provider.Settings; import android.service.quicksettings.TileService; import android.text.TextUtils; import android.util.ArraySet; @@ -69,7 +70,8 @@ public class TileQueryHelper { mSpecs.clear(); mFinished = false; // Enqueue jobs to fetch every system tile and then ever package tile. - addStockTiles(host); + addCurrentAndStockTiles(host); + addPackageTiles(host); } @@ -77,16 +79,28 @@ public class TileQueryHelper { return mFinished; } - private void addStockTiles(QSTileHost host) { - String possible = mContext.getString(R.string.quick_settings_tiles_stock); + private void addCurrentAndStockTiles(QSTileHost host) { + String stock = mContext.getString(R.string.quick_settings_tiles_stock); + String current = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.QS_TILES); final ArrayList<String> possibleTiles = new ArrayList<>(); - possibleTiles.addAll(Arrays.asList(possible.split(","))); - if (Build.IS_DEBUGGABLE) { + if (current != null) { + // The setting QS_TILES is not populated immediately upon Factory Reset + possibleTiles.addAll(Arrays.asList(current.split(","))); + } + String[] stockSplit = stock.split(","); + for (String spec : stockSplit) { + if (!current.contains(spec)) { + possibleTiles.add(spec); + } + } + if (Build.IS_DEBUGGABLE && !current.contains(GarbageMonitor.MemoryTile.TILE_SPEC)) { possibleTiles.add(GarbageMonitor.MemoryTile.TILE_SPEC); } final ArrayList<QSTile> tilesToAdd = new ArrayList<>(); for (String spec : possibleTiles) { + // Only add current and stock tiles that can be created from QSFactoryImpl final QSTile tile = host.createTile(spec); if (tile == null) { continue; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java index b2f60436fecd..7d52f0b2763d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -114,7 +114,7 @@ public class QSFactoryImpl implements QSFactory { } // Broken tiles. - Log.w(TAG, "Bad tile spec: " + tileSpec); + Log.w(TAG, "No stock tile spec: " + tileSpec); return null; } 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/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index c62a592be8e2..3ab1c21b5066 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -205,7 +205,10 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { } else { final BluetoothClass bluetoothClass = lastDevice.getBtClass(); if (bluetoothClass != null) { - if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) { + if (lastDevice.isHearingAidDevice()) { + return mContext.getString( + R.string.quick_settings_bluetooth_secondary_label_hearing_aids); + } else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) { return mContext.getString( R.string.quick_settings_bluetooth_secondary_label_audio); } else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 1b89324209de..12b6f673de1c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -43,6 +43,7 @@ import android.provider.Settings; import android.util.Log; import android.view.MotionEvent; +import com.android.internal.policy.ScreenDecorationsUtils; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.Prefs; @@ -97,6 +98,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private int mCurrentBoundedUserId = -1; private float mBackButtonAlpha; private MotionEvent mStatusBarGestureDownEvent; + private float mWindowCornerRadius; private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() { @@ -228,6 +230,18 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } + public float getWindowCornerRadius() { + if (!verifyCaller("getWindowCornerRadius")) { + return 0; + } + long token = Binder.clearCallingIdentity(); + try { + return mWindowCornerRadius; + } finally { + Binder.restoreCallingIdentity(token); + } + } + private boolean verifyCaller(String reason) { final int callerId = Binder.getCallingUserHandle().getIdentifier(); if (callerId != mCurrentBoundedUserId) { @@ -334,6 +348,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis .setPackage(mRecentsComponentName.getPackageName()); mInteractionFlags = Prefs.getInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS, getDefaultInteractionFlags()); + mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources()); // Listen for the package update changes. if (mDeviceProvisionedController.getCurrentUser() == UserHandle.USER_SYSTEM) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 0c8f48752455..8b9399536969 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -21,7 +21,7 @@ import static com.android.systemui.statusbar.phone.StatusBar.ONLY_CORE_APPS; import android.app.StatusBarManager; import android.content.ComponentName; import android.graphics.Rect; -import android.hardware.biometrics.IBiometricPromptReceiver; +import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -96,6 +96,7 @@ public class CommandQueue extends IStatusBar.Stub { private static final int MSG_SHOW_CHARGING_ANIMATION = 44 << MSG_SHIFT; private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT; private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT; + private static final int MSG_BIOMETRIC_TRY_AGAIN = 47 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -163,12 +164,13 @@ public class CommandQueue extends IStatusBar.Stub { default void onRotationProposal(int rotation, boolean isValid) { } - default void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, + default void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver, int type, boolean requireConfirmation, int userId) { } default void onBiometricAuthenticated() { } default void onBiometricHelp(String message) { } default void onBiometricError(String error) { } default void hideBiometricDialog() { } + default void showBiometricTryAgain() { } } @VisibleForTesting @@ -523,8 +525,8 @@ public class CommandQueue extends IStatusBar.Stub { } @Override - public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type, - boolean requireConfirmation, int userId) { + public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver, + int type, boolean requireConfirmation, int userId) { synchronized (mLock) { SomeArgs args = SomeArgs.obtain(); args.arg1 = bundle; @@ -565,6 +567,13 @@ public class CommandQueue extends IStatusBar.Stub { } } + @Override + public void showBiometricTryAgain() { + synchronized (mLock) { + mHandler.obtainMessage(MSG_BIOMETRIC_TRY_AGAIN).sendToTarget(); + } + } + private final class H extends Handler { private H(Looper l) { super(l); @@ -774,7 +783,7 @@ public class CommandQueue extends IStatusBar.Stub { for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).showBiometricDialog( (Bundle) someArgs.arg1, - (IBiometricPromptReceiver) someArgs.arg2, + (IBiometricServiceReceiverInternal) someArgs.arg2, someArgs.argi1 /* type */, (boolean) someArgs.arg3 /* requireConfirmation */, someArgs.argi2 /* userId */); @@ -816,6 +825,11 @@ public class CommandQueue extends IStatusBar.Stub { mCallbacks.get(i).showPinningEscapeToast(); } break; + case MSG_BIOMETRIC_TRY_AGAIN: + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).showBiometricTryAgain(); + } + break; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java index f0e5462bc167..f506753379a4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java @@ -29,6 +29,7 @@ import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; +import com.android.internal.policy.ScreenDecorationsUtils; import com.android.systemui.Interpolators; import com.android.systemui.shared.system.SurfaceControlCompat; import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier; @@ -55,6 +56,7 @@ public class ActivityLaunchAnimator { private final NotificationPanelView mNotificationPanel; private final NotificationListContainer mNotificationContainer; private final StatusBarWindowView mStatusBarWindow; + private final float mWindowCornerRadius; private Callback mCallback; private final Runnable mTimeoutRunnable = () -> { setAnimationPending(false); @@ -72,6 +74,8 @@ public class ActivityLaunchAnimator { mNotificationContainer = container; mStatusBarWindow = statusBarWindow; mCallback = callback; + mWindowCornerRadius = ScreenDecorationsUtils + .getWindowCornerRadius(statusBarWindow.getResources()); } public RemoteAnimationAdapter getLaunchAnimation( @@ -124,6 +128,8 @@ public class ActivityLaunchAnimator { private final ExpandableNotificationRow mSourceNotification; private final ExpandAnimationParameters mParams; private final Rect mWindowCrop = new Rect(); + private final float mNotificationCornerRadius; + private float mCornerRadius; private boolean mIsFullScreenLaunch = true; private final SyncRtSurfaceTransactionApplier mSyncRtTransactionApplier; @@ -131,6 +137,8 @@ public class ActivityLaunchAnimator { mSourceNotification = sourceNofitication; mParams = new ExpandAnimationParameters(); mSyncRtTransactionApplier = new SyncRtSurfaceTransactionApplier(mSourceNotification); + mNotificationCornerRadius = Math.max(mSourceNotification.getCurrentTopRoundness(), + mSourceNotification.getCurrentBottomRoundness()); } @Override @@ -181,8 +189,7 @@ public class ActivityLaunchAnimator { @Override public void onAnimationUpdate(ValueAnimator animation) { mParams.linearProgress = animation.getAnimatedFraction(); - float progress - = Interpolators.FAST_OUT_SLOW_IN.getInterpolation( + float progress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation( mParams.linearProgress); int newWidth = (int) MathUtils.lerp(notificationWidth, targetWidth, progress); @@ -194,6 +201,8 @@ public class ActivityLaunchAnimator { + notificationHeight, primary.position.y + primary.sourceContainerBounds.bottom, progress); + mCornerRadius = MathUtils.lerp(mNotificationCornerRadius, + mWindowCornerRadius, progress); applyParamsToWindow(primary); applyParamsToNotification(mParams); applyParamsToNotificationList(mParams); @@ -259,7 +268,7 @@ public class ActivityLaunchAnimator { m.postTranslate(0, (float) (mParams.top - app.position.y)); mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight()); SurfaceParams params = new SurfaceParams(new SurfaceControlCompat(app.leash), - 1f /* alpha */, m, mWindowCrop, app.prefixOrderIndex); + 1f /* alpha */, m, mWindowCrop, app.prefixOrderIndex, mCornerRadius); mSyncRtTransactionApplier.scheduleApply(params); } 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/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java index d7680b3523ba..3f8583c6241b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java @@ -66,7 +66,6 @@ import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGuts; import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; @@ -174,7 +173,6 @@ public class NotificationData { public void populateFromRanking(@NonNull Ranking ranking) { channel = ranking.getChannel(); audiblyAlerted = ranking.audiblyAlerted(); - noisy = ranking.isNoisy(); importance = ranking.getImportance(); snoozeCriteria = ranking.getSnoozeCriteria(); userSentiment = ranking.getUserSentiment(); 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 7970f166d8a6..1616b6dc53de 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -176,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. @@ -193,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) { 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 92d1b452bf44..689d6d530308 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 @@ -492,12 +492,12 @@ public class NotificationContentView extends FrameLayout { if (child == null) { mExpandedChild = null; mExpandedWrapper = null; - if (mVisibleType == VISIBLE_TYPE_EXPANDED) { - mVisibleType = VISIBLE_TYPE_CONTRACTED; - } if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) { mTransformationStartVisibleType = UNDEFINED; } + if (mVisibleType == VISIBLE_TYPE_EXPANDED) { + selectLayout(false /* animate */, true /* force */); + } return; } addView(child); @@ -530,12 +530,12 @@ public class NotificationContentView extends FrameLayout { if (child == null) { mHeadsUpChild = null; mHeadsUpWrapper = null; - if (mVisibleType == VISIBLE_TYPE_HEADSUP) { - mVisibleType = VISIBLE_TYPE_CONTRACTED; - } if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) { mTransformationStartVisibleType = UNDEFINED; } + if (mVisibleType == VISIBLE_TYPE_HEADSUP) { + selectLayout(false /* animate */, true /* force */); + } return; } addView(child); @@ -557,12 +557,12 @@ public class NotificationContentView extends FrameLayout { if (child == null) { mAmbientChild = null; mAmbientWrapper = null; - if (mVisibleType == VISIBLE_TYPE_AMBIENT) { - mVisibleType = VISIBLE_TYPE_CONTRACTED; - } if (mTransformationStartVisibleType == VISIBLE_TYPE_AMBIENT) { mTransformationStartVisibleType = UNDEFINED; } + if (mVisibleType == VISIBLE_TYPE_AMBIENT) { + selectLayout(false /* animate */, true /* force */); + } return; } addView(child); 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 3b407b5f8a65..3dc50ae55c56 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 @@ -290,7 +290,6 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx row.getIsNonblockable(), isForBlockingHelper, row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE, - row.getEntry().noisy, 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 213ac704b06a..5329541db92a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -104,7 +104,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private AnimatorSet mExpandAnimation; private boolean mIsForeground; private boolean mIsDeviceProvisioned; - private boolean mIsNoisy; private CheckSaveListener mCheckSaveListener; private OnSettingsClickListener mOnSettingsClickListener; @@ -186,13 +185,12 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G final OnAppSettingsClickListener onAppSettingsClick, boolean isDeviceProvisioned, boolean isNonblockable, - boolean isNoisy, int importance) throws RemoteException { bindNotification(pm, iNotificationManager, pkg, notificationChannel, numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick, onAppSettingsClick, isDeviceProvisioned, isNonblockable, - false /* isBlockingHelper */, false /* isUserSentimentNegative */, isNoisy, + false /* isBlockingHelper */, false /* isUserSentimentNegative */, importance); } @@ -210,7 +208,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G boolean isNonblockable, boolean isForBlockingHelper, boolean isUserSentimentNegative, - boolean isNoisy, int importance) throws RemoteException { mINotificationManager = iNotificationManager; @@ -236,7 +233,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mAppUid = mSbn.getUid(); mDelegatePkg = mSbn.getOpPkg(); mIsDeviceProvisioned = isDeviceProvisioned; - mIsNoisy = isNoisy; int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage( pkg, mAppUid, false /* includeDeleted */); @@ -411,54 +407,74 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } private void bindButtons() { - // Set up stay-in-notification actions - View block = findViewById(R.id.block); - TextView keep = findViewById(R.id.keep); - TextView silent = findViewById(R.id.toggle_silent); - View minimize = findViewById(R.id.minimize); - findViewById(R.id.undo).setOnClickListener(mOnUndo); - block.setOnClickListener(mOnStopOrMinimizeNotifications); - keep.setOnClickListener(mOnKeepShowing); - silent.setOnClickListener(mOnToggleSilent); - minimize.setOnClickListener(mOnStopOrMinimizeNotifications); - if (mIsNonblockable) { - keep.setText(android.R.string.ok); - block.setVisibility(GONE); - silent.setVisibility(GONE); - minimize.setVisibility(GONE); - } else if (mIsForeground) { - block.setVisibility(GONE); - silent.setVisibility(GONE); - minimize.setVisibility(VISIBLE); - } else { - block.setVisibility(VISIBLE); - boolean showToggleSilent = mIsNoisy - && NotificationUtils.useNewInterruptionModel(mContext); - silent.setVisibility(showToggleSilent ? VISIBLE : GONE); + boolean showInterruptivenessSettings = + !mIsNonblockable + && !mIsForeground + && !mIsForBlockingHelper + && NotificationUtils.useNewInterruptionModel(mContext); + if (showInterruptivenessSettings) { + findViewById(R.id.block_or_minimize).setVisibility(GONE); + findViewById(R.id.interruptiveness_settings).setVisibility(VISIBLE); + View block = findViewById(R.id.int_block); + TextView silent = findViewById(R.id.int_silent); + TextView alert = findViewById(R.id.int_alert); + boolean isCurrentlyAlerting = mStartingChannelOrNotificationImportance >= IMPORTANCE_DEFAULT; - silent.setText(isCurrentlyAlerting - ? R.string.inline_silent_button_silent - : R.string.inline_silent_button_alert); - minimize.setVisibility(GONE); - } - // Set up app settings link (i.e. Customize) - TextView settingsLinkView = findViewById(R.id.app_settings); - Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName, mSingleNotificationChannel, - mSbn.getId(), mSbn.getTag()); - if (!mIsForBlockingHelper - && settingsIntent != null - && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) { - settingsLinkView.setVisibility(VISIBLE); - settingsLinkView.setText(mContext.getString(R.string.notification_app_settings)); - settingsLinkView.setOnClickListener((View view) -> { - mAppSettingsClickListener.onClick(view, settingsIntent); - }); + block.setOnClickListener(mOnStopOrMinimizeNotifications); + if (isCurrentlyAlerting) { + silent.setOnClickListener(mOnToggleSilent); + silent.setText(R.string.inline_silent_button_silent); + alert.setOnClickListener(mOnKeepShowing); + alert.setText(R.string.inline_silent_button_keep_alerting); + } else { + silent.setOnClickListener(mOnKeepShowing); + silent.setText(R.string.inline_silent_button_stay_silent); + alert.setOnClickListener(mOnToggleSilent); + alert.setText(R.string.inline_silent_button_alert); + } } else { - settingsLinkView.setVisibility(View.GONE); + findViewById(R.id.block_or_minimize).setVisibility(VISIBLE); + findViewById(R.id.interruptiveness_settings).setVisibility(GONE); + View block = findViewById(R.id.block); + TextView keep = findViewById(R.id.keep); + View minimize = findViewById(R.id.minimize); + + block.setOnClickListener(mOnStopOrMinimizeNotifications); + keep.setOnClickListener(mOnKeepShowing); + minimize.setOnClickListener(mOnStopOrMinimizeNotifications); + + if (mIsNonblockable) { + keep.setText(android.R.string.ok); + block.setVisibility(GONE); + minimize.setVisibility(GONE); + } else if (mIsForeground) { + block.setVisibility(GONE); + minimize.setVisibility(VISIBLE); + } else { + block.setVisibility(VISIBLE); + minimize.setVisibility(GONE); + } + + // Set up app settings link (i.e. Customize) + TextView settingsLinkView = findViewById(R.id.app_settings); + Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName, + mSingleNotificationChannel, + mSbn.getId(), mSbn.getTag()); + if (!mIsForBlockingHelper + && settingsIntent != null + && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) { + settingsLinkView.setVisibility(VISIBLE); + settingsLinkView.setText(mContext.getString(R.string.notification_app_settings)); + settingsLinkView.setOnClickListener((View view) -> { + mAppSettingsClickListener.onClick(view, settingsIntent); + }); + } else { + settingsLinkView.setVisibility(View.GONE); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java index 713bd90b30c3..fef28cf1393e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java @@ -375,12 +375,9 @@ public class StackStateAnimator { // Find the amount to translate up. This is needed in order to understand the // direction of the remove animation (either downwards or upwards) - ExpandableViewState viewState = - ((ExpandableView) event.viewAfterChangingView).getViewState(); - int actualHeight = changingView.getActualHeight(); // upwards by default float translationDirection = -1.0f; - if (viewState != null) { + if (event.viewAfterChangingView != null) { float ownPosition = changingView.getTranslationY(); if (changingView instanceof ExpandableNotificationRow && event.viewAfterChangingView instanceof ExpandableNotificationRow) { @@ -396,8 +393,11 @@ public class StackStateAnimator { ownPosition = changingRow.getTranslationWhenRemoved(); } } + int actualHeight = changingView.getActualHeight(); // there was a view after this one, Approximate the distance the next child // travelled + ExpandableViewState viewState = + ((ExpandableView) event.viewAfterChangingView).getViewState(); translationDirection = ((viewState.yTranslation - (ownPosition + actualHeight / 2.0f)) * 2 / actualHeight); 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 33d022c83c16..cd6e1d794428 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone; import static android.view.MotionEvent.ACTION_DOWN; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID; import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB; import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON; @@ -950,10 +951,10 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav private void updateTaskSwitchHelper() { if (mGestureHelper == null) return; boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL); - int navBarPos = 0; + int navBarPos = NAV_BAR_INVALID; try { - // TODO: Use WindowManagerService.getNavBarPosition(int displayId) - navBarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition(); + navBarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition( + mDisplay.getDisplayId()); } catch (RemoteException e) { Slog.e(TAG, "Failed to get nav bar position.", e); } 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 497fdfb2deb1..43e86d66b8be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone; import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID; import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; @@ -421,6 +422,9 @@ public class QuickStepController implements GestureHelper { mDragHPositive = !isRTL; mDragVPositive = true; break; + case NAV_BAR_INVALID: + Log.e(TAG, "Invalid nav bar position"); + break; } for (NavigationGestureAction action: mGestureActions) { 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 2fc3adc4c98f..cfd53be8979b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -721,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. } @@ -1211,7 +1211,8 @@ public class StatusBar extends SystemUI implements DemoMode, } int dockSide = WindowManagerProxy.getInstance().getDockSide(); if (dockSide == WindowManager.DOCKED_INVALID) { - final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(); + final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition( + mDisplay.getDisplayId()); if (navbarPos == NAV_BAR_POS_INVALID) { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java index 62b6d91fc192..158ee8ae8fbd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java @@ -283,7 +283,6 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat applyModalFlag(state); applyBrightness(state); applyHasTopUi(state); - applySleepToken(state); applyNotTouchable(state); if (mLp.copyFrom(mLpChanged) != 0) { mWindowManager.updateViewLayout(mStatusBarView, mLp); @@ -328,14 +327,6 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat mHasTopUiChanged = isExpanded(state); } - private void applySleepToken(State state) { - if (state.dozing) { - mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN; - } else { - mLpChanged.privateFlags &= ~LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN; - } - } - private void applyNotTouchable(State state) { if (state.notTouchable) { mLpChanged.flags |= LayoutParams.FLAG_NOT_TOUCHABLE; 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/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java index f63d2360d976..26fa20de4e86 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java @@ -14,41 +14,76 @@ package com.android.systemui.qs.customize; -import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import android.content.pm.PackageManager; +import android.provider.Settings; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import android.text.TextUtils; +import android.util.ArraySet; import com.android.systemui.Dependency; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.QSTileHost; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper public class TileQueryHelperTest extends SysuiTestCase { - @Mock private TileQueryHelper.TileStateListener mListener; - @Mock private QSTileHost mQSTileHost; + private static final String CURRENT_TILES = "wifi,dnd,nfc"; + private static final String ONLY_STOCK_TILES = "wifi,dnd"; + private static final String WITH_OTHER_TILES = "wifi,dnd,other"; + // Note no nfc in stock tiles + private static final String STOCK_TILES = "wifi,dnd,cell,battery"; + private static final String ALL_TILES = "wifi,dnd,nfc,cell,battery"; + private static final Set<String> FACTORY_TILES = new ArraySet<>(); + + static { + FACTORY_TILES.addAll(Arrays.asList( + new String[]{"wifi", "bt", "cell", "dnd", "inversion", "airplane", "work", + "rotation", "flashlight", "location", "cast", "hotspot", "user", "battery", + "saver", "night", "nfc"})); + } - private TestableLooper mBGLooper; + @Mock + private TileQueryHelper.TileStateListener mListener; + @Mock + private QSTileHost mQSTileHost; + @Mock + private PackageManager mPackageManager; + private QSTile.State mState; + private TestableLooper mBGLooper; private TileQueryHelper mTileQueryHelper; @Before @@ -56,6 +91,23 @@ public class TileQueryHelperTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mBGLooper = TestableLooper.get(this); mDependency.injectTestDependency(Dependency.BG_LOOPER, mBGLooper.getLooper()); + mContext.setMockPackageManager(mPackageManager); + + mState = new QSTile.State(); + doAnswer(invocation -> { + String spec = (String) invocation.getArguments()[0]; + if (FACTORY_TILES.contains(spec)) { + QSTile m = mock(QSTile.class); + when(m.isAvailable()).thenReturn(true); + when(m.getTileSpec()).thenReturn(spec); + when(m.getState()).thenReturn(mState); + return m; + } else { + return null; + } + } + ).when(mQSTileHost).createTile(anyString()); + mTileQueryHelper = new TileQueryHelper(mContext, mListener); } @@ -98,4 +150,72 @@ public class TileQueryHelperTest extends SysuiTestCase { assertTrue(mTileQueryHelper.isFinished()); } + + @Test + public void testQueryTiles_correctTilesAndOrderOnlyStockTiles() { + ArgumentCaptor<List<TileQueryHelper.TileInfo>> captor = ArgumentCaptor.forClass(List.class); + + Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES, + ONLY_STOCK_TILES); + mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock, + STOCK_TILES); + + mTileQueryHelper.queryTiles(mQSTileHost); + + mBGLooper.processAllMessages(); + waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER)); + + verify(mListener, atLeastOnce()).onTilesChanged(captor.capture()); + List<String> specs = new ArrayList<>(); + for (TileQueryHelper.TileInfo t : captor.getValue()) { + specs.add(t.spec); + } + String tiles = TextUtils.join(",", specs); + assertThat(tiles, is(equalTo(STOCK_TILES))); + } + + @Test + public void testQueryTiles_correctTilesAndOrderOtherFactoryTiles() { + ArgumentCaptor<List<TileQueryHelper.TileInfo>> captor = ArgumentCaptor.forClass(List.class); + + Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES, + CURRENT_TILES); + mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock, + STOCK_TILES); + + mTileQueryHelper.queryTiles(mQSTileHost); + + mBGLooper.processAllMessages(); + waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER)); + + verify(mListener, atLeastOnce()).onTilesChanged(captor.capture()); + List<String> specs = new ArrayList<>(); + for (TileQueryHelper.TileInfo t : captor.getValue()) { + specs.add(t.spec); + } + String tiles = TextUtils.join(",", specs); + assertThat(tiles, is(equalTo(ALL_TILES))); + } + + @Test + public void testQueryTiles_otherTileNotIncluded() { + ArgumentCaptor<List<TileQueryHelper.TileInfo>> captor = ArgumentCaptor.forClass(List.class); + + Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES, + WITH_OTHER_TILES); + mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock, + STOCK_TILES); + + mTileQueryHelper.queryTiles(mQSTileHost); + + mBGLooper.processAllMessages(); + waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER)); + + verify(mListener, atLeastOnce()).onTilesChanged(captor.capture()); + List<String> specs = new ArrayList<>(); + for (TileQueryHelper.TileInfo t : captor.getValue()) { + specs.add(t.spec); + } + assertFalse(specs.contains("other")); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java index 435ede4d8ff0..55583931af43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java @@ -62,6 +62,7 @@ public class ActivityLaunchAnimatorTest extends SysuiTestCase { @Before public void setUp() throws Exception { + when(mStatusBarWindowView.getResources()).thenReturn(mContext.getResources()); when(mCallback.areLaunchAnimationsEnabled()).thenReturn(true); mLaunchAnimator = new ActivityLaunchAnimator( mStatusBarWindowView, 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 84bfae650ce3..766c5d2377c6 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 @@ -311,7 +311,6 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(false), eq(true) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */, - eq(false) /*isNoisy */, eq(0)); } @@ -340,37 +339,6 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(false), eq(false) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */, - eq(false) /*isNoisy */, - eq(0)); - } - - @Test - public void testInitializeNotificationInfoView_noisy() throws Exception { - NotificationInfo notificationInfoView = mock(NotificationInfo.class); - ExpandableNotificationRow row = spy(mHelper.createRow()); - row.setBlockingHelperShowing(true); - row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; - row.getEntry().noisy = true; - when(row.getIsNonblockable()).thenReturn(false); - StatusBarNotification statusBarNotification = row.getStatusBarNotification(); - - mGutsManager.initializeNotificationInfo(row, notificationInfoView); - - verify(notificationInfoView).bindNotification( - any(PackageManager.class), - any(INotificationManager.class), - eq(statusBarNotification.getPackageName()), - any(NotificationChannel.class), - anyInt(), - eq(statusBarNotification), - any(NotificationInfo.CheckSaveListener.class), - any(NotificationInfo.OnSettingsClickListener.class), - any(NotificationInfo.OnAppSettingsClickListener.class), - eq(false), - eq(false), - eq(true) /* isForBlockingHelper */, - eq(true) /* isUserSentimentNegative */, - eq(true) /*isNoisy */, eq(0)); } @@ -400,7 +368,6 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(false), eq(true) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */, - eq(false) /*isNoisy */, eq(IMPORTANCE_DEFAULT)); } @@ -430,7 +397,6 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(false), eq(false) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */, - eq(false) /*isNoisy */, eq(0)); } @@ -459,7 +425,6 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(false), eq(true) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */, - eq(false) /*isNoisy */, eq(0)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index 3dd493f0cd44..d28f0172e7e0 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 @@ -50,12 +50,9 @@ import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; -import android.content.Intent; -import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.IBinder; import android.os.UserHandle; @@ -86,8 +83,6 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.CountDownLatch; @SmallTest @@ -187,7 +182,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); + 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 +195,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); + IMPORTANCE_DEFAULT); final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon); assertEquals(iconDrawable, iconView.getDrawable()); } @@ -209,7 +204,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); + 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 +223,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT); + IMPORTANCE_DEFAULT); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); assertEquals(VISIBLE, nameView.getVisibility()); assertTrue(nameView.getText().toString().contains("Other")); @@ -240,7 +235,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); + 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 +252,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); + IMPORTANCE_DEFAULT); final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(View.VISIBLE, groupNameView.getVisibility()); assertEquals("Test Group Name", groupNameView.getText()); @@ -269,7 +264,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); + IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(TEST_CHANNEL_NAME, textView.getText()); } @@ -278,7 +273,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); + false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(GONE, textView.getVisibility()); } @@ -291,7 +286,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); + false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(VISIBLE, textView.getVisibility()); } @@ -300,7 +295,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); + IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(VISIBLE, textView.getVisibility()); } @@ -309,62 +304,104 @@ 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); - 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); + IMPORTANCE_DEFAULT); + final View block = mNotificationInfo.findViewById(R.id.int_block); + final View minimize = mNotificationInfo.findViewById(R.id.block_or_minimize); assertEquals(VISIBLE, block.getVisibility()); - assertEquals(GONE, toggleSilent.getVisibility()); assertEquals(GONE, minimize.getVisibility()); } @Test - public void testBindNotification_SilenceButton() throws Exception { + public void testBindNotification_BlockButton_BlockHelper() throws Exception { + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + true /* isBlockingHelper */, false, IMPORTANCE_DEFAULT); + final View block = mNotificationInfo.findViewById(R.id.block); + final View interruptivenessSettings = mNotificationInfo.findViewById( + R.id.interruptiveness_settings); + assertEquals(VISIBLE, block.getVisibility()); + assertEquals(GONE, interruptivenessSettings.getVisibility()); + } + + @Test + public void testBindNotification_SilenceButton_CurrentlyAlerting() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_DEFAULT); - final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); - assertEquals(VISIBLE, toggleSilent.getVisibility()); + IMPORTANCE_DEFAULT); + final TextView silent = mNotificationInfo.findViewById(R.id.int_silent); + assertEquals(VISIBLE, silent.getVisibility()); assertEquals( - mContext.getString(R.string.inline_silent_button_silent), toggleSilent.getText()); + mContext.getString(R.string.inline_silent_button_silent), silent.getText()); } @Test - public void testBindNotification_UnSilenceButton() throws Exception { + public void testBindNotification_SilenceButton_CurrentlySilent() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_LOW); - final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); - assertEquals(VISIBLE, toggleSilent.getVisibility()); + IMPORTANCE_LOW); + final TextView silent = mNotificationInfo.findViewById(R.id.int_silent); + assertEquals(VISIBLE, silent.getVisibility()); assertEquals( - mContext.getString(R.string.inline_silent_button_alert), toggleSilent.getText()); + mContext.getString(R.string.inline_silent_button_stay_silent), + silent.getText()); } @Test - public void testBindNotification_SilenceButton_ChannelImportanceUnspecified() throws Exception { + public void testBindNotification_AlertButton_CurrentlySilent() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + IMPORTANCE_LOW); + final TextView alert = mNotificationInfo.findViewById(R.id.int_alert); + assertEquals(VISIBLE, alert.getVisibility()); + assertEquals( + mContext.getString(R.string.inline_silent_button_alert), alert.getText()); + } + + @Test + public void testBindNotification_UnSilenceButton_currentlyAlerting() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + IMPORTANCE_DEFAULT); + final TextView alert = mNotificationInfo.findViewById(R.id.int_alert); + assertEquals(VISIBLE, alert.getVisibility()); + assertEquals( + mContext.getString(R.string.inline_silent_button_keep_alerting), alert.getText()); + } + + @Test + public void testBindNotification_ChannelImportanceUnspecified_NotifAlerting() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_DEFAULT); - final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); - assertEquals(VISIBLE, toggleSilent.getVisibility()); + IMPORTANCE_DEFAULT); + final TextView silent = mNotificationInfo.findViewById(R.id.int_silent); + final TextView alert = mNotificationInfo.findViewById(R.id.int_alert); + assertEquals(VISIBLE, silent.getVisibility()); + assertEquals(VISIBLE, alert.getVisibility()); assertEquals( - mContext.getString(R.string.inline_silent_button_silent), toggleSilent.getText()); + mContext.getString(R.string.inline_silent_button_silent), silent.getText()); + assertEquals( + mContext.getString(R.string.inline_silent_button_keep_alerting), alert.getText()); } @Test - public void testBindNotification_UnSilenceButton_ChannelImportanceUnspecified() - throws Exception { + public void testBindNotification_ChannelImportanceUnspecified_NotifSilent() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_LOW); - final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); - assertEquals(VISIBLE, toggleSilent.getVisibility()); + IMPORTANCE_LOW); + final TextView silent = mNotificationInfo.findViewById(R.id.int_silent); + final TextView alert = mNotificationInfo.findViewById(R.id.int_alert); + assertEquals(VISIBLE, silent.getVisibility()); + assertEquals(VISIBLE, alert.getVisibility()); assertEquals( - mContext.getString(R.string.inline_silent_button_alert), toggleSilent.getText()); + mContext.getString(R.string.inline_silent_button_stay_silent), silent.getText()); + assertEquals( + mContext.getString(R.string.inline_silent_button_alert), alert.getText()); } @Test @@ -372,10 +409,13 @@ 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); + IMPORTANCE_DEFAULT); final View block = mNotificationInfo.findViewById(R.id.block); + final View interruptivenessSettings = mNotificationInfo.findViewById( + R.id.interruptiveness_settings); final View minimize = mNotificationInfo.findViewById(R.id.minimize); assertEquals(GONE, block.getVisibility()); + assertEquals(GONE, interruptivenessSettings.getVisibility()); assertEquals(VISIBLE, minimize.getVisibility()); } @@ -387,7 +427,7 @@ public class NotificationInfoTest extends SysuiTestCase { (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); latch.countDown(); - }, null, true, false, false, IMPORTANCE_DEFAULT); + }, null, true, false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); settingsButton.performClick(); @@ -399,7 +439,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); + IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -411,7 +451,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); + }, null, false, false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -420,11 +460,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); + 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); + }, null, true, false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertEquals(View.VISIBLE, settingsButton.getVisibility()); } @@ -433,7 +473,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); + IMPORTANCE_DEFAULT); mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent"); verify(mMetricsLogger, times(0)).count(anyString(), anyInt()); } @@ -442,7 +482,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); + true, true, IMPORTANCE_DEFAULT); mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent"); verify(mMetricsLogger, times(1)).count(anyString(), anyInt()); } @@ -455,7 +495,7 @@ public class NotificationInfoTest extends SysuiTestCase { (View v, NotificationChannel c, int appUid) -> { assertEquals(null, c); latch.countDown(); - }, null, true, true, false, IMPORTANCE_DEFAULT); + }, null, true, true, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.info).performClick(); // Verify that listener was triggered. @@ -468,7 +508,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); + null, true, true, IMPORTANCE_DEFAULT); final TextView channelNameView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(GONE, channelNameView.getVisibility()); @@ -479,7 +519,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); + null, true, true, IMPORTANCE_DEFAULT); final TextView blockView = mNotificationInfo.findViewById(R.id.block); assertEquals(GONE, blockView.getVisibility()); } @@ -488,7 +528,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); + true, true, 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 +538,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); + 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 +549,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); + IMPORTANCE_DEFAULT); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); @@ -520,9 +560,9 @@ 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); + IMPORTANCE_DEFAULT); - mNotificationInfo.findViewById(R.id.block).performClick(); + mNotificationInfo.findViewById(R.id.int_block).performClick(); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); @@ -534,7 +574,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); + IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); mTestableLooper.processAllMessages(); @@ -548,9 +588,9 @@ 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); + IMPORTANCE_DEFAULT); - mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + mNotificationInfo.findViewById(R.id.int_silent).performClick(); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); @@ -562,9 +602,9 @@ 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); + IMPORTANCE_DEFAULT); - mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + mNotificationInfo.findViewById(R.id.int_alert).performClick(); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); @@ -576,7 +616,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); + IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true, false); mTestableLooper.processAllMessages(); @@ -591,7 +631,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); + IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true, false); @@ -609,10 +649,10 @@ 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 + true, false /* isNonblockable */, IMPORTANCE_DEFAULT ); - mNotificationInfo.findViewById(R.id.block).performClick(); + mNotificationInfo.findViewById(R.id.int_block).performClick(); waitForUndoButton(); mNotificationInfo.handleCloseControls(true, false); @@ -631,10 +671,10 @@ 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 + true, false /* isNonblockable */, IMPORTANCE_DEFAULT ); - mNotificationInfo.findViewById(R.id.block).performClick(); + mNotificationInfo.findViewById(R.id.int_block).performClick(); waitForUndoButton(); mNotificationInfo.handleCloseControls(true, false); @@ -653,7 +693,7 @@ public class NotificationInfoTest extends SysuiTestCase { null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */, false /* isNonblockable */, true /* isForBlockingHelper */, - true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT); + true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT); NotificationGuts guts = spy(new NotificationGuts(mContext, null)); when(guts.getWindowToken()).thenReturn(mock(IBinder.class)); @@ -681,7 +721,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); + true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT); NotificationGuts guts = spy(new NotificationGuts(mContext, null)); when(guts.getWindowToken()).thenReturn(mock(IBinder.class)); @@ -709,7 +749,7 @@ public class NotificationInfoTest extends SysuiTestCase { 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , false /* isNonblockable */, true /* isForBlockingHelper */, - true, true /* isUserSentimentNegative */, false /* isNoisy */, + true, true /* isUserSentimentNegative */, /* isNoisy */ IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true /* save */, false /* force */); @@ -729,7 +769,7 @@ public class NotificationInfoTest extends SysuiTestCase { null /* onSettingsClick */, null /* onAppSettingsClick */, true /* provisioned */, false /* isNonblockable */, true /* isForBlockingHelper */, - true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT); + true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); mTestableLooper.processAllMessages(); @@ -752,7 +792,7 @@ public class NotificationInfoTest extends SysuiTestCase { true /* isForBlockingHelper */, true, false /* isUserSentimentNegative */, - false /* isNoisy */, IMPORTANCE_DEFAULT); + IMPORTANCE_DEFAULT); NotificationGuts guts = mock(NotificationGuts.class); doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean()); mNotificationInfo.setGutsParent(guts); @@ -767,7 +807,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); + IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -781,9 +821,9 @@ 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); + IMPORTANCE_DEFAULT); - mNotificationInfo.findViewById(R.id.block).performClick(); + mNotificationInfo.findViewById(R.id.int_block).performClick(); waitForUndoButton(); mNotificationInfo.handleCloseControls(true, false); @@ -814,7 +854,7 @@ public class NotificationInfoTest extends SysuiTestCase { false /* isNonblockable */, true /* isForBlockingHelper */, true /* isUserSentimentNegative */, - false/* isNoisy */, IMPORTANCE_DEFAULT); + IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -836,7 +876,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); + IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -851,7 +891,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); + IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -872,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); + IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true, false); @@ -890,9 +930,9 @@ 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); + IMPORTANCE_DEFAULT); - mNotificationInfo.findViewById(R.id.block).performClick(); + mNotificationInfo.findViewById(R.id.int_block).performClick(); waitForUndoButton(); mNotificationInfo.findViewById(R.id.undo).performClick(); waitForStopButton(); @@ -911,8 +951,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testMinUndoDoesNotMinNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -934,9 +974,9 @@ 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); + IMPORTANCE_DEFAULT); - mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + mNotificationInfo.findViewById(R.id.int_silent).performClick(); waitForUndoButton(); mNotificationInfo.handleCloseControls(true, false); @@ -955,9 +995,9 @@ 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); + IMPORTANCE_DEFAULT); - mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + mNotificationInfo.findViewById(R.id.int_alert).performClick(); waitForUndoButton(); mNotificationInfo.handleCloseControls(true, false); @@ -977,9 +1017,9 @@ 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); + IMPORTANCE_DEFAULT); - mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + mNotificationInfo.findViewById(R.id.int_silent).performClick(); waitForUndoButton(); mNotificationInfo.handleCloseControls(true, false); @@ -999,9 +1039,9 @@ 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); + IMPORTANCE_LOW); - mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + mNotificationInfo.findViewById(R.id.int_alert).performClick(); waitForUndoButton(); mNotificationInfo.handleCloseControls(true, false); @@ -1016,11 +1056,11 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test - public void testCloseControlsDoesNotUpdateiMinIfSaveIsFalse() throws Exception { + public void testCloseControlsDoesNotUpdateMinIfSaveIsFalse() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT); + IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -1035,10 +1075,10 @@ public class NotificationInfoTest extends SysuiTestCase { public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + IMPORTANCE_DEFAULT); - mNotificationInfo.findViewById(R.id.block).performClick(); + mNotificationInfo.findViewById(R.id.int_block).performClick(); waitForUndoButton(); mNotificationInfo.handleCloseControls(false, false); @@ -1053,9 +1093,9 @@ 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); + }, null, null, true, true, IMPORTANCE_DEFAULT); - mNotificationInfo.findViewById(R.id.block).performClick(); + mNotificationInfo.findViewById(R.id.int_block).performClick(); mTestableLooper.processAllMessages(); ensureNoUndoButton(); mNotificationInfo.handleCloseControls(true, false); @@ -1071,10 +1111,10 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { saveImportance.run(); - }, null, null, true, false, false, IMPORTANCE_DEFAULT + }, null, null, true, false, IMPORTANCE_DEFAULT ); - mNotificationInfo.findViewById(R.id.block).performClick(); + mNotificationInfo.findViewById(R.id.int_block).performClick(); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel)); @@ -1088,124 +1128,6 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test - public void testDisplaySettingsLink() throws Exception { - final CountDownLatch latch = new CountDownLatch(1); - final String settingsText = "work chats"; - final ResolveInfo ri = new ResolveInfo(); - ri.activityInfo = new ActivityInfo(); - ri.activityInfo.packageName = TEST_PACKAGE_NAME; - ri.activityInfo.name = "something"; - List<ResolveInfo> ris = new ArrayList<>(); - ris.add(ri); - when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris); - mNotificationChannel.setImportance(IMPORTANCE_LOW); - Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) - .setSettingsText(settingsText).build(); - StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, - 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); - - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, - (View v, Intent intent) -> { - latch.countDown(); - }, true, false, false, IMPORTANCE_DEFAULT); - final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); - assertEquals(View.VISIBLE, settingsLink.getVisibility()); - settingsLink.performClick(); - assertEquals(0, latch.getCount()); - } - - @Test - public void testDisplaySettingsLink_multipleChannels() throws Exception { - final CountDownLatch latch = new CountDownLatch(1); - final String settingsText = "work chats"; - final ResolveInfo ri = new ResolveInfo(); - ri.activityInfo = new ActivityInfo(); - ri.activityInfo.packageName = TEST_PACKAGE_NAME; - ri.activityInfo.name = "something"; - List<ResolveInfo> ris = new ArrayList<>(); - ris.add(ri); - when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris); - mNotificationChannel.setImportance(IMPORTANCE_LOW); - Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) - .setSettingsText(settingsText).build(); - StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, - 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); - - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null, - (View v, Intent intent) -> { - latch.countDown(); - }, true, false, false, IMPORTANCE_DEFAULT); - final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); - assertEquals(View.VISIBLE, settingsLink.getVisibility()); - settingsLink.performClick(); - assertEquals(0, latch.getCount()); - } - - @Test - public void testNoSettingsLink_noHandlingActivity() throws Exception { - final String settingsText = "work chats"; - when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(null); - mNotificationChannel.setImportance(IMPORTANCE_LOW); - Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) - .setSettingsText(settingsText).build(); - StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, - 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); - - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null, - null, true, false, false, IMPORTANCE_DEFAULT); - final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); - assertEquals(GONE, settingsLink.getVisibility()); - } - - @Test - public void testNoSettingsLink_noLinkText() throws Exception { - final ResolveInfo ri = new ResolveInfo(); - ri.activityInfo = new ActivityInfo(); - ri.activityInfo.packageName = TEST_PACKAGE_NAME; - ri.activityInfo.name = "something"; - List<ResolveInfo> ris = new ArrayList<>(); - ris.add(ri); - when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris); - mNotificationChannel.setImportance(IMPORTANCE_LOW); - Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()).build(); - StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, - 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); - - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT); - final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); - assertEquals(GONE, settingsLink.getVisibility()); - } - - @Test - public void testBindHeader_noSettingsLinkWhenIsForBlockingHelper() throws Exception { - final String settingsText = "work chats"; - final ResolveInfo ri = new ResolveInfo(); - ri.activityInfo = new ActivityInfo(); - ri.activityInfo.packageName = TEST_PACKAGE_NAME; - ri.activityInfo.name = "something"; - List<ResolveInfo> ris = new ArrayList<>(); - ris.add(ri); - when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris); - mNotificationChannel.setImportance(IMPORTANCE_LOW); - Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) - .setSettingsText(settingsText).build(); - StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, - 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); - - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false, true, - true, true, false, IMPORTANCE_DEFAULT); - final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); - assertEquals(GONE, settingsLink.getVisibility()); - } - - - @Test public void testWillBeRemovedReturnsFalseBeforeBind() throws Exception { assertFalse(mNotificationInfo.willBeRemoved()); } @@ -1216,7 +1138,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); + IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -1228,10 +1150,10 @@ public class NotificationInfoTest extends SysuiTestCase { public void testUndoText_block() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + IMPORTANCE_DEFAULT); - mNotificationInfo.findViewById(R.id.block).performClick(); + mNotificationInfo.findViewById(R.id.int_block).performClick(); waitForUndoButton(); TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text); assertTrue(confirmationText.getText().toString().contains("won't see")); @@ -1241,10 +1163,10 @@ public class NotificationInfoTest extends SysuiTestCase { public void testUndoText_silence() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - true, IMPORTANCE_DEFAULT); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + IMPORTANCE_DEFAULT); - mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + mNotificationInfo.findViewById(R.id.int_silent).performClick(); waitForUndoButton(); TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text); assertEquals(mContext.getString(R.string.notification_channel_silenced), @@ -1255,10 +1177,10 @@ public class NotificationInfoTest extends SysuiTestCase { public void testUndoText_unsilence() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - true, IMPORTANCE_DEFAULT); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + IMPORTANCE_DEFAULT); - mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + mNotificationInfo.findViewById(R.id.int_alert).performClick(); waitForUndoButton(); TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text); assertEquals(mContext.getString(R.string.notification_channel_unsilenced), @@ -1269,10 +1191,10 @@ public class NotificationInfoTest extends SysuiTestCase { public void testNoHeaderOnConfirmation() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + IMPORTANCE_DEFAULT); - mNotificationInfo.findViewById(R.id.block).performClick(); + mNotificationInfo.findViewById(R.id.int_block).performClick(); waitForUndoButton(); assertEquals(GONE, mNotificationInfo.findViewById(R.id.header).getVisibility()); } @@ -1281,10 +1203,10 @@ public class NotificationInfoTest extends SysuiTestCase { public void testHeaderOnUndo() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + IMPORTANCE_DEFAULT); - mNotificationInfo.findViewById(R.id.block).performClick(); + mNotificationInfo.findViewById(R.id.int_block).performClick(); waitForUndoButton(); mNotificationInfo.findViewById(R.id.undo).performClick(); waitForStopButton(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java index 93d8aad4e0d6..d99e46d6de12 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java @@ -40,7 +40,7 @@ import org.junit.runner.RunWith; import org.mockito.Mockito; @RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) +@RunWithLooper() @SmallTest public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index 21047217e942..59a49378e9e7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; import static org.junit.Assert.assertFalse; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.app.Notification; import android.app.StatusBarManager; @@ -63,9 +64,11 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mContext.putComponent(CommandQueue.class, mCommandQueue); mDependency.injectTestDependency(ShadeController.class, mShadeController); + StatusBarWindowView statusBarWindowView = mock(StatusBarWindowView.class); + when(statusBarWindowView.getResources()).thenReturn(mContext.getResources()); mStatusBar = new StatusBarNotificationPresenter(mContext, mock(NotificationPanelView.class), mock(HeadsUpManagerPhone.class), - mock(StatusBarWindowView.class), mock(NotificationListContainerViewGroup.class), + statusBarWindowView, mock(NotificationListContainerViewGroup.class), mock(DozeScrimController.class), mock(ScrimController.class), mock(ActivityLaunchAnimator.Callback.class)); } 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/ExperimentNavigationBarSlimOverlay/res/values-as/strings.xml b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/config.xml index 8cce57046c37..d8b69cde863b 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-as/strings.xml +++ b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/config.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,10 @@ * 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> + <!-- 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-gu/strings.xml b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/strings.xml index 96418aec29bc..c9332903be2e 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gu/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-bn/strings.xml b/packages/overlays/ExperimentNavigationBarSlim32Overlay/AndroidManifest.xml index c0ab3b1b321d..10cf6a15a782 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bn/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-si/strings.xml b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/strings.xml index a1abb64fbf47..b48661c387ab 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-si/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/ExperimentNavigationBarSlim40Overlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarSlim40Overlay/AndroidManifest.xml new file mode 100644 index 000000000000..ce8133f3ed40 --- /dev/null +++ b/packages/overlays/ExperimentNavigationBarSlim40Overlay/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.slim40" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="android" + android:category="com.android.internal.experiment_navbar_slim40" + 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/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-ml/strings.xml b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/strings.xml index b65afe35950c..8fe3a5c17bba 100644 --- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ml/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/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index 356a4daba66e..8d912fadf6d1 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -692,7 +692,7 @@ public class AppOpsService extends IAppOpsService.Stub { } }); - if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) { + if (!StorageManager.hasIsolatedStorage()) { StorageManagerInternal storageManagerInternal = LocalServices.getService( StorageManagerInternal.class); storageManagerInternal.addExternalStoragePolicy( diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java index dd960751ab21..98203213e996 100644 --- a/services/core/java/com/android/server/BinderCallsStatsService.java +++ b/services/core/java/com/android/server/BinderCallsStatsService.java @@ -56,6 +56,7 @@ public class BinderCallsStatsService extends Binder { private static final String SETTINGS_DETAILED_TRACKING_KEY = "detailed_tracking"; private static final String SETTINGS_UPLOAD_DATA_KEY = "upload_data"; private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval"; + private static final String SETTINGS_MAX_CALL_STATS_KEY = "max_call_stats_count"; private boolean mEnabled; private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS); @@ -97,6 +98,9 @@ public class BinderCallsStatsService extends Binder { mBinderCallsStats.setSamplingInterval(mParser.getInt( SETTINGS_SAMPLING_INTERVAL_KEY, BinderCallsStats.PERIODIC_SAMPLING_INTERVAL_DEFAULT)); + mBinderCallsStats.setSamplingInterval(mParser.getInt( + SETTINGS_MAX_CALL_STATS_KEY, + BinderCallsStats.MAX_BINDER_CALL_STATS_COUNT_DEFAULT)); final boolean enabled = diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index e5275e50e484..cc7bf3373bdd 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -19,7 +19,6 @@ package com.android.server; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.provider.Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS; -import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -85,7 +84,6 @@ import com.android.internal.location.ProviderRequest; import com.android.internal.os.BackgroundThread; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; -import com.android.internal.util.Preconditions; import com.android.server.location.ActivityRecognitionProxy; import com.android.server.location.GeocoderProxy; import com.android.server.location.GeofenceManager; @@ -114,7 +112,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; -import java.util.Objects; import java.util.Set; /** @@ -3413,48 +3410,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - @Override - public PendingIntent createManageLocationPermissionIntent(String packageName, - String permission) { - Preconditions.checkNotNull(packageName); - Preconditions.checkArgument(permission.equals(Manifest.permission.ACCESS_FINE_LOCATION) - || permission.equals(Manifest.permission.ACCESS_COARSE_LOCATION) - || permission.equals(Manifest.permission.ACCESS_BACKGROUND_LOCATION)); - - int callingUid = Binder.getCallingUid(); - long token = Binder.clearCallingIdentity(); - try { - String locProvider = getNetworkProviderPackage(); - if (locProvider == null) { - return null; - } - - PackageInfo locProviderInfo; - try { - locProviderInfo = mContext.getPackageManager().getPackageInfo( - locProvider, PackageManager.MATCH_DIRECT_BOOT_AUTO); - } catch (NameNotFoundException e) { - Log.e(TAG, "Could not resolve " + locProvider, e); - return null; - } - - if (locProviderInfo.applicationInfo.uid != callingUid) { - throw new SecurityException("Only " + locProvider + " can call this API"); - } - - Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION); - intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); - intent.putExtra(Intent.EXTRA_PERMISSION_NAME, permission); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - - return PendingIntent.getActivity(mContext, - Objects.hash(packageName, permission), intent, - PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE); - } finally { - Binder.restoreCallingIdentity(token); - } - } - private void log(String log) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Slog.d(TAG, log); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 923ac0063baf..e933bd0bc7ff 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -183,8 +183,7 @@ class StorageManagerService extends IStorageManager.Stub private static final String ZRAM_ENABLED_PROPERTY = "persist.sys.zram_enabled"; - private static final boolean ENABLE_ISOLATED_STORAGE = SystemProperties - .getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false); + private static final boolean ENABLE_ISOLATED_STORAGE = StorageManager.hasIsolatedStorage(); public static class Lifecycle extends SystemService { private StorageManagerService mStorageManagerService; @@ -2686,24 +2685,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 +2723,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 +2736,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 +2757,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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b62f648956dd..0e354d515eef 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -328,6 +328,7 @@ import com.android.server.AlarmManagerInternal; import com.android.server.AppOpsService; import com.android.server.AttributeCache; import com.android.server.DeviceIdleController; +import com.android.server.DisplayThread; import com.android.server.IntentResolver; import com.android.server.IoThread; import com.android.server.LocalServices; @@ -2262,7 +2263,8 @@ public class ActivityManagerService extends IActivityManager.Stub mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler); mActivityTaskManager = atm; - mActivityTaskManager.setActivityManagerService(mIntentFirewall, mPendingIntentController); + mActivityTaskManager.initialize(mIntentFirewall, mPendingIntentController, + DisplayThread.get().getLooper()); mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class); mProcessCpuThread = new Thread("CpuTracker") { @@ -18912,7 +18914,6 @@ public class ActivityManagerService extends IActivityManager.Stub if (memoryStat == null) { continue; } - // TODO(rslawik): Delete RSS high-water mark field. ProcessMemoryState processMemoryState = new ProcessMemoryState(uid, r.processName, @@ -18922,7 +18923,6 @@ public class ActivityManagerService extends IActivityManager.Stub memoryStat.rssInBytes, memoryStat.cacheInBytes, memoryStat.swapInBytes, - memoryStat.rssHighWatermarkInBytes, memoryStat.startTimeNanos); processMemoryStates.add(processMemoryState); } @@ -19348,7 +19348,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public boolean isAppStorageSandboxed(int pid, int uid) { - if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) { + if (!StorageManager.hasIsolatedStorage()) { return false; } if (uid == SHELL_UID || uid == ROOT_UID) { diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index a0977be0e25d..8c39d75ea6a4 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -58,8 +58,9 @@ import java.util.Set; /** * BROADCASTS * - * We keep two broadcast queues and associated bookkeeping, one for those at - * foreground priority, and one for normal (background-priority) broadcasts. + * We keep three broadcast queues and associated bookkeeping, one for those at + * foreground priority, and one for normal (background-priority) broadcasts, and one to + * offload special broadcasts that we know take a long time, such as BOOT_COMPLETED. */ public final class BroadcastQueue { private static final String TAG = "BroadcastQueue"; diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java index cc3da1c63c00..90fe30c7c718 100644 --- a/services/core/java/com/android/server/am/MemoryStatUtil.java +++ b/services/core/java/com/android/server/am/MemoryStatUtil.java @@ -49,13 +49,8 @@ public final class MemoryStatUtil { private static final boolean DEVICE_HAS_PER_APP_MEMCG = SystemProperties.getBoolean("ro.config.per_app_memcg", false); - /** Path to check if device has memcg */ - private static final String MEMCG_TEST_PATH = "/dev/memcg/apps/memory.stat"; /** Path to memory stat file for logging app start memory state */ private static final String MEMORY_STAT_FILE_FMT = "/dev/memcg/apps/uid_%d/pid_%d/memory.stat"; - /** Path to memory max usage file for logging app memory state */ - private static final String MEMORY_MAX_USAGE_FILE_FMT = - "/dev/memcg/apps/uid_%d/pid_%d/memory.max_usage_in_bytes"; /** Path to procfs stat file for logging app start memory state */ private static final String PROC_STAT_FILE_FMT = "/proc/%d/stat"; /** Path to procfs status file for logging app memory state */ @@ -98,14 +93,7 @@ public final class MemoryStatUtil { @Nullable static MemoryStat readMemoryStatFromMemcg(int uid, int pid) { final String statPath = String.format(Locale.US, MEMORY_STAT_FILE_FMT, uid, pid); - MemoryStat stat = parseMemoryStatFromMemcg(readFileContents(statPath)); - if (stat == null) { - return null; - } - String maxUsagePath = String.format(Locale.US, MEMORY_MAX_USAGE_FILE_FMT, uid, pid); - stat.rssHighWatermarkInBytes = parseMemoryMaxUsageFromMemCg( - readFileContents(maxUsagePath)); - return stat; + return parseMemoryStatFromMemcg(readFileContents(statPath)); } /** @@ -116,12 +104,7 @@ public final class MemoryStatUtil { @Nullable public static MemoryStat readMemoryStatFromProcfs(int pid) { final String statPath = String.format(Locale.US, PROC_STAT_FILE_FMT, pid); - MemoryStat stat = parseMemoryStatFromProcfs(readFileContents(statPath)); - if (stat == null) { - return null; - } - stat.rssHighWatermarkInBytes = readRssHighWaterMarkFromProcfs(pid); - return stat; + return parseMemoryStatFromProcfs(readFileContents(statPath)); } /** @@ -185,19 +168,6 @@ public final class MemoryStatUtil { return memoryStat; } - @VisibleForTesting - static long parseMemoryMaxUsageFromMemCg(String memoryMaxUsageContents) { - if (memoryMaxUsageContents == null || memoryMaxUsageContents.isEmpty()) { - return 0; - } - try { - return Long.parseLong(memoryMaxUsageContents); - } catch (NumberFormatException e) { - Slog.e(TAG, "Failed to parse value", e); - return 0; - } - } - /** * Parses relevant statistics out from the contents of the /proc/pid/stat file in procfs. */ @@ -258,8 +228,6 @@ public final class MemoryStatUtil { public long cacheInBytes; /** Number of bytes of swap usage */ public long swapInBytes; - /** Number of bytes of peak anonymous and swap cache memory */ - public long rssHighWatermarkInBytes; /** Device time when the processes started. */ public long startTimeNanos; } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 4b19398cc73d..7991783e08c8 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -27,7 +27,6 @@ import static android.os.Process.getFreeMemory; import static android.os.Process.getTotalMemory; import static android.os.Process.killProcessQuiet; import static android.os.Process.startWebView; -import static android.os.storage.StorageManager.PROP_ISOLATED_STORAGE; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES; @@ -73,6 +72,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; +import android.os.storage.StorageManager; import android.os.storage.StorageManagerInternal; import android.text.TextUtils; import android.util.EventLog; @@ -1281,8 +1281,7 @@ public final class ProcessList { final IPackageManager pm = AppGlobals.getPackageManager(); permGids = pm.getPackageGids(app.info.packageName, MATCH_DIRECT_BOOT_AUTO, app.userId); - if (SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false) - && mountExtStorageFull) { + if (StorageManager.hasIsolatedStorage() && mountExtStorageFull) { mountExternal = Zygote.MOUNT_EXTERNAL_FULL; } else { StorageManagerInternal storageManagerInternal = LocalServices.getService( diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java index 2c2d4045b205..eaa7a83fab0d 100644 --- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java @@ -19,19 +19,11 @@ package com.android.server.biometrics; import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; -import android.hardware.biometrics.BiometricPrompt; -import android.hardware.biometrics.IBiometricPromptReceiver; -import android.os.Bundle; -import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.RemoteException; import android.security.KeyStore; -import android.text.TextUtils; import android.util.Slog; -import com.android.internal.statusbar.IStatusBarService; - import java.util.ArrayList; /** @@ -39,88 +31,15 @@ import java.util.ArrayList; */ public abstract class AuthenticationClient extends ClientMonitor { private long mOpId; - private Handler mHandler; public abstract int handleFailedAttempt(); public abstract void resetFailedAttempts(); - public abstract String getErrorString(int error, int vendorCode); - public abstract String getAcquiredString(int acquireInfo, int vendorCode); - /** - * @return one of {@link #TYPE_FINGERPRINT} {@link #TYPE_IRIS} or {@link #TYPE_FACE} - */ - public abstract int getBiometricType(); public static final int LOCKOUT_NONE = 0; public static final int LOCKOUT_TIMED = 1; public static final int LOCKOUT_PERMANENT = 2; private final boolean mRequireConfirmation; - // Callback mechanism received from the client - // (BiometricPrompt -> BiometricPromptService -> <Biometric>Service -> AuthenticationClient) - private IBiometricPromptReceiver mDialogReceiverFromClient; - private Bundle mBundle; - private IStatusBarService mStatusBarService; - private boolean mInLockout; - private TokenEscrow mEscrow; - protected boolean mDialogDismissed; - - /** - * Container that holds the identifier and authToken. For biometrics that require user - * confirmation, these should not be sent to their final destinations until the user confirms. - */ - class TokenEscrow { - final BiometricAuthenticator.Identifier mIdentifier; - final ArrayList<Byte> mToken; - - TokenEscrow(BiometricAuthenticator.Identifier identifier, ArrayList<Byte> token) { - mIdentifier = identifier; - mToken = token; - } - - BiometricAuthenticator.Identifier getIdentifier() { - return mIdentifier; - } - - ArrayList<Byte> getToken() { - return mToken; - } - } - - // Receives events from SystemUI and handles them before forwarding them to BiometricDialog - protected IBiometricPromptReceiver mDialogReceiver = new IBiometricPromptReceiver.Stub() { - @Override // binder call - public void onDialogDismissed(int reason) { - if (mBundle != null && mDialogReceiverFromClient != null) { - try { - if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) { - // Positive button is used by passive modalities as a "confirm" button, - // do not send to client - mDialogReceiverFromClient.onDialogDismissed(reason); - } - if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) { - onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED, - 0 /* vendorCode */); - } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) { - // Have the service send the token to KeyStore, and send onAuthenticated - // to the application. - if (mEscrow != null) { - if (DEBUG) Slog.d(getLogTag(), "Confirmed"); - addTokenToKeyStore(mEscrow.getToken()); - notifyClientAuthenticationSucceeded(mEscrow.getIdentifier()); - mEscrow = null; - onAuthenticationConfirmed(); - } else { - Slog.e(getLogTag(), "Escrow is null!!!"); - } - } - mDialogDismissed = true; - } catch (RemoteException e) { - Slog.e(getLogTag(), "Remote exception", e); - } - stop(true /* initiatedByClient */); - } - } - }; /** * This method is called when authentication starts. @@ -133,25 +52,13 @@ public abstract class AuthenticationClient extends ClientMonitor { */ public abstract void onStop(); - /** - * This method is called when biometric authentication was confirmed by the user. The client - * should be removed. - */ - public abstract void onAuthenticationConfirmed(); - public AuthenticationClient(Context context, Metrics metrics, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId, - boolean restricted, String owner, Bundle bundle, - IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService, - boolean requireConfirmation) { + boolean restricted, String owner, int cookie, boolean requireConfirmation) { super(context, metrics, daemon, halDeviceId, token, listener, targetUserId, groupId, - restricted, owner); + restricted, owner, cookie); mOpId = opId; - mBundle = bundle; - mDialogReceiverFromClient = dialogReceiver; - mStatusBarService = statusBarService; - mHandler = new Handler(Looper.getMainLooper()); mRequireConfirmation = requireConfirmation; } @@ -164,175 +71,99 @@ public abstract class AuthenticationClient extends ClientMonitor { stop(false /* initiatedByClient */); } - @Override - public boolean onAcquired(int acquiredInfo, int vendorCode) { - // If the dialog is showing, the client doesn't need to receive onAcquired messages. - if (mBundle != null) { - try { - if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) { - mStatusBarService.onBiometricHelp(getAcquiredString(acquiredInfo, vendorCode)); - } - return false; // acquisition continues - } catch (RemoteException e) { - Slog.e(getLogTag(), "Remote exception when sending acquired message", e); - return true; // client failed - } finally { - // Good scans will keep the device awake - if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) { - notifyUserActivity(); - } - } - } else { - return super.onAcquired(acquiredInfo, vendorCode); - } - } - - @Override - public boolean onError(long deviceId, int error, int vendorCode) { - if (mDialogDismissed) { - // If user cancels authentication, the application has already received the - // ERROR_USER_CANCELED message from onDialogDismissed() - // and stopped the biometric hardware, so there is no need to send a - // ERROR_CANCELED message. - return true; - } - if (mBundle != null && error != BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED) { - try { - mStatusBarService.onBiometricError(getErrorString(error, vendorCode)); - } catch (RemoteException e) { - Slog.e(getLogTag(), "Remote exception when sending error", e); - } - } - return super.onError(deviceId, error, vendorCode); - } - - public void setTitleIfEmpty(CharSequence title) { - if (TextUtils.isEmpty(mBundle.getCharSequence(BiometricPrompt.KEY_TITLE))) { - mBundle.putCharSequence(BiometricPrompt.KEY_TITLE, title); - } - } - public boolean isBiometricPrompt() { - return mBundle != null; - } - - private void notifyClientAuthenticationSucceeded(BiometricAuthenticator.Identifier identifier) - throws RemoteException { - final BiometricServiceBase.ServiceListener listener = getListener(); - // Explicitly have if/else here to make it super obvious in case the code is - // touched in the future. - if (!getIsRestricted()) { - listener.onAuthenticationSucceeded( - getHalDeviceId(), identifier, getTargetUserId()); - } else { - listener.onAuthenticationSucceeded( - getHalDeviceId(), null, getTargetUserId()); - } + return getCookie() != 0; } - private void addTokenToKeyStore(ArrayList<Byte> token) { - // Send the token to KeyStore - final byte[] byteToken = new byte[token.size()]; - for (int i = 0; i < token.size(); i++) { - byteToken[i] = token.get(i); - } - KeyStore.getInstance().addAuthToken(byteToken); + public boolean getRequireConfirmation() { + return mRequireConfirmation; } @Override public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> token) { - if (authenticated) { - mAlreadyDone = true; - if (mRequireConfirmation) { - // Store the token so it can be sent to keystore after the user presses confirm - mEscrow = new TokenEscrow(identifier, token); - } else { - addTokenToKeyStore(token); - } - } + final BiometricServiceBase.ServiceListener listener = getListener(); + mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated); boolean result = false; - // If the biometric dialog is showing, notify authentication succeeded - if (mBundle != null) { - try { - if (authenticated) { - mStatusBarService.onBiometricAuthenticated(); - } else { - mStatusBarService.onBiometricHelp(getContext().getResources().getString( - com.android.internal.R.string.biometric_not_recognized)); + try { + if (authenticated) { + mAlreadyDone = true; + if (DEBUG) Slog.v(getLogTag(), "onAuthenticated(" + getOwnerString() + + ", ID:" + identifier.getBiometricId() + + ", isBP: " + isBiometricPrompt() + + ", listener: " + listener + + ", requireConfirmation: " + mRequireConfirmation); + if (listener != null) { + vibrateSuccess(); } - } catch (RemoteException e) { - Slog.e(getLogTag(), "Failed to notify Authenticated:", e); - } - } + result = true; + resetFailedAttempts(); + onStop(); - final BiometricServiceBase.ServiceListener listener = getListener(); - if (listener != null) { - try { - mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated); - if (!authenticated) { - listener.onAuthenticationFailed(getHalDeviceId()); - } else { - if (DEBUG) { - Slog.v(getLogTag(), "onAuthenticated(owner=" + getOwnerString() - + ", id=" + identifier.getBiometricId()); - } - if (!mRequireConfirmation) { - notifyClientAuthenticationSucceeded(identifier); + final byte[] byteToken = new byte[token.size()]; + for (int i = 0; i < token.size(); i++) { + byteToken[i] = token.get(i); + } + if (isBiometricPrompt() && listener != null) { + // BiometricService will add the token to keystore + listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken); + } else if (!isBiometricPrompt() && listener != null) { + KeyStore.getInstance().addAuthToken(byteToken); + try { + // Explicitly have if/else here to make it super obvious in case the code is + // touched in the future. + if (!getIsRestricted()) { + listener.onAuthenticationSucceeded( + getHalDeviceId(), identifier, getTargetUserId()); + } else { + listener.onAuthenticationSucceeded( + getHalDeviceId(), null, getTargetUserId()); + } + } catch (RemoteException e) { + Slog.e(getLogTag(), "Remote exception", e); } + } else { + // Client not listening + Slog.w(getLogTag(), "Client not listening"); + result = true; } - } catch (RemoteException e) { - Slog.w(getLogTag(), "Failed to notify Authenticated:", e); - result = true; // client failed - } - } else { - result = true; // client not listening - } - if (!authenticated) { - if (listener != null) { - vibrateError(); - } - // allow system-defined limit of number of attempts before giving up - int lockoutMode = handleFailedAttempt(); - if (lockoutMode != LOCKOUT_NONE) { - try { - mInLockout = true; - Slog.w(getLogTag(), "Forcing lockout (fp driver code should do this!), mode(" + - lockoutMode + ")"); + } else { + if (listener != null) { + vibrateError(); + } + // Allow system-defined limit of number of attempts before giving up + final int lockoutMode = handleFailedAttempt(); + if (lockoutMode != LOCKOUT_NONE) { + Slog.w(getLogTag(), "Forcing lockout (driver code should do this!), mode(" + + lockoutMode + ")"); stop(false); - int errorCode = lockoutMode == LOCKOUT_TIMED ? - BiometricConstants.BIOMETRIC_ERROR_LOCKOUT : - BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; - - // Send the lockout message to the system dialog - if (mBundle != null) { - mStatusBarService.onBiometricError( - getErrorString(errorCode, 0 /* vendorCode */)); - mHandler.postDelayed(() -> { - try { - listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */); - } catch (RemoteException e) { - Slog.w(getLogTag(), "RemoteException while sending error"); - } - }, BiometricPrompt.HIDE_DIALOG_DELAY); - } else { - listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */); + final int errorCode = lockoutMode == LOCKOUT_TIMED + ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT + : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; + if (listener != null) { + listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */, + getCookie()); + } + } else { + // Don't send onAuthenticationFailed if we're in lockout, it causes a + // janky UI on Keyguard/BiometricPrompt since "authentication failed" + // will show briefly and be replaced by "device locked out" message. + if (listener != null) { + if (isBiometricPrompt()) { + listener.onAuthenticationFailedInternal(getCookie(), + getRequireConfirmation()); + } else { + listener.onAuthenticationFailed(getHalDeviceId()); + } } - } catch (RemoteException e) { - Slog.w(getLogTag(), "Failed to notify lockout:", e); } + result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode } - result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode - } else { - if (listener != null) { - vibrateSuccess(); - } - // we have a valid biometric that doesn't require confirmation, done - result |= !mRequireConfirmation; - resetFailedAttempts(); - onStop(); + } catch (RemoteException e) { + Slog.e(getLogTag(), "Remote exception", e); + result = true; } return result; } @@ -353,16 +184,6 @@ public abstract class AuthenticationClient extends ClientMonitor { return result; } if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is authenticating..."); - - // If authenticating with system dialog, show the dialog - if (mBundle != null) { - try { - mStatusBarService.showBiometricDialog(mBundle, mDialogReceiver, - getBiometricType(), mRequireConfirmation, getTargetUserId()); - } catch (RemoteException e) { - Slog.e(getLogTag(), "Unable to show biometric dialog", e); - } - } } catch (RemoteException e) { Slog.e(getLogTag(), "startAuthentication failed", e); return ERROR_ESRCH; @@ -390,18 +211,6 @@ public abstract class AuthenticationClient extends ClientMonitor { } catch (RemoteException e) { Slog.e(getLogTag(), "stopAuthentication failed", e); return ERROR_ESRCH; - } finally { - // If the user already cancelled authentication (via some interaction with the - // dialog, we do not need to hide it since it's already hidden. - // If the device is in lockout, don't hide the dialog - it will automatically hide - // after BiometricPrompt.HIDE_DIALOG_DELAY - if (mBundle != null && !mDialogDismissed && !mInLockout) { - try { - mStatusBarService.hideBiometricDialog(); - } catch (RemoteException e) { - Slog.e(getLogTag(), "Unable to hide biometric dialog", e); - } - } } mAlreadyCancelled = true; diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 5f09189bd84a..add55eaad166 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -19,9 +19,17 @@ package com.android.server.biometrics; import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.Manifest.permission.USE_FINGERPRINT; +import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE; import android.app.ActivityManager; +import android.app.ActivityTaskManager; import android.app.AppOpsManager; +import android.app.IActivityTaskManager; +import android.app.TaskStackListener; import android.app.UserSwitchObserver; import android.content.ContentResolver; import android.content.Context; @@ -32,9 +40,9 @@ import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.BiometricSourceType; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; -import android.hardware.biometrics.IBiometricPromptReceiver; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceReceiver; +import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.hardware.face.FaceManager; import android.hardware.face.IFaceService; import android.hardware.fingerprint.FingerprintManager; @@ -50,14 +58,21 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; +import android.security.KeyStore; +import android.text.TextUtils; import android.util.Pair; import android.util.Slog; import com.android.internal.R; +import com.android.internal.statusbar.IStatusBarService; import com.android.server.SystemService; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Random; /** * System service that arbitrates the modality for BiometricPrompt to use. @@ -66,32 +81,10 @@ public class BiometricService extends SystemService { private static final String TAG = "BiometricService"; - /** - * No biometric methods or nothing has been enrolled. - * Move/expose these in BiometricPrompt if we ever want to allow applications to "blacklist" - * modalities when calling authenticate(). - */ - private static final int BIOMETRIC_NONE = 0; - - /** - * Constant representing fingerprint. - */ - private static final int BIOMETRIC_FINGERPRINT = 1 << 0; - - /** - * Constant representing iris. - */ - private static final int BIOMETRIC_IRIS = 1 << 1; - - /** - * Constant representing face. - */ - private static final int BIOMETRIC_FACE = 1 << 2; - private static final int[] FEATURE_ID = { - BIOMETRIC_FINGERPRINT, - BIOMETRIC_IRIS, - BIOMETRIC_FACE + TYPE_FINGERPRINT, + TYPE_IRIS, + TYPE_FACE }; private final AppOpsManager mAppOps; @@ -242,10 +235,367 @@ public class BiometricService extends SystemService { */ private final class BiometricServiceWrapper extends IBiometricService.Stub { + /** + * Authentication either just called and we have not transitioned to the CALLED state, or + * authentication terminated (success or error). + */ + private static final int STATE_AUTH_IDLE = 0; + /** + * Authentication was called and we are waiting for the <Biometric>Services to return their + * cookies before starting the hardware and showing the BiometricPrompt. + */ + private static final int STATE_AUTH_CALLED = 1; + /** + * Authentication started, BiometricPrompt is showing and the hardware is authenticating. + */ + private static final int STATE_AUTH_STARTED = 2; + /** + * Authentication is paused, waiting for the user to press "try again" button. Since the + * try again button requires us to cancel authentication, this represents the state where + * ERROR_CANCELED is not received yet. + */ + private static final int STATE_AUTH_PAUSED = 3; + /** + * Same as above, except the ERROR_CANCELED has been received. + */ + private static final int STATE_AUTH_PAUSED_CANCELED = 4; + /** + * Authentication is successful, but we're waiting for the user to press "confirm" button. + */ + private static final int STATE_AUTH_PENDING_CONFIRM = 5; + + final class AuthSession { + // Map of Authenticator/Cookie pairs. We expect to receive the cookies back from + // <Biometric>Services before we can start authenticating. Pairs that have been returned + // are moved to mModalitiesMatched. + final HashMap<Integer, Integer> mModalitiesWaiting; + // Pairs that have been matched. + final HashMap<Integer, Integer> mModalitiesMatched = new HashMap<>(); + + // The following variables are passed to authenticateInternal, which initiates the + // appropriate <Biometric>Services. + final IBinder mToken; + final long mSessionId; + final int mUserId; + // Original receiver from BiometricPrompt. + final IBiometricServiceReceiver mClientReceiver; + final String mOpPackageName; + // Info to be shown on BiometricDialog when all cookies are returned. + final Bundle mBundle; + final int mCallingUid; + final int mCallingPid; + final int mCallingUserId; + // Continue authentication with the same modality/modalities after "try again" is + // pressed + final int mModality; + + // The current state, which can be either idle, called, or started + private int mState = STATE_AUTH_IDLE; + // For explicit confirmation, do not send to keystore until the user has confirmed + // the authentication. + byte[] mTokenEscrow; + + AuthSession(HashMap<Integer, Integer> modalities, IBinder token, long sessionId, + int userId, IBiometricServiceReceiver receiver, String opPackageName, + Bundle bundle, int callingUid, int callingPid, int callingUserId, + int modality) { + mModalitiesWaiting = modalities; + mToken = token; + mSessionId = sessionId; + mUserId = userId; + mClientReceiver = receiver; + mOpPackageName = opPackageName; + mBundle = bundle; + mCallingUid = callingUid; + mCallingPid = callingPid; + mCallingUserId = callingUserId; + mModality = modality; + } + + boolean containsCookie(int cookie) { + if (mModalitiesWaiting != null && mModalitiesWaiting.containsValue(cookie)) { + return true; + } + if (mModalitiesMatched != null && mModalitiesMatched.containsValue(cookie)) { + return true; + } + return false; + } + } + + final class BiometricTaskStackListener extends TaskStackListener { + @Override + public void onTaskStackChanged() { + try { + final List<ActivityManager.RunningTaskInfo> runningTasks = + mActivityTaskManager.getTasks(1); + if (!runningTasks.isEmpty()) { + final String topPackage = runningTasks.get(0).topActivity.getPackageName(); + if (mCurrentAuthSession != null + && !topPackage.contentEquals(mCurrentAuthSession.mOpPackageName) + && mCurrentAuthSession.mState != STATE_AUTH_STARTED) { + // We only care about this state, since <Biometric>Service will + // cancel any client that's still in STATE_AUTH_STARTED + mStatusBarService.hideBiometricDialog(); + mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); + mCurrentAuthSession.mClientReceiver.onError( + BiometricConstants.BIOMETRIC_ERROR_CANCELED, + getContext().getString( + com.android.internal.R.string.biometric_error_canceled) + ); + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + } + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to get running tasks", e); + } + } + } + + private final IActivityTaskManager mActivityTaskManager = getContext().getSystemService( + ActivityTaskManager.class).getService(); + private final IStatusBarService mStatusBarService = IStatusBarService.Stub.asInterface( + ServiceManager.getService(Context.STATUS_BAR_SERVICE)); + private final BiometricTaskStackListener mTaskStackListener = + new BiometricTaskStackListener(); + private final Random mRandom = new Random(); + + // The current authentication session, null if idle/done. We need to track both the current + // and pending sessions since errors may be sent to either. + private AuthSession mCurrentAuthSession; + private AuthSession mPendingAuthSession; + + // Wrap the client's receiver so we can do things with the BiometricDialog first + private final IBiometricServiceReceiverInternal mInternalReceiver = + new IBiometricServiceReceiverInternal.Stub() { + @Override + public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token) + throws RemoteException { + try { + if (!requireConfirmation) { + mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); + KeyStore.getInstance().addAuthToken(token); + mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded(); + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + } else { + // Store the auth token and submit it to keystore after the confirmation + // button has been pressed. + mCurrentAuthSession.mTokenEscrow = token; + mCurrentAuthSession.mState = STATE_AUTH_PENDING_CONFIRM; + } + + // Notify SysUI that the biometric has been authenticated. SysUI already knows + // the implicit/explicit state and will react accordingly. + mStatusBarService.onBiometricAuthenticated(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + + @Override + public void onAuthenticationFailed(int cookie, boolean requireConfirmation) + throws RemoteException { + try { + mStatusBarService.onBiometricHelp(getContext().getResources().getString( + com.android.internal.R.string.biometric_not_recognized)); + if (requireConfirmation) { + mCurrentAuthSession.mState = STATE_AUTH_PAUSED; + mStatusBarService.showBiometricTryAgain(); + // Cancel authentication. Skip the token/package check since we are + // cancelling from system server. The interface is permission protected so + // this is fine. + cancelInternal(null /* token */, null /* package */, + false /* fromClient */); + } + mCurrentAuthSession.mClientReceiver.onAuthenticationFailed(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + + @Override + public void onError(int cookie, int error, String message) throws RemoteException { + Slog.d(TAG, "Error: " + error + " cookie: " + cookie); + // Errors can either be from the current auth session or the pending auth session. + // The pending auth session may receive errors such as ERROR_LOCKOUT before + // it becomes the current auth session. Similarly, the current auth session may + // receive errors such as ERROR_CANCELED while the pending auth session is preparing + // to be started. Thus we must match error messages with their cookies to be sure + // of their intended receivers. + try { + if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) { + if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) { + mStatusBarService.onBiometricError(message); + mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); + if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) { + mCurrentAuthSession.mClientReceiver.onError(error, message); + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + mStatusBarService.hideBiometricDialog(); + } else { + // Send errors after the dialog is dismissed. + mHandler.postDelayed(() -> { + try { + mCurrentAuthSession.mClientReceiver.onError(error, message); + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + }, BiometricPrompt.HIDE_DIALOG_DELAY); + } + } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED + || mCurrentAuthSession.mState == STATE_AUTH_PAUSED_CANCELED) { + if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED + && error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) { + // Skip the first ERROR_CANCELED message when this happens, since + // "try again" requires us to cancel authentication but keep + // the prompt showing. + mCurrentAuthSession.mState = STATE_AUTH_PAUSED_CANCELED; + } else { + // In the "try again" state, we should forward canceled errors to + // the client and and clean up. + mCurrentAuthSession.mClientReceiver.onError(error, message); + mStatusBarService.onBiometricError(message); + mActivityTaskManager.unregisterTaskStackListener( + mTaskStackListener); + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + } + } else { + Slog.e(TAG, "Impossible session error state: " + + mCurrentAuthSession.mState); + } + } else if (mPendingAuthSession != null + && mPendingAuthSession.containsCookie(cookie)) { + if (mPendingAuthSession.mState == STATE_AUTH_CALLED) { + mPendingAuthSession.mClientReceiver.onError(error, message); + mPendingAuthSession.mState = STATE_AUTH_IDLE; + mPendingAuthSession = null; + } else { + Slog.e(TAG, "Impossible pending session error state: " + + mPendingAuthSession.mState); + } + } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + + @Override + public void onAcquired(int acquiredInfo, String message) throws RemoteException { + if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) { + try { + mStatusBarService.onBiometricHelp(message); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + } + + @Override + public void onDialogDismissed(int reason) throws RemoteException { + if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) { + // Positive button is used by passive modalities as a "confirm" button, + // do not send to client + mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason); + // Cancel authentication. Skip the token/package check since we are cancelling + // from system server. The interface is permission protected so this is fine. + cancelInternal(null /* token */, null /* package */, false /* fromClient */); + } + if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) { + mCurrentAuthSession.mClientReceiver.onError( + BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED, + getContext().getString( + com.android.internal.R.string.biometric_error_user_canceled)); + } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) { + // Have the service send the token to KeyStore, and send onAuthenticated + // to the application + KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow); + mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded(); + } + mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + } + + @Override + public void onTryAgainPressed() { + Slog.d(TAG, "onTryAgainPressed"); + // No need to check permission, since it can only be invoked by SystemUI + // (or system server itself). + mHandler.post(() -> { + authenticateInternal(mCurrentAuthSession.mToken, + mCurrentAuthSession.mSessionId, + mCurrentAuthSession.mUserId, + mCurrentAuthSession.mClientReceiver, + mCurrentAuthSession.mOpPackageName, + mCurrentAuthSession.mBundle, + mCurrentAuthSession.mCallingUid, + mCurrentAuthSession.mCallingPid, + mCurrentAuthSession.mCallingUserId, + mCurrentAuthSession.mModality); + }); + } + }; + + @Override // Binder call + public void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId) { + checkInternalPermission(); + + Iterator it = mPendingAuthSession.mModalitiesWaiting.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<Integer, Integer> pair = (Map.Entry) it.next(); + if (pair.getValue() == cookie) { + mPendingAuthSession.mModalitiesMatched.put(pair.getKey(), pair.getValue()); + mPendingAuthSession.mModalitiesWaiting.remove(pair.getKey()); + Slog.d(TAG, "Matched cookie: " + cookie + ", " + + mPendingAuthSession.mModalitiesWaiting.size() + " remaining"); + break; + } + } + + if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) { + final boolean mContinuing = mCurrentAuthSession != null + && mCurrentAuthSession.mState == STATE_AUTH_PAUSED; + mCurrentAuthSession = mPendingAuthSession; + mPendingAuthSession = null; + + mCurrentAuthSession.mState = STATE_AUTH_STARTED; + try { + int modality = TYPE_NONE; + it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<Integer, Integer> pair = (Map.Entry) it.next(); + if (pair.getKey() == TYPE_FINGERPRINT) { + mFingerprintService.startPreparedClient(pair.getValue()); + } else if (pair.getKey() == TYPE_IRIS) { + Slog.e(TAG, "Iris unsupported"); + } else if (pair.getKey() == TYPE_FACE) { + mFaceService.startPreparedClient(pair.getValue()); + } else { + Slog.e(TAG, "Unknown modality: " + pair.getKey()); + } + modality |= pair.getKey(); + } + + if (!mContinuing) { + mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle, + mInternalReceiver, modality, requireConfirmation, userId); + mActivityTaskManager.registerTaskStackListener(mTaskStackListener); + } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + } + @Override // Binder call public void authenticate(IBinder token, long sessionId, int userId, - IBiometricServiceReceiver receiver, int flags, String opPackageName, - Bundle bundle, IBiometricPromptReceiver dialogReceiver) throws RemoteException { + IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle) + throws RemoteException { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final int callingUserId = UserHandle.getCallingUserId(); @@ -261,16 +611,45 @@ public class BiometricService extends SystemService { checkInternalPermission(); } - if (token == null || receiver == null || opPackageName == null || bundle == null - || dialogReceiver == null) { + if (token == null || receiver == null || opPackageName == null || bundle == null) { Slog.e(TAG, "Unable to authenticate, one or more null arguments"); return; } // Check the usage of this in system server. Need to remove this check if it becomes // a public API. - if (bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false)) { + final boolean useDefaultTitle = + bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false); + if (useDefaultTitle) { checkInternalPermission(); + // Set the default title if necessary + try { + if (useDefaultTitle) { + final List<ActivityManager.RunningAppProcessInfo> procs = + ActivityManager.getService().getRunningAppProcesses(); + for (int i = 0; i < procs.size(); i++) { + final ActivityManager.RunningAppProcessInfo info = procs.get(i); + if (info.uid == callingUid + && info.importance == IMPORTANCE_FOREGROUND) { + PackageManager pm = getContext().getPackageManager(); + final CharSequence label = pm.getApplicationLabel( + pm.getApplicationInfo(info.processName, + PackageManager.GET_META_DATA)); + final String title = getContext() + .getString(R.string.biometric_dialog_default_title, label); + if (TextUtils.isEmpty( + bundle.getCharSequence(BiometricPrompt.KEY_TITLE))) { + bundle.putCharSequence(BiometricPrompt.KEY_TITLE, title); + } + break; + } + } + } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "Name not found", e); + } } mHandler.post(() -> { @@ -285,13 +664,13 @@ public class BiometricService extends SystemService { getContext().getString(R.string.biometric_error_hw_unavailable); switch (error) { case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT: - receiver.onError(0 /* deviceId */, error, hardwareUnavailable); + receiver.onError(error, hardwareUnavailable); break; case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE: - receiver.onError(0 /* deviceId */, error, hardwareUnavailable); + receiver.onError(error, hardwareUnavailable); break; case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS: - receiver.onError(0 /* deviceId */, error, + receiver.onError(error, getErrorString(modality, error, 0 /* vendorCode */)); break; default: @@ -304,60 +683,91 @@ public class BiometricService extends SystemService { return; } - // Actually start authentication mCurrentModality = modality; - try { - // No polymorphism :( - if (mCurrentModality == BIOMETRIC_FINGERPRINT) { - mFingerprintService.authenticateFromService(token, sessionId, userId, - receiver, flags, opPackageName, bundle, dialogReceiver, - callingUid, callingPid, callingUserId); - } else if (mCurrentModality == BIOMETRIC_IRIS) { - Slog.w(TAG, "Unsupported modality"); - } else if (mCurrentModality == BIOMETRIC_FACE) { - mFaceService.authenticateFromService(true /* requireConfirmation */, - token, sessionId, userId, receiver, flags, opPackageName, - bundle, dialogReceiver, callingUid, callingPid, callingUserId); - } else { - Slog.w(TAG, "Unsupported modality"); - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to start authentication", e); - } + + // Actually start authentication + authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle, + callingUid, callingPid, callingUserId, modality); }); } + /** + * authenticate() (above) which is called from BiometricPrompt determines which + * modality/modalities to start authenticating with. authenticateInternal() should only be + * used for: + * 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is, + * invoked, shortly after which BiometricPrompt is shown and authentication starts + * 2) Preparing <Biometric>Services for authentication when BiometricPrompt is already shown + * and the user has pressed "try again" + */ + private void authenticateInternal(IBinder token, long sessionId, int userId, + IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle, + int callingUid, int callingPid, int callingUserId, int modality) { + try { + // Generate random cookies to pass to the services that should prepare to start + // authenticating. Store the cookie here and wait for all services to "ack" + // with the cookie. Once all cookies are received, we can show the prompt + // and let the services start authenticating. The cookie should be non-zero. + final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1; + Slog.d(TAG, "Creating auth session. Modality: " + modality + + ", cookie: " + cookie); + final HashMap<Integer, Integer> authenticators = new HashMap<>(); + authenticators.put(modality, cookie); + mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId, + receiver, opPackageName, bundle, callingUid, callingPid, callingUserId, + modality); + mPendingAuthSession.mState = STATE_AUTH_CALLED; + // No polymorphism :( + if ((modality & TYPE_FINGERPRINT) != 0) { + mFingerprintService.prepareForAuthentication(token, sessionId, userId, + mInternalReceiver, opPackageName, cookie, + callingUid, callingPid, callingUserId); + } + if ((modality & TYPE_IRIS) != 0) { + Slog.w(TAG, "Iris unsupported"); + } + if ((modality & TYPE_FACE) != 0) { + mFaceService.prepareForAuthentication(true /* requireConfirmation */, + token, sessionId, userId, mInternalReceiver, opPackageName, + cookie, callingUid, callingPid, callingUserId); + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to start authentication", e); + } + } + @Override // Binder call public void cancelAuthentication(IBinder token, String opPackageName) throws RemoteException { checkPermission(); - if (token == null || opPackageName == null) { Slog.e(TAG, "Unable to cancel, one or more null arguments"); return; } - final int callingUid = Binder.getCallingUid(); - final int callingPid = Binder.getCallingPid(); - final int callingUserId = UserHandle.getCallingUserId(); - - mHandler.post(() -> { - try { - if (mCurrentModality == BIOMETRIC_FINGERPRINT) { - mFingerprintService.cancelAuthenticationFromService(token, opPackageName, - callingUid, callingPid, callingUserId); - } else if (mCurrentModality == BIOMETRIC_IRIS) { - Slog.w(TAG, "Unsupported modality"); - } else if (mCurrentModality == BIOMETRIC_FACE) { - mFaceService.cancelAuthenticationFromService(token, opPackageName, - callingUid, callingPid, callingUserId); - } else { - Slog.w(TAG, "Unsupported modality"); + // We need to check the current authenticators state. If we're pending confirm + // or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client, + // since we won't be getting an onError from the driver. + if (mCurrentAuthSession != null && mCurrentAuthSession.mState != STATE_AUTH_STARTED) { + mHandler.post(() -> { + try { + // Send error to client + mCurrentAuthSession.mClientReceiver.onError( + BiometricConstants.BIOMETRIC_ERROR_CANCELED, + getContext().getString( + com.android.internal.R.string.biometric_error_user_canceled) + ); + + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + mStatusBarService.hideBiometricDialog(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to cancel authentication"); - } - }); + }); + } else { + cancelInternal(token, opPackageName, true /* fromClient */); + } } @Override // Binder call @@ -402,6 +812,31 @@ public class BiometricService extends SystemService { Binder.restoreCallingIdentity(ident); } } + + void cancelInternal(IBinder token, String opPackageName, boolean fromClient) { + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); + final int callingUserId = UserHandle.getCallingUserId(); + mHandler.post(() -> { + try { + // TODO: For multiple modalities, send a single ERROR_CANCELED only when all + // drivers have canceled authentication. + if ((mCurrentModality & TYPE_FINGERPRINT) != 0) { + mFingerprintService.cancelAuthenticationFromService(token, opPackageName, + callingUid, callingPid, callingUserId, fromClient); + } + if ((mCurrentModality & TYPE_IRIS) != 0) { + Slog.w(TAG, "Iris unsupported"); + } + if ((mCurrentModality & TYPE_FACE) != 0) { + mFaceService.cancelAuthenticationFromService(token, opPackageName, + callingUid, callingPid, callingUserId, fromClient); + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to cancel authentication"); + } + }); + } } private void checkAppOp(String opPackageName, int callingUid) { @@ -413,7 +848,7 @@ public class BiometricService extends SystemService { } private void checkInternalPermission() { - getContext().enforceCallingPermission(USE_BIOMETRIC_INTERNAL, + getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL, "Must have USE_BIOMETRIC_INTERNAL permission"); } @@ -490,16 +925,19 @@ public class BiometricService extends SystemService { * returns errors through the callback (no biometric feature, hardware not detected, no * templates enrolled, etc). This service must not start authentication if errors are sent. * - * @Returns A pair [Modality, Error] with Modality being one of {@link #BIOMETRIC_NONE}, - * {@link #BIOMETRIC_FINGERPRINT}, {@link #BIOMETRIC_IRIS}, {@link #BIOMETRIC_FACE} + * @Returns A pair [Modality, Error] with Modality being one of + * {@link BiometricAuthenticator#TYPE_NONE}, + * {@link BiometricAuthenticator#TYPE_FINGERPRINT}, + * {@link BiometricAuthenticator#TYPE_IRIS}, + * {@link BiometricAuthenticator#TYPE_FACE} * and the error containing one of the {@link BiometricConstants} errors. */ private Pair<Integer, Integer> checkAndGetBiometricModality(int callingUid) { - int modality = BIOMETRIC_NONE; + int modality = TYPE_NONE; // No biometric features, send error if (mAuthenticators.isEmpty()) { - return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT); + return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT); } // Assuming that authenticators are listed in priority-order, the rest of this function @@ -512,13 +950,13 @@ public class BiometricService extends SystemService { boolean hasTemplatesEnrolled = false; boolean enabledForApps = false; - int firstHwAvailable = BIOMETRIC_NONE; + int firstHwAvailable = TYPE_NONE; for (int i = 0; i < mAuthenticators.size(); i++) { modality = mAuthenticators.get(i).getType(); BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator(); if (authenticator.isHardwareDetected()) { isHardwareDetected = true; - if (firstHwAvailable == BIOMETRIC_NONE) { + if (firstHwAvailable == TYPE_NONE) { // Store the first one since we want to return the error in correct priority // order. firstHwAvailable = modality; @@ -538,13 +976,13 @@ public class BiometricService extends SystemService { // Check error conditions if (!isHardwareDetected) { - return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE); + return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE); } else if (!hasTemplatesEnrolled) { // Return the modality here so the correct error string can be sent. This error is // preferred over !enabledForApps return new Pair<>(firstHwAvailable, BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS); } else if (!enabledForApps) { - return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE); + return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE); } return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS); @@ -552,11 +990,11 @@ public class BiometricService extends SystemService { private boolean isEnabledForApp(int modality) { switch(modality) { - case BIOMETRIC_FINGERPRINT: + case TYPE_FINGERPRINT: return true; - case BIOMETRIC_IRIS: + case TYPE_IRIS: return true; - case BIOMETRIC_FACE: + case TYPE_FACE: return mSettingObserver.getFaceEnabledForApps(); default: Slog.w(TAG, "Unsupported modality: " + modality); @@ -566,12 +1004,12 @@ public class BiometricService extends SystemService { private String getErrorString(int type, int error, int vendorCode) { switch (type) { - case BIOMETRIC_FINGERPRINT: + case TYPE_FINGERPRINT: return FingerprintManager.getErrorString(getContext(), error, vendorCode); - case BIOMETRIC_IRIS: + case TYPE_IRIS: Slog.w(TAG, "Modality not supported"); return null; // not supported - case BIOMETRIC_FACE: + case TYPE_FACE: return FaceManager.getErrorString(getContext(), error, vendorCode); default: Slog.w(TAG, "Unable to get error string for modality: " + type); @@ -581,12 +1019,12 @@ public class BiometricService extends SystemService { private BiometricAuthenticator getAuthenticator(int type) { switch (type) { - case BIOMETRIC_FINGERPRINT: + case TYPE_FINGERPRINT: return (FingerprintManager) getContext().getSystemService(Context.FINGERPRINT_SERVICE); - case BIOMETRIC_IRIS: + case TYPE_IRIS: return null; - case BIOMETRIC_FACE: + case TYPE_FACE: return (FaceManager) getContext().getSystemService(Context.FACE_SERVICE); default: @@ -596,11 +1034,11 @@ public class BiometricService extends SystemService { private boolean hasFeature(int type) { switch (type) { - case BIOMETRIC_FINGERPRINT: + case TYPE_FINGERPRINT: return mHasFeatureFingerprint; - case BIOMETRIC_IRIS: + case TYPE_IRIS: return mHasFeatureIris; - case BIOMETRIC_FACE: + case TYPE_FACE: return mHasFeatureFace; default: return false; diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index 74d742af5b84..9649ccd3c750 100644 --- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java @@ -16,7 +16,6 @@ package com.android.server.biometrics; -import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; import android.app.ActivityManager; @@ -36,8 +35,9 @@ import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; -import android.hardware.biometrics.IBiometricPromptReceiver; +import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; +import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.hardware.fingerprint.Fingerprint; import android.os.Binder; import android.os.Bundle; @@ -56,7 +56,6 @@ import android.util.Slog; import android.util.SparseBooleanArray; import android.util.SparseIntArray; -import com.android.internal.R; import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.server.SystemService; @@ -106,6 +105,7 @@ public abstract class BiometricServiceBase extends SystemService protected final AppOpsManager mAppOps; protected final H mHandler = new H(); + private IBiometricService mBiometricService; private ClientMonitor mCurrentClient; private ClientMonitor mPendingClient; private PerformanceStats mPerformanceStats; @@ -223,12 +223,9 @@ public abstract class BiometricServiceBase extends SystemService public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId, IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId, - boolean restricted, String owner, Bundle bundle, - IBiometricPromptReceiver dialogReceiver, - IStatusBarService statusBarService, boolean requireConfirmation) { - super(context, getMetrics(), daemon, halDeviceId, token, listener, - targetUserId, groupId, opId, restricted, owner, bundle, dialogReceiver, - statusBarService, requireConfirmation); + boolean restricted, String owner, int cookie, boolean requireConfirmation) { + super(context, getMetrics(), daemon, halDeviceId, token, listener, targetUserId, + groupId, opId, restricted, owner, cookie, requireConfirmation); } @Override @@ -279,11 +276,6 @@ public abstract class BiometricServiceBase extends SystemService } return AuthenticationClient.LOCKOUT_NONE; } - - @Override - public void onAuthenticationConfirmed() { - removeClient(mCurrentClient); - } } protected class EnrollClientImpl extends EnrollClient { @@ -345,18 +337,28 @@ public abstract class BiometricServiceBase extends SystemService default void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) throws RemoteException {}; - void onAcquired(long deviceId, int acquiredInfo, int vendorCode) - throws RemoteException; + void onAcquired(long deviceId, int acquiredInfo, int vendorCode) throws RemoteException; - void onAuthenticationSucceeded(long deviceId, - BiometricAuthenticator.Identifier biometric, int userId) - throws RemoteException; + default void onAuthenticationSucceeded(long deviceId, + BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException { + throw new UnsupportedOperationException("Stub!"); + } - void onAuthenticationFailed(long deviceId) - throws RemoteException; + default void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token) + throws RemoteException { + throw new UnsupportedOperationException("Stub!"); + } - void onError(long deviceId, int error, int vendorCode) - throws RemoteException; + default void onAuthenticationFailed(long deviceId) throws RemoteException { + throw new UnsupportedOperationException("Stub!"); + } + + default void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation) + throws RemoteException { + throw new UnsupportedOperationException("Stub!"); + } + + void onError(long deviceId, int error, int vendorCode, int cookie) throws RemoteException; default void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) throws RemoteException {}; @@ -366,6 +368,37 @@ public abstract class BiometricServiceBase extends SystemService } /** + * Wraps the callback interface from Service -> BiometricPrompt + */ + protected abstract class BiometricServiceListener implements ServiceListener { + private IBiometricServiceReceiverInternal mWrapperReceiver; + + public BiometricServiceListener(IBiometricServiceReceiverInternal wrapperReceiver) { + mWrapperReceiver = wrapperReceiver; + } + + public IBiometricServiceReceiverInternal getWrapperReceiver() { + return mWrapperReceiver; + } + + @Override + public void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token) + throws RemoteException { + if (getWrapperReceiver() != null) { + getWrapperReceiver().onAuthenticationSucceeded(requireConfirmation, token); + } + } + + @Override + public void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation) + throws RemoteException { + if (getWrapperReceiver() != null) { + getWrapperReceiver().onAuthenticationFailed(cookie, requireConfirmation); + } + } + } + + /** * Wraps a portion of the interface from Service -> Daemon that is used by the ClientMonitor * subclasses. */ @@ -706,30 +739,6 @@ public abstract class BiometricServiceBase extends SystemService } mHandler.post(() -> { - if (client.isBiometricPrompt()) { - try { - final List<ActivityManager.RunningAppProcessInfo> procs = - ActivityManager.getService().getRunningAppProcesses(); - for (int i = 0; i < procs.size(); i++) { - final ActivityManager.RunningAppProcessInfo info = procs.get(i); - if (info.uid == callingUid && info.importance == IMPORTANCE_FOREGROUND) { - PackageManager pm = getContext().getPackageManager(); - final CharSequence label = pm.getApplicationLabel( - pm.getApplicationInfo(info.processName, - PackageManager.GET_META_DATA)); - final String title = getContext() - .getString(R.string.biometric_dialog_default_title, label); - client.setTitleIfEmpty(title); - break; - } - } - } catch (RemoteException e) { - Slog.e(getTag(), "Unable to get application name", e); - } catch (PackageManager.NameNotFoundException e) { - Slog.e(getTag(), "Unable to get application name", e); - } - } - mMetricsLogger.histogram(getMetrics().tagAuthToken(), opId != 0L ? 1 : 0); // Get performance stats object for this user. @@ -751,29 +760,37 @@ public abstract class BiometricServiceBase extends SystemService final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final int callingUserId = UserHandle.getCallingUserId(); - cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, callingUserId); + cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, callingUserId, + true /* fromClient */); } protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName, - int callingUid, int callingPid, int callingUserId) { - if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid, - callingUserId)) { - if (DEBUG) Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName); - return; + int callingUid, int callingPid, int callingUserId, boolean fromClient) { + if (fromClient) { + // Only check this if cancel was called from the client (app). If cancel was called + // from BiometricService, it means the dialog was dismissed due to user interaction. + if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid, + callingUserId)) { + if (DEBUG) Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName); + return; + } } mHandler.post(() -> { ClientMonitor client = mCurrentClient; if (client instanceof AuthenticationClient) { - if (client.getToken() == token) { - if (DEBUG) Slog.v(getTag(), "stop client " + client.getOwnerString()); + if (client.getToken() == token || !fromClient) { + if (DEBUG) Slog.v(getTag(), "Stopping client " + client.getOwnerString() + + ", fromClient: " + fromClient); + // If cancel was from BiometricService, it means the dialog was dismissed + // and authentication should be canceled. client.stop(client.getToken() == token); } else { - if (DEBUG) Slog.v(getTag(), "can't stop client " - + client.getOwnerString() + " since tokens don't match"); + if (DEBUG) Slog.v(getTag(), "Can't stop client " + client.getOwnerString() + + " since tokens don't match. fromClient: " + fromClient); } } else if (client != null) { - if (DEBUG) Slog.v(getTag(), "can't cancel non-authenticating client " + if (DEBUG) Slog.v(getTag(), "Can't cancel non-authenticating client " + client.getOwnerString()); } }); @@ -805,8 +822,7 @@ public abstract class BiometricServiceBase extends SystemService int lockoutMode = getLockoutMode(); if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) { - Slog.v(getTag(), "In lockout mode(" + lockoutMode + - ") ; disallowing authentication"); + Slog.v(getTag(), "In lockout mode(" + lockoutMode + ") ; disallowing authentication"); int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; @@ -919,7 +935,6 @@ public abstract class BiometricServiceBase extends SystemService if (currentClient != null) { if (DEBUG) Slog.v(getTag(), "request stop current client " + currentClient.getOwnerString()); - // This check only matters for FingerprintService, since enumerate may call back // multiple times. if (currentClient instanceof FingerprintService.EnumerateClientImpl || @@ -940,15 +955,49 @@ public abstract class BiometricServiceBase extends SystemService mHandler.removeCallbacks(mResetClientState); mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT); } else if (newClient != null) { + // For BiometricPrompt clients, do not start until + // <Biometric>Service#startPreparedClient is called. BiometricService waits until all + // modalities are ready before initiating authentication. + if (newClient instanceof AuthenticationClient) { + AuthenticationClient client = (AuthenticationClient) newClient; + if (client.isBiometricPrompt()) { + if (DEBUG) Slog.v(getTag(), "Returning cookie: " + client.getCookie()); + mCurrentClient = newClient; + if (mBiometricService == null) { + mBiometricService = IBiometricService.Stub.asInterface( + ServiceManager.getService(Context.BIOMETRIC_SERVICE)); + } + try { + mBiometricService.onReadyForAuthentication(client.getCookie(), + client.getRequireConfirmation(), client.getTargetUserId()); + } catch (RemoteException e) { + Slog.e(getTag(), "Remote exception", e); + } + return; + } + } + + // We are not a BiometricPrompt client, start the client immediately mCurrentClient = newClient; - if (DEBUG) Slog.v(getTag(), "starting client " - + newClient.getClass().getSuperclass().getSimpleName() - + "(" + newClient.getOwnerString() + ")" - + ", initiatedByClient = " + initiatedByClient); - notifyClientActiveCallbacks(true); + startCurrentClient(mCurrentClient.getCookie()); + } + } - newClient.start(); + protected void startCurrentClient(int cookie) { + if (mCurrentClient == null) { + Slog.e(getTag(), "Trying to start null client!"); + return; + } + if (DEBUG) Slog.v(getTag(), "starting client " + + mCurrentClient.getClass().getSuperclass().getSimpleName() + + "(" + mCurrentClient.getOwnerString() + ")" + + " cookie: " + cookie + "/" + mCurrentClient.getCookie()); + if (cookie != mCurrentClient.getCookie()) { + Slog.e(getTag(), "Mismatched cookie"); + return; } + notifyClientActiveCallbacks(true); + mCurrentClient.start(); } protected void removeClient(ClientMonitor client) { diff --git a/services/core/java/com/android/server/biometrics/ClientMonitor.java b/services/core/java/com/android/server/biometrics/ClientMonitor.java index a7ada2f6556c..d19aff69b832 100644 --- a/services/core/java/com/android/server/biometrics/ClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/ClientMonitor.java @@ -58,6 +58,9 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient { private IBinder mToken; private BiometricServiceBase.ServiceListener mListener; + // Currently only used for authentication client. The cookie generated by BiometricService + // is never 0. + private final int mCookie; protected final MetricsLogger mMetricsLogger; protected final Metrics mMetrics; @@ -80,7 +83,7 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient { public ClientMonitor(Context context, Metrics metrics, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int userId, int groupId, - boolean restricted, String owner) { + boolean restricted, String owner, int cookie) { mContext = context; mMetrics = metrics; mDaemon = daemon; @@ -91,6 +94,7 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient { mGroupId = groupId; mIsRestricted = restricted; mOwner = owner; + mCookie = cookie; mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); mMetricsLogger = new MetricsLogger(); @@ -107,6 +111,10 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient { return mMetrics.logTag(); } + public int getCookie() { + return mCookie; + } + /** * Contacts the biometric's HAL to start the client. * @return 0 on success, errno from driver on failure @@ -174,7 +182,7 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient { public boolean onError(long deviceId, int error, int vendorCode) { try { if (mListener != null) { - mListener.onError(deviceId, error, vendorCode); + mListener.onError(deviceId, error, vendorCode, getCookie()); } } catch (RemoteException e) { Slog.w(getLogTag(), "Failed to invoke sendError", e); diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java index 76dc5a91bdbe..f858ef5ec6f8 100644 --- a/services/core/java/com/android/server/biometrics/EnrollClient.java +++ b/services/core/java/com/android/server/biometrics/EnrollClient.java @@ -40,7 +40,7 @@ public abstract class EnrollClient extends ClientMonitor { BiometricServiceBase.ServiceListener listener, int userId, int groupId, byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils) { super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted, - owner); + owner, 0 /* cookie */); mBiometricUtils = utils; mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length); } diff --git a/services/core/java/com/android/server/biometrics/EnumerateClient.java b/services/core/java/com/android/server/biometrics/EnumerateClient.java index 47dc7ffda456..df6220cfd94b 100644 --- a/services/core/java/com/android/server/biometrics/EnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/EnumerateClient.java @@ -34,7 +34,7 @@ public abstract class EnumerateClient extends ClientMonitor { BiometricServiceBase.ServiceListener listener, int groupId, int userId, boolean restricted, String owner) { super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted, - owner); + owner, 0 /* cookie */); } @Override diff --git a/services/core/java/com/android/server/biometrics/RemovalClient.java b/services/core/java/com/android/server/biometrics/RemovalClient.java index 15b3773314bf..be233ec89342 100644 --- a/services/core/java/com/android/server/biometrics/RemovalClient.java +++ b/services/core/java/com/android/server/biometrics/RemovalClient.java @@ -37,7 +37,7 @@ public abstract class RemovalClient extends ClientMonitor { BiometricServiceBase.ServiceListener listener, int biometricId, int groupId, int userId, boolean restricted, String owner, BiometricUtils utils) { super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted, - owner); + owner, 0 /* cookie */); mBiometricId = biometricId; mBiometricUtils = utils; } diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index 7aa2e47300dd..557af0478b87 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -27,9 +27,8 @@ import android.content.Context; import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; -import android.hardware.biometrics.IBiometricPromptReceiver; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; -import android.hardware.biometrics.IBiometricServiceReceiver; +import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback; import android.hardware.biometrics.face.V1_0.Status; @@ -38,7 +37,6 @@ import android.hardware.face.FaceManager; import android.hardware.face.IFaceService; import android.hardware.face.IFaceServiceReceiver; import android.os.Binder; -import android.os.Bundle; import android.os.Environment; import android.os.IBinder; import android.os.RemoteException; @@ -50,7 +48,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; -import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.DumpUtils; import com.android.server.SystemServerInitThreadPool; import com.android.server.biometrics.BiometricServiceBase; @@ -89,27 +86,9 @@ public class FaceService extends BiometricServiceBase { public FaceAuthClient(Context context, DaemonWrapper daemon, long halDeviceId, IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId, - boolean restricted, String owner, Bundle bundle, - IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService, - boolean requireConfirmation) { + boolean restricted, String owner, int cookie, boolean requireConfirmation) { super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId, - restricted, owner, bundle, dialogReceiver, statusBarService, - requireConfirmation); - } - - @Override - public String getErrorString(int error, int vendorCode) { - return FaceManager.getErrorString(getContext(), error, vendorCode); - } - - @Override - public String getAcquiredString(int acquireInfo, int vendorCode) { - return FaceManager.getAcquiredString(getContext(), acquireInfo, vendorCode); - } - - @Override - public int getBiometricType() { - return BiometricAuthenticator.TYPE_FACE; + restricted, owner, cookie, requireConfirmation); } } @@ -162,28 +141,33 @@ public class FaceService extends BiometricServiceBase { final AuthenticationClientImpl client = new FaceAuthClient(getContext(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, - null /* bundle */, null /* dialogReceiver */, mStatusBarService, - false /* requireConfirmation */); + 0 /* cookie */, false /* requireConfirmation */); authenticateInternal(client, opId, opPackageName); } @Override // Binder call - public void authenticateFromService(boolean requireConfirmation, IBinder token, long opId, - int groupId, IBiometricServiceReceiver receiver, int flags, - String opPackageName, Bundle bundle, IBiometricPromptReceiver dialogReceiver, - int callingUid, int callingPid, int callingUserId) { + public void prepareForAuthentication(boolean requireConfirmation, IBinder token, long opId, + int groupId, IBiometricServiceReceiverInternal wrapperReceiver, + String opPackageName, int cookie, int callingUid, int callingPid, + int callingUserId) { checkPermission(USE_BIOMETRIC_INTERNAL); final boolean restricted = true; // BiometricPrompt is always restricted final AuthenticationClientImpl client = new FaceAuthClient(getContext(), mDaemonWrapper, mHalDeviceId, token, - new BiometricPromptServiceListenerImpl(receiver), - mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, - bundle, dialogReceiver, mStatusBarService, true /* requireConfirmation */); + new BiometricPromptServiceListenerImpl(wrapperReceiver), + mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, cookie, + true /* requireConfirmation */); authenticateInternal(client, opId, opPackageName, callingUid, callingPid, callingUserId); } @Override // Binder call + public void startPreparedClient(int cookie) { + checkPermission(MANAGE_BIOMETRIC); + startCurrentClient(cookie); + } + + @Override // Binder call public void cancelAuthentication(final IBinder token, final String opPackageName) { checkPermission(USE_BIOMETRIC_INTERNAL); cancelAuthenticationInternal(token, opPackageName); @@ -191,10 +175,10 @@ public class FaceService extends BiometricServiceBase { @Override // Binder call public void cancelAuthenticationFromService(final IBinder token, final String opPackageName, - int callingUid, int callingPid, int callingUserId) { + int callingUid, int callingPid, int callingUserId, boolean fromClient) { checkPermission(USE_BIOMETRIC_INTERNAL); - cancelAuthenticationInternal(token, opPackageName, - callingUid, callingPid, callingUserId); + cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, + callingUserId, fromClient); } @Override // Binder call @@ -405,12 +389,9 @@ public class FaceService extends BiometricServiceBase { * Receives callbacks from the ClientMonitor implementations. The results are forwarded to * BiometricPrompt. */ - private class BiometricPromptServiceListenerImpl implements ServiceListener { - - private IBiometricServiceReceiver mBiometricServiceReceiver; - - public BiometricPromptServiceListenerImpl(IBiometricServiceReceiver receiver) { - mBiometricServiceReceiver = receiver; + private class BiometricPromptServiceListenerImpl extends BiometricServiceListener { + BiometricPromptServiceListenerImpl(IBiometricServiceReceiverInternal wrapperReceiver) { + super(wrapperReceiver); } @Override @@ -419,32 +400,18 @@ public class FaceService extends BiometricServiceBase { /** * Map the acquired codes onto existing {@link BiometricConstants} acquired codes. */ - if (mBiometricServiceReceiver != null) { - mBiometricServiceReceiver.onAcquired(deviceId, + if (getWrapperReceiver() != null) { + getWrapperReceiver().onAcquired( FaceManager.getMappedAcquiredInfo(acquiredInfo, vendorCode), FaceManager.getAcquiredString(getContext(), acquiredInfo, vendorCode)); } } @Override - public void onAuthenticationSucceeded(long deviceId, - BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException { - if (mBiometricServiceReceiver != null) { - mBiometricServiceReceiver.onAuthenticationSucceeded(deviceId); - } - } - - @Override - public void onAuthenticationFailed(long deviceId) throws RemoteException { - if (mBiometricServiceReceiver != null) { - mBiometricServiceReceiver.onAuthenticationFailed(deviceId); - } - } - - @Override - public void onError(long deviceId, int error, int vendorCode) throws RemoteException { - if (mBiometricServiceReceiver != null) { - mBiometricServiceReceiver.onError(deviceId, error, + public void onError(long deviceId, int error, int vendorCode, int cookie) + throws RemoteException { + if (getWrapperReceiver() != null) { + getWrapperReceiver().onError(cookie, error, FaceManager.getErrorString(getContext(), error, vendorCode)); } } @@ -455,7 +422,6 @@ public class FaceService extends BiometricServiceBase { * the FaceManager. */ private class ServiceListenerImpl implements ServiceListener { - private IFaceServiceReceiver mFaceServiceReceiver; public ServiceListenerImpl(IFaceServiceReceiver receiver) { @@ -501,7 +467,8 @@ public class FaceService extends BiometricServiceBase { } @Override - public void onError(long deviceId, int error, int vendorCode) throws RemoteException { + public void onError(long deviceId, int error, int vendorCode, int cookie) + throws RemoteException { if (mFaceServiceReceiver != null) { mFaceServiceReceiver.onError(deviceId, error, vendorCode); } diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java index b0b788fbe589..6a5bc61f2eb6 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java @@ -30,9 +30,8 @@ import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; -import android.hardware.biometrics.IBiometricPromptReceiver; -import android.hardware.biometrics.IBiometricServiceReceiver; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; +import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback; import android.hardware.fingerprint.Fingerprint; @@ -42,7 +41,6 @@ import android.hardware.fingerprint.IFingerprintService; import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.os.Binder; import android.os.Build; -import android.os.Bundle; import android.os.Environment; import android.os.IBinder; import android.os.RemoteException; @@ -55,7 +53,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; -import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.DumpUtils; import com.android.server.SystemServerInitThreadPool; import com.android.server.biometrics.AuthenticationClient; @@ -109,27 +106,10 @@ public class FingerprintService extends BiometricServiceBase { public FingerprintAuthClient(Context context, DaemonWrapper daemon, long halDeviceId, IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId, - boolean restricted, String owner, Bundle bundle, - IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService, + boolean restricted, String owner, int cookie, boolean requireConfirmation) { super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId, - restricted, owner, bundle, dialogReceiver, statusBarService, - requireConfirmation); - } - - @Override - public String getErrorString(int error, int vendorCode) { - return FingerprintManager.getErrorString(getContext(), error, vendorCode); - } - - @Override - public String getAcquiredString(int acquireInfo, int vendorCode) { - return FingerprintManager.getAcquiredString(getContext(), acquireInfo, vendorCode); - } - - @Override - public int getBiometricType() { - return BiometricAuthenticator.TYPE_FINGERPRINT; + restricted, owner, cookie, requireConfirmation); } } @@ -182,38 +162,44 @@ public class FingerprintService extends BiometricServiceBase { final boolean restricted = isRestricted(); final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), - mCurrentUserId, groupId, opId, restricted, opPackageName, null /* bundle */, - null /* dialogReceiver */, mStatusBarService, false /* requireConfirmation */); + mCurrentUserId, groupId, opId, restricted, opPackageName, + 0 /* cookie */, false /* requireConfirmation */); authenticateInternal(client, opId, opPackageName); } @Override // Binder call - public void authenticateFromService(IBinder token, long opId, int groupId, - IBiometricServiceReceiver receiver, int flags, String opPackageName, - Bundle bundle, IBiometricPromptReceiver dialogReceiver, - int callingUid, int callingPid, int callingUserId) { + public void prepareForAuthentication(IBinder token, long opId, int groupId, + IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName, + int cookie, int callingUid, int callingPid, int callingUserId) { checkPermission(MANAGE_BIOMETRIC); final boolean restricted = true; // BiometricPrompt is always restricted final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(), mDaemonWrapper, mHalDeviceId, token, - new BiometricPromptServiceListenerImpl(receiver), - mCurrentUserId, groupId, opId, restricted, opPackageName, bundle, - dialogReceiver, mStatusBarService, false /* requireConfirmation */); + new BiometricPromptServiceListenerImpl(wrapperReceiver), + mCurrentUserId, groupId, opId, restricted, opPackageName, cookie, + false /* requireConfirmation */); authenticateInternal(client, opId, opPackageName, callingUid, callingPid, callingUserId); } @Override // Binder call + public void startPreparedClient(int cookie) { + checkPermission(MANAGE_BIOMETRIC); + startCurrentClient(cookie); + } + + + @Override // Binder call public void cancelAuthentication(final IBinder token, final String opPackageName) { cancelAuthenticationInternal(token, opPackageName); } @Override // Binder call public void cancelAuthenticationFromService(final IBinder token, final String opPackageName, - int callingUid, int callingPid, int callingUserId) { + int callingUid, int callingPid, int callingUserId, boolean fromClient) { checkPermission(MANAGE_BIOMETRIC); - cancelAuthenticationInternal(token, opPackageName, - callingUid, callingPid, callingUserId); + cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, + callingUserId, fromClient); } @Override // Binder call @@ -388,43 +374,25 @@ public class FingerprintService extends BiometricServiceBase { * Receives callbacks from the ClientMonitor implementations. The results are forwarded to * BiometricPrompt. */ - private class BiometricPromptServiceListenerImpl implements ServiceListener { - - private IBiometricServiceReceiver mBiometricServiceReceiver; - - public BiometricPromptServiceListenerImpl(IBiometricServiceReceiver receiver) { - mBiometricServiceReceiver = receiver; + private class BiometricPromptServiceListenerImpl extends BiometricServiceListener { + BiometricPromptServiceListenerImpl(IBiometricServiceReceiverInternal wrapperReceiver) { + super(wrapperReceiver); } @Override public void onAcquired(long deviceId, int acquiredInfo, int vendorCode) throws RemoteException { - if (mBiometricServiceReceiver != null) { - mBiometricServiceReceiver.onAcquired(deviceId, acquiredInfo, - FingerprintManager.getAcquiredString( + if (getWrapperReceiver() != null) { + getWrapperReceiver().onAcquired(acquiredInfo, FingerprintManager.getAcquiredString( getContext(), acquiredInfo, vendorCode)); } } @Override - public void onAuthenticationSucceeded(long deviceId, - BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException { - if (mBiometricServiceReceiver != null) { - mBiometricServiceReceiver.onAuthenticationSucceeded(deviceId); - } - } - - @Override - public void onAuthenticationFailed(long deviceId) throws RemoteException { - if (mBiometricServiceReceiver != null) { - mBiometricServiceReceiver.onAuthenticationFailed(deviceId); - } - } - - @Override - public void onError(long deviceId, int error, int vendorCode) throws RemoteException { - if (mBiometricServiceReceiver != null) { - mBiometricServiceReceiver.onError(deviceId, error, + public void onError(long deviceId, int error, int vendorCode, int cookie) + throws RemoteException { + if (getWrapperReceiver() != null) { + getWrapperReceiver().onError(cookie, error, FingerprintManager.getErrorString(getContext(), error, vendorCode)); } } @@ -435,7 +403,6 @@ public class FingerprintService extends BiometricServiceBase { * the FingerprintManager. */ private class ServiceListenerImpl implements ServiceListener { - private IFingerprintServiceReceiver mFingerprintServiceReceiver; public ServiceListenerImpl(IFingerprintServiceReceiver receiver) { @@ -483,7 +450,8 @@ public class FingerprintService extends BiometricServiceBase { } @Override - public void onError(long deviceId, int error, int vendorCode) throws RemoteException { + public void onError(long deviceId, int error, int vendorCode, int cookie) + throws RemoteException { if (mFingerprintServiceReceiver != null) { mFingerprintServiceReceiver.onError(deviceId, error, vendorCode); } 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/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/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/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 7750c3781067..0d6dadfb1ad8 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -554,7 +554,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final Handler mHandler; @VisibleForTesting - public final Handler mUidEventHandler; + final Handler mUidEventHandler; private final ServiceThread mUidEventThread; @@ -1465,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(() -> { @@ -1510,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; @@ -1542,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; @@ -1575,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; @@ -2039,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()); @@ -3489,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)); @@ -3931,7 +3931,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * power saving restrictions may still apply. */ @VisibleForTesting - public void setAppIdleWhitelist(int uid, boolean shouldWhitelist) { + void setAppIdleWhitelist(int uid, boolean shouldWhitelist) { + mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + synchronized (mUidRulesFirstLock) { if (mAppIdleTempWhitelistAppIds.get(uid) == shouldWhitelist) { // No change. @@ -3956,7 +3958,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** Return the list of UIDs currently in the app idle whitelist. */ @VisibleForTesting - public int[] getAppIdleWhitelist() { + int[] getAppIdleWhitelist() { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); synchronized (mUidRulesFirstLock) { @@ -3971,7 +3973,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** Returns if the UID is currently considered idle. */ @VisibleForTesting - public boolean isUidIdle(int uid) { + boolean isUidIdle(int uid) { synchronized (mUidRulesFirstLock) { if (mAppIdleTempWhitelistAppIds.get(uid)) { // UID is temporarily whitelisted. @@ -4844,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; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index ae27d0c07ea8..6c2549e9b04d 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -198,6 +198,7 @@ import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.BackgroundThread; +import com.android.internal.os.SomeArgs; import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; @@ -206,6 +207,7 @@ import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.DeviceIdleController; import com.android.server.EventLogTags; +import com.android.server.IoThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.lights.Light; @@ -264,11 +266,12 @@ public class NotificationManagerService extends SystemService { // message codes static final int MESSAGE_DURATION_REACHED = 2; - static final int MESSAGE_SAVE_POLICY_FILE = 3; + // 3: removed to a different handler static final int MESSAGE_SEND_RANKING_UPDATE = 4; static final int MESSAGE_LISTENER_HINTS_CHANGED = 5; static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6; static final int MESSAGE_FINISH_TOKEN_TIMEOUT = 7; + static final int MESSAGE_ON_PACKAGE_CHANGED = 8; // ranking thread messages private static final int MESSAGE_RECONSIDER_RANKING = 1000; @@ -570,7 +573,7 @@ public class NotificationManagerService extends SystemService { mListeners.migrateToXml(); mAssistants.migrateToXml(); mConditionProviders.migrateToXml(); - savePolicyFile(); + handleSavePolicyFile(); } mAssistants.ensureAssistant(); @@ -600,31 +603,28 @@ public class NotificationManagerService extends SystemService { } } - public void savePolicyFile() { - mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE); - mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE); - } - private void handleSavePolicyFile() { - if (DBG) Slog.d(TAG, "handleSavePolicyFile"); - synchronized (mPolicyFile) { - final FileOutputStream stream; - try { - stream = mPolicyFile.startWrite(); - } catch (IOException e) { - Slog.w(TAG, "Failed to save policy file", e); - return; - } + IoThread.getHandler().post(() -> { + if (DBG) Slog.d(TAG, "handleSavePolicyFile"); + synchronized (mPolicyFile) { + final FileOutputStream stream; + try { + stream = mPolicyFile.startWrite(); + } catch (IOException e) { + Slog.w(TAG, "Failed to save policy file", e); + return; + } - try { - writePolicyXml(stream, false /*forBackup*/); - mPolicyFile.finishWrite(stream); - } catch (IOException e) { - Slog.w(TAG, "Failed to save policy file, restoring backup", e); - mPolicyFile.failWrite(stream); + try { + writePolicyXml(stream, false /*forBackup*/); + mPolicyFile.finishWrite(stream); + } catch (IOException e) { + Slog.w(TAG, "Failed to save policy file, restoring backup", e); + mPolicyFile.failWrite(stream); + } } - } - BackupManager.dataChanged(getContext().getPackageName()); + BackupManager.dataChanged(getContext().getPackageName()); + }); } private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException { @@ -1133,12 +1133,7 @@ public class NotificationManagerService extends SystemService { } } - mListeners.onPackagesChanged(removingPackage, pkgList, uidList); - mAssistants.onPackagesChanged(removingPackage, pkgList, uidList); - mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList); - mPreferencesHelper.onPackagesChanged( - removingPackage, changeUserId, pkgList, uidList); - savePolicyFile(); + mHandler.scheduleOnPackageChanged(removingPackage, changeUserId, pkgList, uidList); } } }; @@ -1205,7 +1200,7 @@ public class NotificationManagerService extends SystemService { mListeners.onUserRemoved(userId); mConditionProviders.onUserRemoved(userId); mAssistants.onUserRemoved(userId); - savePolicyFile(); + handleSavePolicyFile(); } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) { final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); mUserProfiles.updateCache(context); @@ -1456,7 +1451,7 @@ public class NotificationManagerService extends SystemService { mZenModeHelper.addCallback(new ZenModeHelper.Callback() { @Override public void onConfigChanged() { - savePolicyFile(); + handleSavePolicyFile(); } @Override @@ -1755,7 +1750,7 @@ public class NotificationManagerService extends SystemService { modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED); } - savePolicyFile(); + handleSavePolicyFile(); } private void maybeNotifyChannelOwner(String pkg, int uid, NotificationChannel preUpdate, @@ -2223,7 +2218,7 @@ public class NotificationManagerService extends SystemService { Slog.w(TAG, "Can't notify app about app block change", e); } - savePolicyFile(); + handleSavePolicyFile(); } /** @@ -2280,7 +2275,7 @@ public class NotificationManagerService extends SystemService { public void setShowBadge(String pkg, int uid, boolean showBadge) { checkCallerIsSystem(); mPreferencesHelper.setShowBadge(pkg, uid, showBadge); - savePolicyFile(); + handleSavePolicyFile(); } @Override @@ -2296,7 +2291,7 @@ public class NotificationManagerService extends SystemService { if (info != null) { mPreferencesHelper.setNotificationDelegate( callingPkg, callingUid, delegate, info.uid); - savePolicyFile(); + handleSavePolicyFile(); } } catch (RemoteException e) { // :( @@ -2307,7 +2302,7 @@ public class NotificationManagerService extends SystemService { public void revokeNotificationDelegate(String callingPkg) { checkCallerIsSameApp(callingPkg); mPreferencesHelper.revokeNotificationDelegate(callingPkg, Binder.getCallingUid()); - savePolicyFile(); + handleSavePolicyFile(); } @Override @@ -2342,7 +2337,7 @@ public class NotificationManagerService extends SystemService { NotificationChannelGroup group) throws RemoteException { enforceSystemOrSystemUI("Caller not system or systemui"); createNotificationChannelGroup(pkg, uid, group, false, false); - savePolicyFile(); + handleSavePolicyFile(); } @Override @@ -2355,7 +2350,7 @@ public class NotificationManagerService extends SystemService { final NotificationChannelGroup group = groups.get(i); createNotificationChannelGroup(pkg, Binder.getCallingUid(), group, true, false); } - savePolicyFile(); + handleSavePolicyFile(); } private void createNotificationChannelsImpl(String pkg, int uid, @@ -2373,7 +2368,7 @@ public class NotificationManagerService extends SystemService { mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), false), NOTIFICATION_CHANNEL_OR_GROUP_ADDED); } - savePolicyFile(); + handleSavePolicyFile(); } @Override @@ -2418,7 +2413,7 @@ public class NotificationManagerService extends SystemService { UserHandle.getUserHandleForUid(callingUid), mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true), NOTIFICATION_CHANNEL_OR_GROUP_DELETED); - savePolicyFile(); + handleSavePolicyFile(); } @Override @@ -2460,7 +2455,7 @@ public class NotificationManagerService extends SystemService { mListeners.notifyNotificationChannelGroupChanged( pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete, NOTIFICATION_CHANNEL_OR_GROUP_DELETED); - savePolicyFile(); + handleSavePolicyFile(); } } @@ -2593,7 +2588,7 @@ public class NotificationManagerService extends SystemService { true, UserHandle.getCallingUserId(), packages, uids); } - savePolicyFile(); + handleSavePolicyFile(); } @@ -3381,7 +3376,7 @@ public class NotificationManagerService extends SystemService { final ByteArrayInputStream bais = new ByteArrayInputStream(payload); try { readPolicyXml(bais, true /*forRestore*/); - savePolicyFile(); + handleSavePolicyFile(); } catch (NumberFormatException | XmlPullParserException | IOException e) { Slog.w(TAG, "applyRestore: error reading payload", e); } @@ -3422,7 +3417,7 @@ public class NotificationManagerService extends SystemService { .setPackage(pkg) .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.of(userId), null); - savePolicyFile(); + handleSavePolicyFile(); } } finally { Binder.restoreCallingIdentity(identity); @@ -3566,7 +3561,7 @@ public class NotificationManagerService extends SystemService { .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.of(userId), null); - savePolicyFile(); + handleSavePolicyFile(); } } finally { Binder.restoreCallingIdentity(identity); @@ -3592,7 +3587,7 @@ public class NotificationManagerService extends SystemService { .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.of(userId), null); - savePolicyFile(); + handleSavePolicyFile(); } } finally { Binder.restoreCallingIdentity(identity); @@ -3675,7 +3670,7 @@ public class NotificationManagerService extends SystemService { verifyPrivilegedListener(token, user, false); createNotificationChannelGroup( pkg, getUidForPackageAndUser(pkg, user), group, false, true); - savePolicyFile(); + handleSavePolicyFile(); } @Override @@ -3724,7 +3719,7 @@ public class NotificationManagerService extends SystemService { } if (allow != mLockScreenAllowSecureNotifications) { mLockScreenAllowSecureNotifications = allow; - savePolicyFile(); + handleSavePolicyFile(); } } @@ -4247,18 +4242,7 @@ public class NotificationManagerService extends SystemService { // Fix the notification as best we can. try { - final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser( - pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, - (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId); - Notification.addFieldsFromContext(ai, notification); - - int canColorize = mPackageManagerClient.checkPermission( - android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg); - if (canColorize == PERMISSION_GRANTED) { - notification.flags |= Notification.FLAG_CAN_COLORIZE; - } else { - notification.flags &= ~Notification.FLAG_CAN_COLORIZE; - } + fixNotification(notification, pkg, userId); } catch (NameNotFoundException e) { Slog.e(TAG, "Cannot create a context for sending app", e); @@ -4359,6 +4343,33 @@ public class NotificationManagerService extends SystemService { mHandler.post(new EnqueueNotificationRunnable(userId, r)); } + @VisibleForTesting + protected void fixNotification(Notification notification, String pkg, int userId) + throws NameNotFoundException { + final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser( + pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, + (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId); + Notification.addFieldsFromContext(ai, notification); + + int canColorize = mPackageManagerClient.checkPermission( + android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg); + if (canColorize == PERMISSION_GRANTED) { + notification.flags |= Notification.FLAG_CAN_COLORIZE; + } else { + notification.flags &= ~Notification.FLAG_CAN_COLORIZE; + } + + if (ai.targetSdkVersion >= Build.VERSION_CODES.Q) { + int fullscreenIntentPermission = mPackageManagerClient.checkPermission( + android.Manifest.permission.USE_FULL_SCREEN_INTENT, pkg); + if (fullscreenIntentPermission != PERMISSION_GRANTED) { + notification.fullScreenIntent = null; + Log.w(TAG, "Package " + pkg + + ": Use of fullScreenIntent requires the USE_FULL_SCREEN_INTENT permission"); + } + } + } + private void doChannelWarningToast(CharSequence toastText) { Binder.withCleanCallingIdentity(() -> { final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0; @@ -4467,7 +4478,7 @@ public class NotificationManagerService extends SystemService { Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey()); } mSnoozeHelper.update(userId, r); - savePolicyFile(); + handleSavePolicyFile(); return false; } @@ -4598,7 +4609,7 @@ public class NotificationManagerService extends SystemService { mSnoozeHelper.snooze(r, mDuration); } r.recordSnoozed(); - savePolicyFile(); + handleSavePolicyFile(); } } @@ -4676,7 +4687,7 @@ public class NotificationManagerService extends SystemService { if (mReason != REASON_SNOOZED) { final boolean wasSnoozed = mSnoozeHelper.cancel(mUserId, mPkg, mTag, mId); if (wasSnoozed) { - savePolicyFile(); + handleSavePolicyFile(); } } } @@ -5691,6 +5702,16 @@ public class NotificationManagerService extends SystemService { } } + private void handleOnPackageChanged(boolean removingPackage, int changeUserId, + String[] pkgList, int[] uidList) { + mListeners.onPackagesChanged(removingPackage, pkgList, uidList); + mAssistants.onPackagesChanged(removingPackage, pkgList, uidList); + mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList); + mPreferencesHelper.onPackagesChanged( + removingPackage, changeUserId, pkgList, uidList); + handleSavePolicyFile(); + } + protected class WorkerHandler extends Handler { public WorkerHandler(Looper looper) { @@ -5703,13 +5724,10 @@ public class NotificationManagerService extends SystemService { switch (msg.what) { case MESSAGE_DURATION_REACHED: - handleDurationReached((ToastRecord)msg.obj); + handleDurationReached((ToastRecord) msg.obj); break; case MESSAGE_FINISH_TOKEN_TIMEOUT: - handleKillTokenTimeout((ToastRecord)msg.obj); - break; - case MESSAGE_SAVE_POLICY_FILE: - handleSavePolicyFile(); + handleKillTokenTimeout((ToastRecord) msg.obj); break; case MESSAGE_SEND_RANKING_UPDATE: handleSendRankingUpdate(); @@ -5720,6 +5738,12 @@ public class NotificationManagerService extends SystemService { case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED: handleListenerInterruptionFilterChanged(msg.arg1); break; + case MESSAGE_ON_PACKAGE_CHANGED: + SomeArgs args = (SomeArgs) msg.obj; + handleOnPackageChanged((boolean) args.arg1, args.argi1, (String[]) args.arg2, + (int[]) args.arg3); + args.recycle(); + break; } } @@ -5735,6 +5759,16 @@ public class NotificationManagerService extends SystemService { sendMessage(Message.obtain(this, cancelRunnable)); } } + + protected void scheduleOnPackageChanged(boolean removingPackage, int changeUserId, + String[] pkgList, int[] uidList) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = removingPackage; + args.argi1 = changeUserId; + args.arg2 = pkgList; + args.arg3 = uidList; + sendMessage(Message.obtain(this, MESSAGE_ON_PACKAGE_CHANGED, args)); + } } private final class RankingHandlerWorker extends Handler implements RankingHandler @@ -6212,7 +6246,7 @@ public class NotificationManagerService extends SystemService { Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName)); } mSnoozeHelper.repost(key); - savePolicyFile(); + handleSavePolicyFile(); } @GuardedBy("mNotificationLock") 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/oemlock/OemLock.java b/services/core/java/com/android/server/oemlock/OemLock.java index ee70c29b7da4..352884b606a0 100644 --- a/services/core/java/com/android/server/oemlock/OemLock.java +++ b/services/core/java/com/android/server/oemlock/OemLock.java @@ -19,6 +19,9 @@ package com.android.server.oemlock; import android.annotation.Nullable; abstract class OemLock { + @Nullable + abstract String getLockName(); + abstract void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature); abstract boolean isOemUnlockAllowedByCarrier(); diff --git a/services/core/java/com/android/server/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java index a6200bf433e4..6e82c24a6e9b 100644 --- a/services/core/java/com/android/server/oemlock/OemLockService.java +++ b/services/core/java/com/android/server/oemlock/OemLockService.java @@ -113,6 +113,19 @@ public class OemLockService extends SystemService { */ private final IBinder mService = new IOemLockService.Stub() { @Override + @Nullable + public String getLockName() { + enforceManageCarrierOemUnlockPermission(); + + final long token = Binder.clearCallingIdentity(); + try { + return mOemLock.getLockName(); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override public void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature) { enforceManageCarrierOemUnlockPermission(); enforceUserIsAdmin(); diff --git a/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java b/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java index d9362d4b0f28..a1c27d6432f1 100644 --- a/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java +++ b/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java @@ -40,6 +40,12 @@ class PersistentDataBlockLock extends OemLock { } @Override + @Nullable + String getLockName() { + return ""; + } + + @Override void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature) { // Note: this implementation does not require a signature if (signature != null) { diff --git a/services/core/java/com/android/server/oemlock/VendorLock.java b/services/core/java/com/android/server/oemlock/VendorLock.java index 1b9de3612367..37540d039b9e 100644 --- a/services/core/java/com/android/server/oemlock/VendorLock.java +++ b/services/core/java/com/android/server/oemlock/VendorLock.java @@ -22,8 +22,6 @@ import android.hardware.oemlock.V1_0.IOemLock; import android.hardware.oemlock.V1_0.OemLockSecureStatus; import android.hardware.oemlock.V1_0.OemLockStatus; import android.os.RemoteException; -import android.os.UserHandle; -import android.os.UserManager; import android.util.Slog; import java.util.ArrayList; @@ -55,14 +53,49 @@ class VendorLock extends OemLock { } @Override + @Nullable + String getLockName() { + final Integer[] requestStatus = new Integer[1]; + final String[] lockName = new String[1]; + + try { + mOemLock.getName((status, name) -> { + requestStatus[0] = status; + lockName[0] = name; + }); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to get name from HAL", e); + throw e.rethrowFromSystemServer(); + } + + switch (requestStatus[0]) { + case OemLockStatus.OK: + // Success + return lockName[0]; + + case OemLockStatus.FAILED: + Slog.e(TAG, "Failed to get OEM lock name."); + return null; + + default: + Slog.e(TAG, "Unknown return value indicates code is out of sync with HAL"); + return null; + } + } + + @Override void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature) { try { - switch (mOemLock.setOemUnlockAllowedByCarrier(allowed, toByteArrayList(signature))) { + ArrayList<Byte> signatureBytes = toByteArrayList(signature); + switch (mOemLock.setOemUnlockAllowedByCarrier(allowed, signatureBytes)) { case OemLockSecureStatus.OK: Slog.i(TAG, "Updated carrier allows OEM lock state to: " + allowed); return; case OemLockSecureStatus.INVALID_SIGNATURE: + if (signatureBytes.isEmpty()) { + throw new IllegalArgumentException("Signature required for carrier unlock"); + } throw new SecurityException( "Invalid signature used in attempt to carrier unlock"); @@ -154,9 +187,9 @@ class VendorLock extends OemLock { } } - private ArrayList toByteArrayList(byte[] data) { + private ArrayList<Byte> toByteArrayList(byte[] data) { if (data == null) { - return null; + return new ArrayList<Byte>(); } ArrayList<Byte> result = new ArrayList<Byte>(data.length); for (final byte b : data) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 1045289ca5c1..8abb5000d544 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -116,8 +116,7 @@ import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures; import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE; import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS; -import static com.android.server.pm.permission.PermissionsState - .PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED; +import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED; import android.Manifest; import android.annotation.IntDef; @@ -197,6 +196,7 @@ import android.content.pm.SharedLibraryInfo; import android.content.pm.Signature; import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; +import android.content.pm.UsesPermissionInfo; import android.content.pm.VerifierDeviceIdentity; import android.content.pm.VerifierInfo; import android.content.pm.VersionedPackage; @@ -313,8 +313,7 @@ import com.android.server.pm.dex.DexoptOptions; import com.android.server.pm.dex.PackageDexUsage; import com.android.server.pm.permission.BasePermission; import com.android.server.pm.permission.DefaultPermissionGrantPolicy; -import com.android.server.pm.permission.DefaultPermissionGrantPolicy - .DefaultPermissionGrantedCallback; +import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback; import com.android.server.pm.permission.PermissionManagerInternal; import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback; import com.android.server.pm.permission.PermissionManagerService; @@ -373,6 +372,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.function.Predicate; /** @@ -11242,6 +11242,26 @@ public class PackageManagerService extends IPackageManager.Stub } } } + + // Check permission usage info requirements. + if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q) { + for (UsesPermissionInfo upi : pkg.usesPermissionInfos) { + if (!mPermissionManager.isPermissionUsageInfoRequired(upi.getPermission())) { + continue; + } + if (upi.getDataSentOffDevice() == UsesPermissionInfo.USAGE_UNDEFINED + || upi.getDataSharedWithThirdParty() + == UsesPermissionInfo.USAGE_UNDEFINED + || upi.getDataUsedForMonetization() + == UsesPermissionInfo.USAGE_UNDEFINED + || upi.getDataRetention() == UsesPermissionInfo.RETENTION_UNDEFINED) { + // STOPSHIP: Make this throw + Slog.wtf(TAG, "Package " + pkg.packageName + " does not provide usage " + + "information for permission " + upi.getPermission() + + ". This will be a fatal error in Q."); + } + } + } } } @@ -15246,7 +15266,8 @@ public class PackageManagerService extends IPackageManager.Stub "inputs not balanced; missing argument for " + installPackageName); } final DeletePackageAction deletePackageAction; - if (prepareResult.replace) { + // 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); @@ -17818,7 +17839,7 @@ public class PackageManagerService extends IPackageManager.Stub return null; } if (isSystemApp(ps)) { - if (ps.parentPackageName == null) { + if (ps.parentPackageName != null) { Slog.w(TAG, "Attempt to delete child system package " + ps.pkg.packageName); return null; } @@ -20114,7 +20135,7 @@ public class PackageManagerService extends IPackageManager.Stub if (Process.isIsolated(uid)) { return Zygote.MOUNT_EXTERNAL_NONE; } - if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) { + if (StorageManager.hasIsolatedStorage()) { return checkUidPermission(WRITE_MEDIA_STORAGE, uid) == PERMISSION_GRANTED ? Zygote.MOUNT_EXTERNAL_FULL : Zygote.MOUNT_EXTERNAL_WRITE; @@ -23185,6 +23206,45 @@ public class PackageManagerService extends IPackageManager.Stub throws IOException { PackageManagerService.this.freeStorage(volumeUuid, bytes, storageFlags); } + + @Override + public void forEachPackage(Consumer<PackageParser.Package> actionLocked) { + PackageManagerService.this.forEachPackage(actionLocked); + } + + @Override + public ArraySet<String> getEnabledComponents(String packageName, int userId) { + synchronized (mPackages) { + PackageSetting setting = mSettings.getPackageLPr(packageName); + if (setting == null) { + return new ArraySet<>(); + } + return setting.getEnabledComponents(userId); + } + } + + @Override + public ArraySet<String> getDisabledComponents(String packageName, int userId) { + synchronized (mPackages) { + PackageSetting setting = mSettings.getPackageLPr(packageName); + if (setting == null) { + return new ArraySet<>(); + } + return setting.getDisabledComponents(userId); + } + } + + @Override + public @PackageManager.EnabledState int getApplicationEnabledState( + String packageName, int userId) { + synchronized (mPackages) { + PackageSetting setting = mSettings.getPackageLPr(packageName); + if (setting == null) { + return COMPONENT_ENABLED_STATE_DEFAULT; + } + return setting.getEnabled(userId); + } + } } @GuardedBy("mPackages") @@ -23307,6 +23367,15 @@ public class PackageManagerService extends IPackageManager.Stub } } + void forEachPackage(Consumer<PackageParser.Package> actionLocked) { + synchronized (mPackages) { + int numPackages = mPackages.size(); + for (int i = 0; i < numPackages; i++) { + actionLocked.accept(mPackages.valueAt(i)); + } + } + } + private static void enforceSystemOrPhoneCaller(String tag) { int callingUid = Binder.getCallingUid(); if (callingUid != Process.PHONE_UID && callingUid != Process.SYSTEM_UID) { 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..68fe1d8a05f8 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); @@ -184,7 +189,7 @@ public final class DefaultPermissionGrantPolicy { private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>(); static { // STOPSHIP(b/112545973): remove once feature enabled by default - if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) { + if (!StorageManager.hasIsolatedStorage()) { STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE); STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); } @@ -193,7 +198,7 @@ public final class DefaultPermissionGrantPolicy { private static final Set<String> MEDIA_AURAL_PERMISSIONS = new ArraySet<>(); static { // STOPSHIP(b/112545973): remove once feature enabled by default - if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) { + if (StorageManager.hasIsolatedStorage()) { MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_AUDIO); } } @@ -201,7 +206,7 @@ public final class DefaultPermissionGrantPolicy { private static final Set<String> MEDIA_VISUAL_PERMISSIONS = new ArraySet<>(); static { // STOPSHIP(b/112545973): remove once feature enabled by default - if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) { + if (StorageManager.hasIsolatedStorage()) { MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VIDEO); MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES); } @@ -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/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java index ec15c16981a8..189d0f476a8c 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java @@ -181,4 +181,9 @@ public abstract class PermissionManagerInternal { /** HACK HACK methods to allow for partial migration of data to the PermissionManager class */ public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName); -}
\ No newline at end of file + + /** + * Returns {@code true} if {@code permName} has {@code usageInfoRequired} set. + */ + public abstract boolean isPermissionUsageInfoRequired(@NonNull String permName); +} diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index c5d38db2c469..4406fdde6454 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -2644,5 +2644,12 @@ public class PermissionManagerService { return mSettings.getPermissionLocked(permName); } } + @Override + public boolean isPermissionUsageInfoRequired(String permName) { + synchronized (PermissionManagerService.this.mLock) { + BasePermission bp = mSettings.getPermissionLocked(permName); + return bp != null && bp.usageInfoRequired; + } + } } } diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index b390eebf3d7e..b065470fe98c 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -30,20 +30,27 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManagerInternal; +import android.content.pm.Signature; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.UserHandle; import android.os.UserManagerInternal; import android.text.TextUtils; import android.util.ArraySet; +import android.util.PackageUtils; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.BitUtils; +import com.android.internal.util.CollectionUtils; +import com.android.internal.util.FunctionalUtils; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; import com.android.server.SystemService; +import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; import java.util.ArrayList; import java.util.Collections; @@ -117,13 +124,18 @@ public class RoleManagerService extends SystemService { @Override public void onStartUser(@UserIdInt int userId) { + RoleUserState userState; synchronized (mLock) { - //TODO only call into PermissionController if it or system upgreaded (for boot time) - getUserStateLocked(userId); + userState = getUserStateLocked(userId); } - //TODO consider calling grants only when certain conditions are met - // such as OS or PermissionController upgrade - if (RemoteRoleControllerService.DEBUG) { + String packagesHash = computeComponentStateHash(userId); + boolean needGrant; + synchronized (mLock) { + needGrant = !packagesHash.equals(userState.getLastGrantPackagesHashLocked()); + } + if (needGrant) { + // Some vital packages state has changed since last role grant + // Run grants again Slog.i(LOG_TAG, "Granting default permissions..."); CompletableFuture<Void> result = new CompletableFuture<>(); getControllerService(userId).onGrantDefaultRoles( @@ -140,12 +152,47 @@ public class RoleManagerService extends SystemService { }); try { result.get(5, TimeUnit.SECONDS); + synchronized (mLock) { + userState.setLastGrantPackagesHashLocked(packagesHash); + } } catch (InterruptedException | ExecutionException | TimeoutException e) { Slog.e(LOG_TAG, "Failed to grant defaults for user " + userId, e); } + } else if (RemoteRoleControllerService.DEBUG) { + Slog.i(LOG_TAG, "Already ran grants for package state " + packagesHash); } } + private String computeComponentStateHash(int userId) { + PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + pm.forEachPackage(FunctionalUtils.uncheckExceptions(pkg -> { + out.write(pkg.packageName.getBytes()); + out.write(BitUtils.toBytes(pkg.getLongVersionCode())); + out.write(pm.getApplicationEnabledState(pkg.packageName, userId)); + + ArraySet<String> enabledComponents = + pm.getEnabledComponents(pkg.packageName, userId); + int numComponents = CollectionUtils.size(enabledComponents); + for (int i = 0; i < numComponents; i++) { + out.write(enabledComponents.valueAt(i).getBytes()); + } + + ArraySet<String> disabledComponents = + pm.getDisabledComponents(pkg.packageName, userId); + numComponents = CollectionUtils.size(disabledComponents); + for (int i = 0; i < numComponents; i++) { + out.write(disabledComponents.valueAt(i).getBytes()); + } + for (Signature signature : pkg.mSigningDetails.signatures) { + out.write(signature.toByteArray()); + } + })); + + return PackageUtils.computeSha256Digest(out.toByteArray()); + } + @GuardedBy("mLock") @NonNull private RoleUserState getUserStateLocked(@UserIdInt int userId) { diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java index 9c43f4d02ad0..f218d3a5834b 100644 --- a/services/core/java/com/android/server/role/RoleUserState.java +++ b/services/core/java/com/android/server/role/RoleUserState.java @@ -31,6 +31,7 @@ import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BackgroundThread; +import com.android.internal.util.CollectionUtils; import com.android.internal.util.function.pooled.PooledLambda; import libcore.io.IoUtils; @@ -63,6 +64,7 @@ public class RoleUserState { private static final String TAG_HOLDER = "holder"; private static final String ATTRIBUTE_VERSION = "version"; private static final String ATTRIBUTE_NAME = "name"; + private static final String ATTRIBUTE_PACKAGES_HASH = "packagesHash"; @UserIdInt private final int mUserId; @@ -70,11 +72,15 @@ public class RoleUserState { @GuardedBy("RoleManagerService.mLock") private int mVersion; + @GuardedBy("RoleManagerService.mLock") + private String mLastGrantPackagesHash = null; + /** * Maps role names to its holders' package names. The values should never be null. */ @GuardedBy("RoleManagerService.mLock") - private ArrayMap<String, ArraySet<String>> mRoles; + @Nullable + private ArrayMap<String, ArraySet<String>> mRoles = null; @GuardedBy("RoleManagerService.mLock") private boolean mDestroyed; @@ -110,6 +116,23 @@ public class RoleUserState { } /** + * Get the hash representing the state of packages during the last time initial grants was run + */ + @GuardedBy("RoleManagerService.mLock") + public String getLastGrantPackagesHashLocked() { + return mLastGrantPackagesHash; + } + + /** + * Set the hash representing the state of packages during the last time initial grants was run + */ + @GuardedBy("RoleManagerService.mLock") + public void setLastGrantPackagesHashLocked(String lastGrantPackagesHash) { + mLastGrantPackagesHash = lastGrantPackagesHash; + writeAsyncLocked(); + } + + /** * Get whether the role is available. * * @param roleName the name of the role to get the holders for @@ -227,11 +250,11 @@ public class RoleUserState { * Schedule writing the state to file. */ @GuardedBy("RoleManagerService.mLock") - private void writeAsyncLocked() { + void writeAsyncLocked() { throwIfDestroyedLocked(); int version = mVersion; ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>(); - for (int i = 0, size = mRoles.size(); i < size; ++i) { + for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) { String roleName = mRoles.keyAt(i); ArraySet<String> roleHolders = mRoles.valueAt(i); roleHolders = new ArraySet<>(roleHolders); @@ -240,11 +263,12 @@ public class RoleUserState { mWriteHandler.removeCallbacksAndMessages(null); // TODO: Throttle writes. mWriteHandler.sendMessage(PooledLambda.obtainMessage( - RoleUserState::writeSync, this, version, roles)); + RoleUserState::writeSync, this, version, roles, mLastGrantPackagesHash)); } @WorkerThread - private void writeSync(int version, @NonNull ArrayMap<String, ArraySet<String>> roles) { + private void writeSync(int version, @NonNull ArrayMap<String, ArraySet<String>> roles, + String packagesHash) { AtomicFile atomicFile = new AtomicFile(getFile(mUserId), "roles-" + mUserId); FileOutputStream out = null; try { @@ -256,7 +280,7 @@ public class RoleUserState { "http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startDocument(null, true); - serializeRoles(serializer, version, roles); + serializeRoles(serializer, version, roles, packagesHash); serializer.endDocument(); atomicFile.finishWrite(out); @@ -272,9 +296,11 @@ public class RoleUserState { @WorkerThread private void serializeRoles(@NonNull XmlSerializer serializer, int version, - @NonNull ArrayMap<String, ArraySet<String>> roles) throws IOException { + @NonNull ArrayMap<String, ArraySet<String>> roles, String packagesHash) + throws IOException { serializer.startTag(null, TAG_ROLES); serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version)); + serializer.attribute(null, ATTRIBUTE_PACKAGES_HASH, packagesHash); for (int i = 0, size = roles.size(); i < size; ++i) { String roleName = roles.keyAt(i); ArraySet<String> roleHolders = roles.valueAt(i); @@ -341,6 +367,7 @@ public class RoleUserState { private void parseRolesLocked(@NonNull XmlPullParser parser) throws IOException, XmlPullParserException { mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION)); + mLastGrantPackagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH); mRoles = new ArrayMap<>(); int type; diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index aa11e1e3285c..f0ebb7512015 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -195,12 +195,11 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { "/system/bin/traced", // Perfetto. "/system/bin/traced_probes", // Perfetto. "webview_zygote", - // Temporarily excluded zygote to investigate its forking consequences in - // NativeProcessMemoryState. - // "zygote", - // "zygote64", + "zygote", + "zygote64", }; + private static final int CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES = 8; static final class CompanionHandler extends Handler { CompanionHandler(Looper looper) { @@ -1080,7 +1079,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { e.writeLong(processMemoryState.rssInBytes); e.writeLong(processMemoryState.cacheInBytes); e.writeLong(processMemoryState.swapInBytes); - e.writeLong(processMemoryState.rssHighWatermarkInBytes); + e.writeLong(0); // unused e.writeLong(processMemoryState.startTimeNanos); pulledData.add(e); } @@ -1089,6 +1088,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private void pullNativeProcessMemoryState( int tagId, long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) { + final List<String> processNames = Arrays.asList(MEMORY_INTERESTING_NATIVE_PROCESSES); int[] pids = getPidsForCommands(MEMORY_INTERESTING_NATIVE_PROCESSES); for (int i = 0; i < pids.length; i++) { int pid = pids[i]; @@ -1098,13 +1098,19 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } int uid = getUidForPid(pid); String processName = readCmdlineFromProcfs(pid); + // Sometimes we get here processName that is not included in the whitelist. It comes + // from forking the zygote for an app. We can ignore that sample because this process + // is collected by ProcessMemoryState. + if (!processNames.contains(processName)) { + continue; + } StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); e.writeInt(uid); e.writeString(processName); e.writeLong(memoryStat.pgfault); e.writeLong(memoryStat.pgmajfault); e.writeLong(memoryStat.rssInBytes); - e.writeLong(memoryStat.rssHighWatermarkInBytes); + e.writeLong(0); // unused e.writeLong(memoryStat.startTimeNanos); pulledData.add(e); } @@ -1654,6 +1660,11 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { return; } int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz(); + if (cpuFrequencies.length != CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES) { + Slog.w(TAG, "Expected " + CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES + + " frequencies, but got " + cpuFrequencies.length); + return; + } for (int i = 0; i < processCpuUsages.size(); i++) { KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = processCpuUsages.get(i); ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages = @@ -1667,23 +1678,18 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { continue; } - for (int k = 0; k < threadCpuUsage.usageTimesMillis.length; k++) { - // Do not report CPU usage at a frequency when it's zero - if (threadCpuUsage.usageTimesMillis[k] == 0) { - continue; - } - - StatsLogEventWrapper e = - new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(processCpuUsage.uid); - e.writeInt(processCpuUsage.processId); - e.writeInt(threadCpuUsage.threadId); - e.writeString(processCpuUsage.processName); - e.writeString(threadCpuUsage.threadName); + StatsLogEventWrapper e = + new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); + e.writeInt(processCpuUsage.uid); + e.writeInt(processCpuUsage.processId); + e.writeInt(threadCpuUsage.threadId); + e.writeString(processCpuUsage.processName); + e.writeString(threadCpuUsage.threadName); + for (int k = 0; k < CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES; k++) { e.writeInt(cpuFrequencies[k]); e.writeInt(threadCpuUsage.usageTimesMillis[k]); - pulledData.add(e); } + pulledData.add(e); } } } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 0d66a2c8b442..e645b84e83a0 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -24,7 +24,7 @@ import android.app.StatusBarManager; import android.content.ComponentName; import android.content.Context; import android.graphics.Rect; -import android.hardware.biometrics.IBiometricPromptReceiver; +import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -598,8 +598,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } @Override - public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type, - boolean requireConfirmation, int userId) { + public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver, + int type, boolean requireConfirmation, int userId) { enforceBiometricDialog(); if (mBar != null) { try { @@ -654,6 +654,17 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } @Override + public void showBiometricTryAgain() { + enforceBiometricDialog(); + if (mBar != null) { + try { + mBar.showBiometricTryAgain(); + } catch (RemoteException ex) { + } + } + } + + @Override public void disable(int what, IBinder token, String pkg) { disableForUser(what, token, pkg, mCurrentUserId); } 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/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index d5e59c8dfd6a..1163d3916cb1 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -626,7 +626,7 @@ public final class TvInputManagerService extends SystemService { updateServiceConnectionLocked(serviceState.component, userId); } - private void createSessionInternalLocked(ITvInputService service, IBinder sessionToken, + private boolean createSessionInternalLocked(ITvInputService service, IBinder sessionToken, int userId) { UserState userState = getOrCreateUserStateLocked(userId); SessionState sessionState = userState.sessionStateMap.get(sessionToken); @@ -638,6 +638,7 @@ public final class TvInputManagerService extends SystemService { // Set up a callback to send the session token. ITvInputSessionCallback callback = new SessionCallback(sessionState, channels); + boolean created = true; // Create a session. When failed, send a null token immediately. try { if (sessionState.isRecordingSession) { @@ -647,11 +648,12 @@ public final class TvInputManagerService extends SystemService { } } catch (RemoteException e) { Slog.e(TAG, "error in createSession", e); - removeSessionStateLocked(sessionToken, userId); sendSessionTokenToClientLocked(sessionState.client, sessionState.inputId, null, null, sessionState.seq); + created = false; } channels[1].dispose(); + return created; } private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId, @@ -1193,8 +1195,10 @@ public final class TvInputManagerService extends SystemService { serviceState.sessionTokens.add(sessionToken); if (serviceState.service != null) { - createSessionInternalLocked(serviceState.service, sessionToken, - resolvedUserId); + if (!createSessionInternalLocked(serviceState.service, sessionToken, + resolvedUserId)) { + removeSessionStateLocked(sessionToken, resolvedUserId); + } } else { updateServiceConnectionLocked(info.getComponent(), resolvedUserId); } @@ -2282,9 +2286,17 @@ public final class TvInputManagerService extends SystemService { } } + List<IBinder> tokensToBeRemoved = new ArrayList<>(); + // And create sessions, if any. for (IBinder sessionToken : serviceState.sessionTokens) { - createSessionInternalLocked(serviceState.service, sessionToken, mUserId); + if (!createSessionInternalLocked(serviceState.service, sessionToken, mUserId)) { + tokensToBeRemoved.add(sessionToken); + } + } + + for (IBinder sessionToken : tokensToBeRemoved) { + removeSessionStateLocked(sessionToken, mUserId); } for (TvInputState inputState : userState.inputMap.values()) { 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/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java index 84750b385d97..7bf2c9401294 100644 --- a/services/core/java/com/android/server/wm/ActivityDisplay.java +++ b/services/core/java/com/android/server/wm/ActivityDisplay.java @@ -1242,7 +1242,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { final ActivityRecord r = activities.get(activityNdx); if (r.isActivityTypeHome() - && ((userId == UserHandle.USER_ALL) || (r.userId == userId))) { + && ((userId == UserHandle.USER_ALL) || (r.mUserId == userId))) { return r; } } 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 416e133ea14a..12690a99062e 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 { @@ -251,7 +254,7 @@ class ActivityMetricsLogger { type = getTransitionType(info); processRecord = findProcessForActivity(launchedActivity); processName = launchedActivity.processName; - userId = launchedActivity.userId; + userId = launchedActivity.mUserId; launchedActivityShortComponentName = launchedActivity.shortComponentName; activityRecordIdHashCode = System.identityHashCode(launchedActivity); this.windowsFullyDrawnDelayMs = windowsFullyDrawnDelayMs; @@ -263,6 +266,7 @@ class ActivityMetricsLogger { mSupervisor = supervisor; mContext = context; mHandler = new H(looper); + mLaunchObserver = new LaunchObserverRegistryImpl(looper); } void logWindowState() { @@ -516,7 +520,7 @@ class ActivityMetricsLogger { if (info.launchedActivity != activityRecord) { return; } - final TaskRecord t = activityRecord.getTask(); + final TaskRecord t = activityRecord.getTaskRecord(); final SomeArgs args = SomeArgs.obtain(); args.arg1 = t; args.arg2 = activityRecord; @@ -889,7 +893,7 @@ class ActivityMetricsLogger { 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()); + r.mActivityComponent.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); @@ -1000,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); } /** @@ -1014,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); } /** @@ -1024,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); } /** @@ -1039,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); } /** @@ -1051,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 b4aec35d9d6e..25399ef165a5 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -244,7 +244,7 @@ final class ActivityRecord extends ConfigurationContainer { private static final String ATTR_COMPONENTSPECIFIED = "component_specified"; static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_"; - final ActivityTaskManagerService service; // owner + final ActivityTaskManagerService mAtmService; // owner final IApplicationToken.Stub appToken; // window manager token // TODO: Remove after unification AppWindowToken mAppWindowToken; @@ -255,9 +255,9 @@ final class ActivityRecord extends ConfigurationContainer { final int launchedFromPid; // always the pid who started the activity. final int launchedFromUid; // always the uid who started the activity. final String launchedFromPackage; // always the package who started the activity. - final int userId; // Which user is this running for? + final int mUserId; // Which user is this running for? final Intent intent; // the original intent that generated us - final ComponentName realActivity; // the intent component, or target of an alias. + final ComponentName mActivityComponent; // the intent component, or target of an alias. final String shortComponentName; // the short component name of the intent final String resolvedType; // as per original caller; final String packageName; // the package implementing intent's component @@ -407,14 +407,14 @@ final class ActivityRecord extends ConfigurationContainer { pw.print(" processName="); pw.println(processName); pw.print(prefix); pw.print("launchedFromUid="); pw.print(launchedFromUid); pw.print(" launchedFromPackage="); pw.print(launchedFromPackage); - pw.print(" userId="); pw.println(userId); + pw.print(" userId="); pw.println(mUserId); pw.print(prefix); pw.print("app="); pw.println(app); pw.print(prefix); pw.println(intent.toInsecureStringWithClip()); pw.print(prefix); pw.print("frontOfTask="); pw.print(frontOfTask); pw.print(" task="); pw.println(task); pw.print(prefix); pw.print("taskAffinity="); pw.println(taskAffinity); - pw.print(prefix); pw.print("realActivity="); - pw.println(realActivity.flattenToShortString()); + pw.print(prefix); pw.print("mActivityComponent="); + pw.println(mActivityComponent.flattenToShortString()); if (appInfo != null) { pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir); if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) { @@ -644,7 +644,7 @@ final class ActivityRecord extends ConfigurationContainer { "Reporting activity moved to display" + ", activityRecord=" + this + ", displayId=" + displayId + ", config=" + config); - service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, + mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, MoveToDisplayItem.obtain(displayId, config)); } catch (RemoteException e) { // If process died, whatever. @@ -662,7 +662,7 @@ final class ActivityRecord extends ConfigurationContainer { if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + this + ", config: " + config); - service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, + mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, ActivityConfigurationChangeItem.obtain(config)); } catch (RemoteException e) { // If process died, whatever. @@ -689,7 +689,7 @@ final class ActivityRecord extends ConfigurationContainer { private void scheduleMultiWindowModeChanged(Configuration overrideConfig) { try { - service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, + mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, MultiWindowModeChangeItem.obtain(mLastReportedMultiWindowMode, overrideConfig)); } catch (Exception e) { // If process died, I don't care. @@ -720,7 +720,7 @@ final class ActivityRecord extends ConfigurationContainer { private void schedulePictureInPictureModeChanged(Configuration overrideConfig) { try { - service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, + mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, PipModeChangeItem.obtain(mLastReportedPictureInPictureMode, overrideConfig)); } catch (Exception e) { @@ -741,10 +741,10 @@ final class ActivityRecord extends ConfigurationContainer { @Override protected ConfigurationContainer getParent() { - return getTask(); + return getTaskRecord(); } - TaskRecord getTask() { + TaskRecord getTaskRecord() { return task; } @@ -765,12 +765,12 @@ final class ActivityRecord extends ConfigurationContainer { * @param reparenting Whether we're in the middle of reparenting. */ void setTask(TaskRecord task, boolean reparenting) { - // Do nothing if the {@link TaskRecord} is the same as the current {@link getTask}. - if (task != null && task == getTask()) { + // Do nothing if the {@link TaskRecord} is the same as the current {@link getTaskRecord}. + if (task != null && task == getTaskRecord()) { return; } - final ActivityStack oldStack = getStack(); + final ActivityStack oldStack = getActivityStack(); final ActivityStack newStack = task != null ? task.getStack() : null; // Inform old stack (if present) of activity removal and new stack (if set) of activity @@ -819,7 +819,7 @@ final class ActivityRecord extends ConfigurationContainer { return null; } ActivityRecord r = token.weakActivity.get(); - if (r == null || r.getStack() == null) { + if (r == null || r.getActivityStack() == null) { return null; } return r; @@ -852,7 +852,7 @@ final class ActivityRecord extends ConfigurationContainer { } boolean isResolverActivity() { - return ResolverActivity.class.getName().equals(realActivity.getClassName()); + return ResolverActivity.class.getName().equals(mActivityComponent.getClassName()); } boolean isResolverOrChildActivity() { @@ -861,7 +861,7 @@ final class ActivityRecord extends ConfigurationContainer { } try { return ResolverActivity.class.isAssignableFrom( - Object.class.getClassLoader().loadClass(realActivity.getClassName())); + Object.class.getClassLoader().loadClass(mActivityComponent.getClassName())); } catch (ClassNotFoundException e) { return false; } @@ -873,14 +873,14 @@ final class ActivityRecord extends ConfigurationContainer { ActivityRecord _resultTo, String _resultWho, int _reqCode, boolean _componentSpecified, boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor, ActivityOptions options, ActivityRecord sourceRecord) { - service = _service; + mAtmService = _service; mRootActivityContainer = _service.mRootActivityContainer; appToken = new Token(this, _intent); info = aInfo; launchedFromPid = _launchedFromPid; launchedFromUid = _launchedFromUid; launchedFromPackage = _launchedFromPackage; - userId = UserHandle.getUserId(aInfo.applicationInfo.uid); + mUserId = UserHandle.getUserId(aInfo.applicationInfo.uid); intent = _intent; shortComponentName = _intent.getComponent().flattenToShortString(); resolvedType = _resolvedType; @@ -917,9 +917,9 @@ final class ActivityRecord extends ConfigurationContainer { || (aInfo.targetActivity.equals(_intent.getComponent().getClassName()) && (aInfo.launchMode == LAUNCH_MULTIPLE || aInfo.launchMode == LAUNCH_SINGLE_TOP))) { - realActivity = _intent.getComponent(); + mActivityComponent = _intent.getComponent(); } else { - realActivity = new ComponentName(aInfo.packageName, aInfo.targetActivity); + mActivityComponent = new ComponentName(aInfo.packageName, aInfo.targetActivity); } taskAffinity = aInfo.taskAffinity; stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0; @@ -958,7 +958,7 @@ final class ActivityRecord extends ConfigurationContainer { launchMode = aInfo.launchMode; Entry ent = AttributeCache.instance().get(packageName, - realTheme, com.android.internal.R.styleable.Window, userId); + realTheme, com.android.internal.R.styleable.Window, mUserId); if (ent != null) { fullscreen = !ActivityInfo.isTranslucentOrFloating(ent.array); @@ -1038,7 +1038,7 @@ final class ActivityRecord extends ConfigurationContainer { updateOverrideConfiguration(); // TODO: remove after unification - mAppWindowToken = service.mWindowManager.mRoot.getAppWindowToken(appToken.asBinder()); + mAppWindowToken = mAtmService.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); @@ -1048,7 +1048,7 @@ final class ActivityRecord extends ConfigurationContainer { throw new IllegalArgumentException("AppWindowContainerController: invalid " + " controller=" + taskController); } - mAppWindowToken = createAppWindow(service.mWindowManager, appToken, + mAppWindowToken = createAppWindow(mAtmService.mWindowManager, appToken, task.voiceSession != null, container.getDisplayContent(), ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L, fullscreen, @@ -1098,16 +1098,16 @@ final class ActivityRecord extends ConfigurationContainer { boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint, int configChanges, boolean launchTaskBehind, boolean alwaysFocusable) { - return new AppWindowToken(service, token, realActivity, voiceInteraction, dc, + return new AppWindowToken(service, token, mActivityComponent, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, this); } void removeWindowContainer() { - if (service.mWindowManager.mRoot == null) return; + if (mAtmService.mWindowManager.mRoot == null) return; - final DisplayContent dc = service.mWindowManager.mRoot.getDisplayContent( + final DisplayContent dc = mAtmService.mWindowManager.mRoot.getDisplayContent( getDisplayId()); if (dc == null) { Slog.w(TAG, "removeWindowContainer: Attempted to remove token: " @@ -1193,7 +1193,7 @@ final class ActivityRecord extends ConfigurationContainer { */ private boolean canLaunchAssistActivity(String packageName) { final ComponentName assistComponent = - service.mActiveVoiceInteractionServiceComponent; + mAtmService.mActiveVoiceInteractionServiceComponent; if (assistComponent != null) { return assistComponent.getPackageName().equals(packageName); } @@ -1213,8 +1213,8 @@ final class ActivityRecord extends ConfigurationContainer { // We only allow home activities to be resizeable if they explicitly requested it. info.resizeMode = RESIZE_MODE_UNRESIZEABLE; } - } else if (realActivity.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME) - || service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) { + } else if (mActivityComponent.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME) + || mAtmService.getRecentTasks().isRecentsComponent(mActivityComponent, appInfo.uid)) { activityType = ACTIVITY_TYPE_RECENTS; } else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_ASSISTANT && canLaunchAssistActivity(launchedFromPackage)) { @@ -1232,16 +1232,16 @@ final class ActivityRecord extends ConfigurationContainer { /** * @return Stack value from current task, null if there is no task. */ - <T extends ActivityStack> T getStack() { + <T extends ActivityStack> T getActivityStack() { return task != null ? (T) task.getStack() : null; } int getStackId() { - return getStack() != null ? getStack().mStackId : INVALID_STACK_ID; + return getActivityStack() != null ? getActivityStack().mStackId : INVALID_STACK_ID; } ActivityDisplay getDisplay() { - final ActivityStack stack = getStack(); + final ActivityStack stack = getActivityStack(); return stack != null ? stack.getDisplay() : null; } @@ -1272,7 +1272,7 @@ final class ActivityRecord extends ConfigurationContainer { } boolean isInStackLocked() { - final ActivityStack stack = getStack(); + final ActivityStack stack = getActivityStack(); return stack != null && stack.isInStackLocked(this) != null; } @@ -1302,7 +1302,7 @@ final class ActivityRecord extends ConfigurationContainer { * @return whether this activity supports PiP multi-window and can be put in the pinned stack. */ boolean supportsPictureInPicture() { - return service.mSupportsPictureInPicture && isActivityTypeStandardOrUndefined() + return mAtmService.mSupportsPictureInPicture && isActivityTypeStandardOrUndefined() && info.supportsPictureInPicture(); } @@ -1315,7 +1315,7 @@ final class ActivityRecord extends ConfigurationContainer { // An activity can not be docked even if it is considered resizeable because it only // supports picture-in-picture mode but has a non-resizeable resizeMode return super.supportsSplitScreenWindowingMode() - && service.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow(); + && mAtmService.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow(); } /** @@ -1323,16 +1323,16 @@ final class ActivityRecord extends ConfigurationContainer { * stack. */ boolean supportsFreeform() { - return service.mSupportsFreeformWindowManagement && supportsResizeableMultiWindow(); + return mAtmService.mSupportsFreeformWindowManagement && supportsResizeableMultiWindow(); } /** * @return whether this activity supports non-PiP multi-window. */ private boolean supportsResizeableMultiWindow() { - return service.mSupportsMultiWindow && !isActivityTypeHome() + return mAtmService.mSupportsMultiWindow && !isActivityTypeHome() && (ActivityInfo.isResizeableMode(info.resizeMode) - || service.mForceResizableActivities); + || mAtmService.mForceResizableActivities); } /** @@ -1343,7 +1343,7 @@ final class ActivityRecord extends ConfigurationContainer { * secondary screen. */ boolean canBeLaunchedOnDisplay(int displayId) { - return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId, launchedFromPid, + return mAtmService.mStackSupervisor.canPlaceEntityOnDisplay(displayId, launchedFromPid, launchedFromUid, info); } @@ -1364,13 +1364,13 @@ final class ActivityRecord extends ConfigurationContainer { } // Check to see if we are in VR mode, and disallow PiP if so - if (service.shouldDisableNonVrUiLocked()) { + if (mAtmService.shouldDisableNonVrUiLocked()) { return false; } - boolean isKeyguardLocked = service.isKeyguardLocked(); + boolean isKeyguardLocked = mAtmService.isKeyguardLocked(); boolean isCurrentAppLocked = - service.getLockTaskModeState() != LOCK_TASK_MODE_NONE; + mAtmService.getLockTaskModeState() != LOCK_TASK_MODE_NONE; final ActivityDisplay display = getDisplay(); boolean hasPinnedStack = display != null && display.hasPinnedStack(); // Don't return early if !isNotLocked, since we want to throw an exception if the activity @@ -1411,7 +1411,7 @@ final class ActivityRecord extends ConfigurationContainer { * @return Whether AppOps allows this package to enter picture-in-picture. */ private boolean checkEnterPictureInPictureAppOpsState() { - return service.getAppOpsService().checkOperation( + return mAtmService.getAppOpsService().checkOperation( OP_PICTURE_IN_PICTURE, appInfo.uid, packageName) == MODE_ALLOWED; } @@ -1428,8 +1428,8 @@ final class ActivityRecord extends ConfigurationContainer { return false; } - final TaskRecord task = getTask(); - final ActivityStack stack = getStack(); + final TaskRecord task = getTaskRecord(); + final ActivityStack stack = getActivityStack(); if (stack == null) { Slog.w(TAG, "moveActivityStackToFront: invalid task or stack: activity=" + this + " task=" + task); @@ -1451,7 +1451,7 @@ final class ActivityRecord extends ConfigurationContainer { // Report top activity change to tracking services and WM if (mRootActivityContainer.getTopResumedActivity() == this) { // TODO(b/111361570): Support multiple focused apps in WM - service.setResumedActivityUncheckLocked(this, reason); + mAtmService.setResumedActivityUncheckLocked(this, reason); } return true; } @@ -1461,7 +1461,7 @@ final class ActivityRecord extends ConfigurationContainer { * {@link LayoutParams#FLAG_DISMISS_KEYGUARD} set */ boolean hasDismissKeyguardWindows() { - return service.mWindowManager.containsDismissKeyguardWindow(appToken); + return mAtmService.mWindowManager.containsDismissKeyguardWindow(appToken); } void makeFinishingLocked() { @@ -1473,14 +1473,14 @@ final class ActivityRecord extends ConfigurationContainer { clearOptionsLocked(); } - if (service != null) { - service.getTaskChangeNotificationController().notifyTaskStackChanged(); + if (mAtmService != null) { + mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged(); } } UriPermissionOwner getUriPermissionsLocked() { if (uriPermissions == null) { - uriPermissions = new UriPermissionOwner(service.mUgmInternal, this); + uriPermissions = new UriPermissionOwner(mAtmService.mUgmInternal, this); } return uriPermissions; } @@ -1522,8 +1522,8 @@ final class ActivityRecord extends ConfigurationContainer { } final boolean isSleeping() { - final ActivityStack stack = getStack(); - return stack != null ? stack.shouldSleepActivities() : service.isSleepingLocked(); + final ActivityStack stack = getActivityStack(); + return stack != null ? stack.shouldSleepActivities() : mAtmService.isSleepingLocked(); } /** @@ -1532,8 +1532,8 @@ final class ActivityRecord extends ConfigurationContainer { */ final void deliverNewIntentLocked(int callingUid, Intent intent, String referrer) { // The activity now gets access to the data associated with this Intent. - service.mUgmInternal.grantUriPermissionFromIntent(callingUid, packageName, - intent, getUriPermissionsLocked(), userId); + mAtmService.mUgmInternal.grantUriPermissionFromIntent(callingUid, packageName, + intent, getUriPermissionsLocked(), mUserId); final ReferrerIntent rintent = new ReferrerIntent(intent, referrer); boolean unsent = true; final boolean isTopActivityWhileSleeping = isTopRunningActivity() && isSleeping(); @@ -1547,7 +1547,7 @@ final class ActivityRecord extends ConfigurationContainer { try { ArrayList<ReferrerIntent> ar = new ArrayList<>(1); ar.add(rintent); - service.getLifecycleManager().scheduleTransaction( + mAtmService.getLifecycleManager().scheduleTransaction( app.getThread(), appToken, NewIntentItem.obtain(ar, mState == PAUSED)); unsent = false; } catch (RemoteException e) { @@ -1781,7 +1781,7 @@ final class ActivityRecord extends ConfigurationContainer { mState = state; - final TaskRecord parent = getTask(); + final TaskRecord parent = getTaskRecord(); if (parent != null) { parent.onActivityStateChanged(this, state, reason); @@ -1879,7 +1879,7 @@ final class ActivityRecord extends ConfigurationContainer { // If this activity is paused, tell it to now show its window. if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making visible and scheduling visibility: " + this); - final ActivityStack stack = getStack(); + final ActivityStack stack = getActivityStack(); try { if (stack.mTranslucentActivityWaiting != null) { updateOptionsLocked(returningOptions); @@ -1907,13 +1907,13 @@ final class ActivityRecord extends ConfigurationContainer { void makeClientVisible() { mClientVisibilityDeferred = false; try { - service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, + mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, WindowVisibilityItem.obtain(true /* showWindow */)); if (shouldPauseWhenBecomingVisible()) { // An activity must be in the {@link PAUSING} state for the system to validate // the move to {@link PAUSED}. setState(PAUSING, "makeVisibleIfNeeded"); - service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, + mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, PauseActivityItem.obtain(finishing, false /* userLeaving */, configChangeFlags, false /* dontReport */)); } @@ -1931,7 +1931,7 @@ final class ActivityRecord extends ConfigurationContainer { // paused state. We also avoid doing this for the activity the stack supervisor // considers the resumed activity, as normal means will bring the activity from STOPPED // to RESUMED. Adding PAUSING in this scenario will lead to double lifecycles. - if (!isState(STOPPED, STOPPING) || getStack().mTranslucentActivityWaiting != null + if (!isState(STOPPED, STOPPING) || getActivityStack().mTranslucentActivityWaiting != null || isResumedActivityOnDisplay()) { return false; } @@ -1994,8 +1994,8 @@ final class ActivityRecord extends ConfigurationContainer { if (isActivityTypeHome()) { WindowProcessController app = task.mActivities.get(0).app; - if (hasProcess() && app != service.mHomeProcess) { - service.mHomeProcess = app; + if (hasProcess() && app != mAtmService.mHomeProcess) { + mAtmService.mHomeProcess = app; } } @@ -2010,7 +2010,7 @@ final class ActivityRecord extends ConfigurationContainer { mStackSupervisor.reportResumedActivityLocked(this); resumeKeyDispatchingLocked(); - final ActivityStack stack = getStack(); + final ActivityStack stack = getActivityStack(); mStackSupervisor.mNoAnimActivities.clear(); // Mark the point when the activity is resuming @@ -2036,7 +2036,7 @@ final class ActivityRecord extends ConfigurationContainer { final void activityStoppedLocked(Bundle newIcicle, PersistableBundle newPersistentState, CharSequence description) { - final ActivityStack stack = getStack(); + final ActivityStack stack = getActivityStack(); if (mState != STOPPING) { Slog.i(TAG, "Activity reported stop, but no longer stopping: " + this); stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this); @@ -2044,7 +2044,7 @@ final class ActivityRecord extends ConfigurationContainer { } if (newPersistentState != null) { persistentState = newPersistentState; - service.notifyTaskPersisterLocked(task, false); + mAtmService.notifyTaskPersisterLocked(task, false); } if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, "Saving icicle of " + this + ": " + icicle); @@ -2094,7 +2094,7 @@ final class ActivityRecord extends ConfigurationContainer { return false; } - final ActivityStack stack = getStack(); + final ActivityStack stack = getActivityStack(); if (stack == null) { return false; } @@ -2107,7 +2107,7 @@ final class ActivityRecord extends ConfigurationContainer { void finishLaunchTickingLocked() { launchTickTime = 0; - final ActivityStack stack = getStack(); + final ActivityStack stack = getActivityStack(); if (stack != null) { stack.mHandler.removeMessages(LAUNCH_TICK_MSG); } @@ -2168,7 +2168,7 @@ final class ActivityRecord extends ConfigurationContainer { * Called when the starting window for this container is drawn. */ public void onStartingWindowDrawn(long timestamp) { - synchronized (service.mGlobalLock) { + synchronized (mAtmService.mGlobalLock) { mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn( getWindowingMode(), timestamp); } @@ -2176,7 +2176,7 @@ final class ActivityRecord extends ConfigurationContainer { /** Called when the windows associated app window container are drawn. */ public void onWindowsDrawn(boolean drawn, long timestamp) { - synchronized (service.mGlobalLock) { + synchronized (mAtmService.mGlobalLock) { mDrawn = drawn; if (!drawn) { return; @@ -2196,7 +2196,7 @@ final class ActivityRecord extends ConfigurationContainer { /** Called when the windows associated app window container are visible. */ public void onWindowsVisible() { - synchronized (service.mGlobalLock) { + synchronized (mAtmService.mGlobalLock) { mStackSupervisor.reportActivityVisibleLocked(this); if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsVisibleLocked(): " + this); if (!nowVisible) { @@ -2223,14 +2223,14 @@ final class ActivityRecord extends ConfigurationContainer { mStackSupervisor.processStoppingActivitiesLocked(null /* idleActivity */, false /* remove */, true /* processPausingActivities */); } - service.scheduleAppGcsLocked(); + mAtmService.scheduleAppGcsLocked(); } } } /** Called when the windows associated app window container are no longer visible. */ public void onWindowsGone() { - synchronized (service.mGlobalLock) { + synchronized (mAtmService.mGlobalLock) { if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + this); nowVisible = false; } @@ -2248,7 +2248,7 @@ final class ActivityRecord extends ConfigurationContainer { ActivityRecord anrActivity; WindowProcessController anrApp; boolean windowFromSameProcessAsActivity; - synchronized (service.mGlobalLock) { + synchronized (mAtmService.mGlobalLock) { anrActivity = getWaitingHistoryRecordLocked(); anrApp = app; windowFromSameProcessAsActivity = @@ -2256,13 +2256,13 @@ final class ActivityRecord extends ConfigurationContainer { } if (windowFromSameProcessAsActivity) { - return service.mAmInternal.inputDispatchingTimedOut(anrApp.mOwner, + return mAtmService.mAmInternal.inputDispatchingTimedOut(anrApp.mOwner, anrActivity.shortComponentName, anrActivity.appInfo, shortComponentName, app, false, reason); } else { // In this case another process added windows using this activity token. So, we call the // generic service input dispatch timed out method so that the right process is blamed. - return service.mAmInternal.inputDispatchingTimedOut( + return mAtmService.mAmInternal.inputDispatchingTimedOut( windowPid, false /* aboveSystem */, reason) < 0; } } @@ -2289,14 +2289,14 @@ final class ActivityRecord extends ConfigurationContainer { public boolean okToShowLocked() { // We cannot show activities when the device is locked and the application is not // encryption aware. - if (!StorageManager.isUserKeyUnlocked(userId) + if (!StorageManager.isUserKeyUnlocked(mUserId) && !info.applicationInfo.isEncryptionAware()) { return false; } return (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0 - || (mStackSupervisor.isCurrentProfileLocked(userId) - && service.mAmInternal.isUserRunning(userId, 0 /* flags */)); + || (mStackSupervisor.isCurrentProfileLocked(mUserId) + && mAtmService.mAmInternal.isUserRunning(mUserId, 0 /* flags */)); } /** @@ -2343,13 +2343,13 @@ final class ActivityRecord extends ConfigurationContainer { static ActivityRecord isInStackLocked(IBinder token) { final ActivityRecord r = ActivityRecord.forTokenLocked(token); - return (r != null) ? r.getStack().isInStackLocked(r) : null; + return (r != null) ? r.getActivityStack().isInStackLocked(r) : null; } static ActivityStack getStackLocked(IBinder token) { final ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r != null) { - return r.getStack(); + return r.getActivityStack(); } return null; } @@ -2359,7 +2359,7 @@ final class ActivityRecord extends ConfigurationContainer { * {@link android.view.Display#INVALID_DISPLAY} if not attached. */ int getDisplayId() { - final ActivityStack stack = getStack(); + final ActivityStack stack = getActivityStack(); if (stack == null) { return INVALID_DISPLAY; } @@ -2371,7 +2371,7 @@ final class ActivityRecord extends ConfigurationContainer { // This would be redundant. return false; } - final ActivityStack stack = getStack(); + final ActivityStack stack = getActivityStack(); if (stack == null || this == stack.getResumedActivity() || this == stack.mPausingActivity || !haveState || !stopped) { // We're not ready for this kind of thing. @@ -2397,7 +2397,7 @@ final class ActivityRecord extends ConfigurationContainer { final File iconFile = new File(TaskPersister.getUserImagesDir(task.userId), iconFilename); final String iconFilePath = iconFile.getAbsolutePath(); - service.getRecentTasks().saveImage(icon, iconFilePath); + mAtmService.getRecentTasks().saveImage(icon, iconFilePath); _taskDescription.setIconFilename(iconFilePath); } taskDescription = _taskDescription; @@ -2433,7 +2433,7 @@ final class ActivityRecord extends ConfigurationContainer { } final CompatibilityInfo compatInfo = - service.compatibilityInfoForPackageLocked(info.applicationInfo); + mAtmService.compatibilityInfoForPackageLocked(info.applicationInfo); final boolean shown = addStartingWindow(packageName, theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(), @@ -2466,12 +2466,12 @@ final class ActivityRecord extends ConfigurationContainer { displayId, displayConfig, mayFreezeScreenLocked(app)); if (config != null) { frozenBeforeDestroy = true; - if (!service.updateDisplayOverrideConfigurationLocked(config, this, + if (!mAtmService.updateDisplayOverrideConfigurationLocked(config, this, false /* deferResume */, displayId)) { mRootActivityContainer.resumeFocusedStacksTopActivities(); } } - service.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged( + mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged( task.taskId, requestedOrientation); } @@ -2486,7 +2486,7 @@ final class ActivityRecord extends ConfigurationContainer { mAppWindowToken.setOrientation(requestedOrientation); final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null; - return service.mWindowManager.updateOrientationFromAppTokens(displayConfig, binder, + return mAtmService.mWindowManager.updateOrientationFromAppTokens(displayConfig, binder, displayId); } @@ -2572,7 +2572,7 @@ final class ActivityRecord extends ConfigurationContainer { private void computeBounds(Rect outBounds) { outBounds.setEmpty(); final float maxAspectRatio = info.maxAspectRatio; - final ActivityStack stack = getStack(); + final ActivityStack stack = getActivityStack(); if (task == null || stack == null || task.inMultiWindowMode() || maxAspectRatio == 0 || isInVrUiMode(getConfiguration())) { // We don't set override configuration if that activity task isn't fullscreen. I.e. the @@ -2618,7 +2618,7 @@ final class ActivityRecord extends ConfigurationContainer { // bounds would end up too small. outBounds.set(0, 0, maxActivityWidth + appBounds.left, maxActivityHeight + appBounds.top); - if (service.mWindowManager.getNavBarPosition(getDisplayId()) == NAV_BAR_LEFT) { + if (mAtmService.mWindowManager.getNavBarPosition(getDisplayId()) == NAV_BAR_LEFT) { // Position the activity frame on the opposite side of the nav bar. outBounds.left = appBounds.right - maxActivityWidth; outBounds.right = appBounds.right; @@ -2654,7 +2654,7 @@ final class ActivityRecord extends ConfigurationContainer { */ boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow, boolean ignoreStopState) { - final ActivityStack stack = getStack(); + final ActivityStack stack = getActivityStack(); if (stack.mConfigWillChange) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Skipping config check (will change): " + this); @@ -2717,7 +2717,7 @@ final class ActivityRecord extends ConfigurationContainer { // Update last reported values. final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration(); - setLastReportedConfiguration(service.getGlobalConfiguration(), newMergedOverrideConfig); + setLastReportedConfiguration(mAtmService.getGlobalConfiguration(), newMergedOverrideConfig); if (mState == INITIALIZING) { // No need to relaunch or schedule new config for activity that hasn't been launched @@ -2771,7 +2771,7 @@ final class ActivityRecord extends ConfigurationContainer { final boolean hasResizeChange = hasResizeChange(changes & ~info.getRealConfigChanged()); if (hasResizeChange) { final boolean isDragResizing = - getTask().getWindowContainerController().isDragResizing(); + getTaskRecord().getWindowContainerController().isDragResizing(); mRelaunchReason = isDragResizing ? RELAUNCH_REASON_FREE_RESIZE : RELAUNCH_REASON_WINDOWING_MODE_RESIZE; } else { @@ -2904,7 +2904,7 @@ final class ActivityRecord extends ConfigurationContainer { } void relaunchActivityLocked(boolean andResume, boolean preserveWindow) { - if (service.mSuppressResizeConfigChanges && preserveWindow) { + if (mAtmService.mSuppressResizeConfigChanges && preserveWindow) { configChangeFlags = 0; return; } @@ -2920,7 +2920,7 @@ final class ActivityRecord extends ConfigurationContainer { + " newIntents=" + pendingNewIntents + " andResume=" + andResume + " preserveWindow=" + preserveWindow); EventLog.writeEvent(andResume ? AM_RELAUNCH_RESUME_ACTIVITY - : AM_RELAUNCH_ACTIVITY, userId, System.identityHashCode(this), + : AM_RELAUNCH_ACTIVITY, mUserId, System.identityHashCode(this), task.taskId, shortComponentName); startFreezingScreenLocked(app, 0); @@ -2933,7 +2933,7 @@ final class ActivityRecord extends ConfigurationContainer { mStackSupervisor.activityRelaunchingLocked(this); final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults, pendingNewIntents, configChangeFlags, - new MergedConfiguration(service.getGlobalConfiguration(), + new MergedConfiguration(mAtmService.getGlobalConfiguration(), getMergedOverrideConfiguration()), preserveWindow); final ActivityLifecycleItem lifecycleItem; @@ -2946,7 +2946,7 @@ final class ActivityRecord extends ConfigurationContainer { final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), appToken); transaction.addCallback(callbackItem); transaction.setLifecycleStateRequest(lifecycleItem); - service.getLifecycleManager().scheduleTransaction(transaction); + mAtmService.getLifecycleManager().scheduleTransaction(transaction); // Note: don't need to call pauseIfSleepingLocked() here, because the caller will only // request resume if this activity is currently resumed, which implies we aren't // sleeping. @@ -2960,9 +2960,9 @@ final class ActivityRecord extends ConfigurationContainer { } results = null; newIntents = null; - service.getAppWarningsLocked().onResumeActivity(this); + mAtmService.getAppWarningsLocked().onResumeActivity(this); } else { - final ActivityStack stack = getStack(); + final ActivityStack stack = getActivityStack(); if (stack != null) { stack.mHandler.removeMessages(PAUSE_TIMEOUT_MSG, this); } @@ -2977,7 +2977,7 @@ final class ActivityRecord extends ConfigurationContainer { private boolean isProcessRunning() { WindowProcessController proc = app; if (proc == null) { - proc = service.mProcessNames.get(processName, info.applicationInfo.uid); + proc = mAtmService.mProcessNames.get(processName, info.applicationInfo.uid); } return proc != null && proc.hasThread(); } @@ -3021,7 +3021,7 @@ final class ActivityRecord extends ConfigurationContainer { out.attribute(null, ATTR_RESOLVEDTYPE, resolvedType); } out.attribute(null, ATTR_COMPONENTSPECIFIED, String.valueOf(componentSpecified)); - out.attribute(null, ATTR_USERID, String.valueOf(userId)); + out.attribute(null, ATTR_USERID, String.valueOf(mUserId)); if (taskDescription != null) { taskDescription.saveToXml(out); @@ -3144,7 +3144,7 @@ final class ActivityRecord extends ConfigurationContainer { */ boolean canShowWhenLocked() { return !inPinnedWindowingMode() && (mShowWhenLocked - || service.mWindowManager.containsShowWhenLockedWindow(appToken)); + || mAtmService.mWindowManager.containsShowWhenLockedWindow(appToken)); } void setTurnScreenOn(boolean turnScreenOn) { @@ -3159,7 +3159,7 @@ final class ActivityRecord extends ConfigurationContainer { * @return true if the screen can be turned on, false otherwise. */ boolean canTurnScreenOn() { - final ActivityStack stack = getStack(); + final ActivityStack stack = getActivityStack(); return mTurnScreenOn && stack != null && stack.checkKeyguardVisibility(this, true /* shouldBeVisible */, true /* isTop */); } @@ -3200,7 +3200,7 @@ final class ActivityRecord extends ConfigurationContainer { sb.append("ActivityRecord{"); sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(" u"); - sb.append(userId); + sb.append(mUserId); sb.append(' '); sb.append(intent.getComponent().flattenToShortString()); stringName = sb.toString(); @@ -3210,13 +3210,16 @@ final class ActivityRecord extends ConfigurationContainer { void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(HASH_CODE, System.identityHashCode(this)); - proto.write(USER_ID, userId); + proto.write(USER_ID, mUserId); proto.write(TITLE, intent.getComponent().flattenToShortString()); 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()); @@ -3226,6 +3229,11 @@ final class ActivityRecord extends ConfigurationContainer { 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 9fbeaf8cd7bf..d40a4faf9d21 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -538,7 +538,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // TODO(b/111361570): Support multiple focused apps in WM mService.setResumedActivityUncheckLocked(record, reason); } - mStackSupervisor.mRecentTasks.add(record.getTask()); + mStackSupervisor.mRecentTasks.add(record.getTaskRecord()); } } @@ -1106,8 +1106,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (r == null) { return null; } - final TaskRecord task = r.getTask(); - final ActivityStack stack = r.getStack(); + final TaskRecord task = r.getTaskRecord(); + final ActivityStack stack = r.getActivityStack(); if (stack != null && task.mActivities.contains(r) && mTaskHistory.contains(task)) { if (stack != this) Slog.w(TAG, "Illegal state! task does not point to stack it is in."); @@ -1282,7 +1282,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Overlays should not be considered as the task's logical top activity. final ActivityRecord r = task.getTopActivity(false /* includeOverlays */); - if (r == null || r.finishing || r.userId != userId || + if (r == null || r.finishing || r.mUserId != userId || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch root " + r); continue; @@ -1309,7 +1309,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Comparing existing cls=" + taskIntent.getComponent().flattenToShortString() - + "/aff=" + r.getTask().rootAffinity + " to new cls=" + + "/aff=" + r.getTaskRecord().rootAffinity + " to new cls=" + intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity); // TODO Refactor to remove duplications. Check if logic can be simplified. if (taskIntent != null && taskIntent.getComponent() != null && @@ -1368,7 +1368,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (!r.okToShowLocked()) { continue; } - if (!r.finishing && r.userId == userId) { + if (!r.finishing && r.mUserId == userId) { if (compareIntentFilters) { if (r.intent.filterEquals(intent)) { return r; @@ -1452,7 +1452,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { final ActivityRecord ar = activities.get(activityNdx); - if ((userId == ar.userId) && packageName.equals(ar.packageName)) { + if ((userId == ar.mUserId) && packageName.equals(ar.packageName)) { ar.updateApplicationInfo(aInfo); } } @@ -1538,7 +1538,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai private boolean containsActivityFromStack(List<ActivityRecord> rs) { for (ActivityRecord r : rs) { - if (r.getStack() == this) { + if (r.getActivityStack() == this) { return true; } } @@ -1606,7 +1606,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0 || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null; prev.setState(PAUSING, "startPausingLocked"); - prev.getTask().touchActiveTime(); + prev.getTaskRecord().touchActiveTime(); clearLaunchTime(prev); mService.updateCpuStats(); @@ -1614,7 +1614,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (prev.attachedToProcess()) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev); try { - EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev), + EventLogTags.writeAmPauseActivity(prev.mUserId, System.identityHashCode(prev), prev.shortComponentName, "userLeaving=" + userLeaving); mService.updateUsageStats(prev, false); @@ -1692,7 +1692,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return; } else { EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE, - r.userId, System.identityHashCode(r), r.shortComponentName, + r.mUserId, System.identityHashCode(r), r.shortComponentName, mPausingActivity != null ? mPausingActivity.shortComponentName : "(none)"); if (r.isState(PAUSING)) { @@ -2573,9 +2573,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Make sure that the user who owns this activity is started. If not, // we will just leave it as is because someone should be bringing // another user's activities to the top of the stack. - if (!mService.mAmInternal.hasStartedUserState(next.userId)) { + if (!mService.mAmInternal.hasStartedUserState(next.mUserId)) { Slog.w(TAG, "Skipping resume of top activity " + next - + ": user " + next.userId + " is stopped"); + + ": user " + next.mUserId + " is stopped"); return false; } @@ -2702,7 +2702,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // considered stopped. try { AppGlobals.getPackageManager().setPackageStoppedState( - next.packageName, false, next.userId); /* TODO: Verify if correct userid */ + next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */ } catch (RemoteException e1) { } catch (IllegalArgumentException e) { Slog.w(TAG, "Failed trying to unstop package " @@ -2723,7 +2723,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai dwc.prepareAppTransition(TRANSIT_NONE, false); } else { dwc.prepareAppTransition( - prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_CLOSE + prev.getTaskRecord() == next.getTaskRecord() ? TRANSIT_ACTIVITY_CLOSE : TRANSIT_TASK_CLOSE, false); } prev.setVisibility(false); @@ -2735,7 +2735,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai dwc.prepareAppTransition(TRANSIT_NONE, false); } else { dwc.prepareAppTransition( - prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_OPEN + prev.getTaskRecord() == next.getTaskRecord() ? TRANSIT_ACTIVITY_OPEN : next.mLaunchTaskBehind ? TRANSIT_TASK_OPEN_BEHIND : TRANSIT_TASK_OPEN, false); } @@ -2858,8 +2858,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Clear app token stopped state in window manager if needed. next.notifyAppResumed(next.stopped); - EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId, - System.identityHashCode(next), next.getTask().taskId, + EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.mUserId, + System.identityHashCode(next), next.getTaskRecord().taskId, next.shortComponentName); next.sleeping = false; @@ -3008,7 +3008,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity, boolean newTask, boolean keepCurTransition, ActivityOptions options) { - TaskRecord rTask = r.getTask(); + TaskRecord rTask = r.getTaskRecord(); final int taskId = rTask.taskId; // mLaunchTaskBehind tasks get placed at the back of the task stack. if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) { @@ -3049,7 +3049,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // If we are not placing the new activity frontmost, we do not want to deliver the // onUserLeaving callback to the actual frontmost activity - final TaskRecord activityTask = r.getTask(); + final TaskRecord activityTask = r.getTaskRecord(); if (task == activityTask && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) { mStackSupervisor.mUserLeaving = false; if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING, @@ -3122,12 +3122,12 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // "has the same starting icon" as the next one. This allows the // window manager to keep the previous window it had previously // created, if it still had one. - TaskRecord prevTask = r.getTask(); + TaskRecord prevTask = r.getTaskRecord(); ActivityRecord prev = prevTask.topRunningActivityWithStartingWindowLocked(); if (prev != null) { // We don't want to reuse the previous starting preview if: // (1) The current activity is in a different task. - if (prev.getTask() != prevTask) { + if (prev.getTaskRecord() != prevTask) { prev = null; } // (2) The current activity is already displayed. @@ -3160,7 +3160,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return false; } final ActivityStack targetStack = toFrontTask != null - ? toFrontTask.getStack() : toFrontActivity.getStack(); + ? toFrontTask.getStack() : toFrontActivity.getActivityStack(); if (targetStack != null && targetStack.isActivityTypeAssistant()) { // Ensure the task/activity being brought forward is not the assistant return false; @@ -3170,7 +3170,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai private boolean isTaskSwitch(ActivityRecord r, ActivityRecord topFocusedActivity) { - return topFocusedActivity != null && r.getTask() != topFocusedActivity.getTask(); + return topFocusedActivity != null && r.getTaskRecord() != topFocusedActivity.getTaskRecord(); } /** @@ -3237,16 +3237,16 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai !mTaskHistory.isEmpty() && !mTaskHistory.get(0).mActivities.isEmpty() ? mTaskHistory.get(0).mActivities.get(0) : null; if (bottom != null && target.taskAffinity != null - && target.taskAffinity.equals(bottom.getTask().affinity)) { + && target.taskAffinity.equals(bottom.getTaskRecord().affinity)) { // If the activity currently at the bottom has the // same task affinity as the one we are moving, // then merge it into the same task. - targetTask = bottom.getTask(); + targetTask = bottom.getTaskRecord(); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target + " out to bottom task " + targetTask); } else { targetTask = createTaskRecord( - mStackSupervisor.getNextTaskIdForUserLocked(target.userId), + mStackSupervisor.getNextTaskIdForUserLocked(target.mUserId), target.info, null, null, null, false); targetTask.affinityIntent = target.intent; if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target @@ -3440,7 +3440,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai ActivityRecord newActivity) { final boolean forceReset = (newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0; - final TaskRecord task = taskTop.getTask(); + final TaskRecord task = taskTop.getTaskRecord(); /** False until we evaluate the TaskRecord associated with taskTop. Switches to true * for remaining tasks. Used for later tasks to reparent to task. */ @@ -3489,7 +3489,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (callingUid > 0) { mService.mUgmInternal.grantUriPermissionFromIntent(callingUid, r.packageName, - data, r.getUriPermissionsLocked(), r.userId); + data, r.getUriPermissionsLocked(), r.mUserId); } if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r @@ -3551,7 +3551,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Task is not guaranteed to be non-null. For example, destroying the // {@link ActivityRecord} will disassociate the task from the activity. - final TaskRecord task = r.getTask(); + final TaskRecord task = r.getTaskRecord(); if (task == null) { throw new IllegalStateException("activity no longer associated with task:" + r); @@ -3643,7 +3643,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai r.setVisible(false); } EventLogTags.writeAmStopActivity( - r.userId, System.identityHashCode(r), r.shortComponentName); + r.mUserId, System.identityHashCode(r), r.shortComponentName); mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken, StopActivityItem.obtain(r.visible, r.configChangeFlags)); if (shouldSleepOrShutDownActivities()) { @@ -3719,7 +3719,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } Slog.w(TAG, " Force finishing activity " + r.intent.getComponent().flattenToShortString()); - finishedTask = r.getTask(); + finishedTask = r.getTaskRecord(); int taskNdx = mTaskHistory.indexOf(finishedTask); final TaskRecord task = finishedTask; int activityNdx = task.mActivities.indexOf(r); @@ -3793,7 +3793,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } final boolean finishActivityAffinityLocked(ActivityRecord r) { - ArrayList<ActivityRecord> activities = r.getTask().mActivities; + ArrayList<ActivityRecord> activities = r.getTaskRecord().mActivities; for (int index = activities.indexOf(r); index >= 0; --index) { ActivityRecord cur = activities.get(index); if (!Objects.equals(cur.taskAffinity, r.taskAffinity)) { @@ -3811,15 +3811,15 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "Adding result to " + resultTo + " who=" + r.resultWho + " req=" + r.requestCode + " res=" + resultCode + " data=" + resultData); - if (resultTo.userId != r.userId) { + if (resultTo.mUserId != r.mUserId) { if (resultData != null) { - resultData.prepareToLeaveUser(r.userId); + resultData.prepareToLeaveUser(r.mUserId); } } if (r.info.applicationInfo.uid > 0) { mService.mUgmInternal.grantUriPermissionFromIntent(r.info.applicationInfo.uid, resultTo.packageName, resultData, - resultTo.getUriPermissionsLocked(), resultTo.userId); + resultTo.getUriPermissionsLocked(), resultTo.mUserId); } resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode, resultData); r.resultTo = null; @@ -3857,9 +3857,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mWindowManager.deferSurfaceLayout(); try { r.makeFinishingLocked(); - final TaskRecord task = r.getTask(); + final TaskRecord task = r.getTaskRecord(); EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, - r.userId, System.identityHashCode(r), + r.mUserId, System.identityHashCode(r), task.taskId, r.shortComponentName, reason); final ArrayList<ActivityRecord> activities = task.mActivities; final int index = activities.indexOf(r); @@ -3992,7 +3992,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai r.setState(FINISHING, "finishCurrentActivityLocked"); final boolean finishingInNonFocusedStackOrNoRunning = mode == FINISH_AFTER_VISIBLE - && prevState == PAUSED && (r.getStack() != display.getFocusedStack() + && prevState == PAUSED && (r.getActivityStack() != display.getFocusedStack() || (next == null && display.topRunningActivity() == null)); if (mode == FINISH_IMMEDIATELY @@ -4068,15 +4068,15 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai boolean shouldUpRecreateTaskLocked(ActivityRecord srec, String destAffinity) { // Basic case: for simple app-centric recents, we need to recreate // the task if the affinity has changed. - if (srec == null || srec.getTask().affinity == null || - !srec.getTask().affinity.equals(destAffinity)) { + if (srec == null || srec.getTaskRecord().affinity == null || + !srec.getTaskRecord().affinity.equals(destAffinity)) { return true; } // Document-centric case: an app may be split in to multiple documents; // they need to re-create their task if this current activity is the root // of a document, unless simply finishing it will return them to the the // correct app behind. - final TaskRecord task = srec.getTask(); + final TaskRecord task = srec.getTaskRecord(); if (srec.frontOfTask && task.getBaseIntent() != null && task.getBaseIntent().isDocument()) { // Okay, this activity is at the root of its task. What to do, what to do... if (!inFrontOfStandardStack()) { @@ -4100,7 +4100,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final boolean navigateUpToLocked(ActivityRecord srec, Intent destIntent, int resultCode, Intent resultData) { - final TaskRecord task = srec.getTask(); + final TaskRecord task = srec.getTaskRecord(); final ArrayList<ActivityRecord> activities = task.mActivities; final int start = activities.indexOf(srec); if (!mTaskHistory.contains(task) || (start < 0)) { @@ -4165,7 +4165,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai try { ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo( destIntent.getComponent(), ActivityManagerService.STOCK_PM_FLAGS, - srec.userId); + srec.mUserId); // TODO(b/64750076): Check if calling pid should really be -1. final int res = mService.getActivityStartController() .obtainStarter(destIntent, "navigateUpTo") @@ -4282,7 +4282,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + r); r.app = null; r.removeWindowContainer(); - final TaskRecord task = r.getTask(); + final TaskRecord task = r.getTaskRecord(); final boolean lastActivity = task != null ? task.removeActivity(r) : false; // If we are removing the last activity in the task, not including task overlay activities, // then fall through into the block below to remove the entire task itself @@ -4446,8 +4446,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY, - r.userId, System.identityHashCode(r), - r.getTask().taskId, r.shortComponentName, reason); + r.mUserId, System.identityHashCode(r), + r.getTaskRecord().taskId, r.shortComponentName, reason); boolean removedFromHistory = false; @@ -4646,8 +4646,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (!r.finishing) { Slog.w(TAG, "Force removing " + r + ": app died, no saved state"); EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, - r.userId, System.identityHashCode(r), - r.getTask().taskId, r.shortComponentName, + r.mUserId, System.identityHashCode(r), + r.getTaskRecord().taskId, r.shortComponentName, "proc died without state saved"); if (r.getState() == RESUMED) { mService.updateUsageStats(r, false); @@ -4746,7 +4746,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final ActivityRecord top = tr.getTopActivity(); if (top == null || !top.okToShowLocked()) { if (top != null) { - mStackSupervisor.mRecentTasks.add(top.getTask()); + mStackSupervisor.mRecentTasks.add(top.getTaskRecord()); } ActivityOptions.abort(options); return; @@ -4861,7 +4861,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final String strData = data != null ? data.toSafeString() : null; EventLog.writeEvent(tag, - r.userId, System.identityHashCode(r), task.taskId, + r.mUserId, System.identityHashCode(r), task.taskId, r.shortComponentName, r.intent.getAction(), r.intent.getType(), strData, r.intent.getFlags()); } @@ -4874,15 +4874,15 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return; } - final TaskRecord startTask = start.getTask(); + final TaskRecord startTask = start.getTaskRecord(); boolean behindFullscreen = false; boolean updatedConfig = false; for (int taskIndex = mTaskHistory.indexOf(startTask); taskIndex >= 0; --taskIndex) { final TaskRecord task = mTaskHistory.get(taskIndex); final ArrayList<ActivityRecord> activities = task.mActivities; - int activityIndex = - (start.getTask() == task) ? activities.indexOf(start) : activities.size() - 1; + int activityIndex = (start.getTaskRecord() == task) + ? activities.indexOf(start) : activities.size() - 1; for (; activityIndex >= 0; --activityIndex) { final ActivityRecord r = activities.get(activityIndex); updatedConfig |= r.ensureActivityConfiguration(0 /* globalChanges */, @@ -5046,10 +5046,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai ActivityRecord r = mTmpActivities.remove(0); final boolean sameComponent = (r.packageName.equals(packageName) && (filterByClasses == null - || filterByClasses.contains(r.realActivity.getClassName()))) - || (packageName == null && r.userId == userId); - if ((userId == UserHandle.USER_ALL || r.userId == userId) - && (sameComponent || r.getTask() == lastTask) + || filterByClasses.contains(r.mActivityComponent.getClassName()))) + || (packageName == null && r.mUserId == userId); + if ((userId == UserHandle.USER_ALL || r.mUserId == userId) + && (sameComponent || r.getTaskRecord() == lastTask) && (r.app == null || evenPersistent || !r.app.isPersistent())) { if (!doit) { if (r.finishing) { @@ -5060,11 +5060,11 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return true; } if (r.isActivityTypeHome()) { - if (homeActivity != null && homeActivity.equals(r.realActivity)) { + if (homeActivity != null && homeActivity.equals(r.mActivityComponent)) { Slog.i(TAG, "Skip force-stop again " + r); continue; } else { - homeActivity = r.realActivity; + homeActivity = r.mActivityComponent; } } didSomething = true; @@ -5075,7 +5075,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } r.app = null; } - lastTask = r.getTask(); + lastTask = r.getTaskRecord(); finishActivityLocked(r, Activity.RESULT_CANCELED, null, "force-stop", true); } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 3162ee37276e..f3c5630b53f0 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -91,9 +91,6 @@ import android.app.AppOpsManager; import android.app.ProfilerInfo; import android.app.ResultInfo; import android.app.WaitResult; -import android.app.WindowConfiguration; -import android.app.WindowConfiguration.ActivityType; -import android.app.WindowConfiguration.WindowingMode; import android.app.servertransaction.ActivityLifecycleItem; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.LaunchActivityItem; @@ -111,7 +108,6 @@ import android.content.pm.UserInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; -import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.Handler; @@ -434,7 +430,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { 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(); @@ -542,7 +540,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { mActivitiesWaitingForVisibleActivity.remove(r); for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) { - if (mWaitingForActivityVisible.get(i).matches(r.realActivity)) { + if (mWaitingForActivityVisible.get(i).matches(r.mActivityComponent)) { mWaitingForActivityVisible.remove(i); } } @@ -556,7 +554,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { boolean changed = false; for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) { final WaitInfo w = mWaitingForActivityVisible.get(i); - if (w.matches(r.realActivity)) { + if (w.matches(r.mActivityComponent)) { final WaitResult result = w.getResult(); changed = true; result.timeout = false; @@ -590,7 +588,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // Unlike START_TASK_TO_FRONT, When an intent is delivered to top, there // will be no followup launch signals. Assign the result and launched component. if (result == START_DELIVERED_TO_TOP) { - w.who = r.realActivity; + w.who = r.mActivityComponent; } } } @@ -664,31 +662,29 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags, int filterCallingUid) { - synchronized (mService.mGlobalLock) { - try { - Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent"); - int modifiedFlags = flags - | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS; - if (intent.isWebIntent() - || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) { - modifiedFlags |= PackageManager.MATCH_INSTANT; - } + try { + Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent"); + int modifiedFlags = flags + | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS; + if (intent.isWebIntent() + || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) { + modifiedFlags |= PackageManager.MATCH_INSTANT; + } - // In order to allow cross-profile lookup, we clear the calling identity here. - // Note the binder identity won't affect the result, but filterCallingUid will. + // In order to allow cross-profile lookup, we clear the calling identity here. + // Note the binder identity won't affect the result, but filterCallingUid will. - // Cross-user/profile call check are done at the entry points - // (e.g. AMS.startActivityAsUser). - final long token = Binder.clearCallingIdentity(); - try { - return mService.getPackageManagerInternalLocked().resolveIntent( - intent, resolvedType, modifiedFlags, userId, true, filterCallingUid); - } finally { - Binder.restoreCallingIdentity(token); - } + // Cross-user/profile call check are done at the entry points + // (e.g. AMS.startActivityAsUser). + final long token = Binder.clearCallingIdentity(); + try { + return mService.getPackageManagerInternalLocked().resolveIntent( + intent, resolvedType, modifiedFlags, userId, true, filterCallingUid); } finally { - Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + Binder.restoreCallingIdentity(token); } + } finally { + Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } } @@ -711,7 +707,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { return false; } - final TaskRecord task = r.getTask(); + final TaskRecord task = r.getTaskRecord(); final ActivityStack stack = task.getStack(); beginDeferResume(); @@ -740,7 +736,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { false /* markFrozenIfConfigChanged */, true /* deferResume */); } - if (r.getStack().checkKeyguardVisibility(r, true /* shouldBeVisible */, + if (r.getActivityStack().checkKeyguardVisibility(r, true /* shouldBeVisible */, true /* isTop */)) { // We only set the visibility to true if the activity is allowed to be visible // based on @@ -752,7 +748,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { final int applicationInfoUid = (r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1; - if ((r.userId != proc.mUserId) || (r.appInfo.uid != applicationInfoUid)) { + if ((r.mUserId != proc.mUserId) || (r.appInfo.uid != applicationInfoUid)) { Slog.wtf(TAG, "User ID for activity changing for " + r + " appInfo.uid=" + r.appInfo.uid @@ -793,7 +789,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Launching: " + r + " icicle=" + r.icicle + " with results=" + results + " newIntents=" + newIntents + " andResume=" + andResume); - EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, r.userId, + EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, r.mUserId, System.identityHashCode(r), task.taskId, r.shortComponentName); if (r.isActivityTypeHome()) { // Home process is the root process of the task. @@ -1310,7 +1306,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // waiting for the next one to start. for (int i = 0; i < NS; i++) { r = stops.get(i); - final ActivityStack stack = r.getStack(); + final ActivityStack stack = r.getActivityStack(); if (stack != null) { if (r.finishing) { stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false, @@ -1325,7 +1321,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // waiting for the next one to start. for (int i = 0; i < NF; i++) { r = finishes.get(i); - final ActivityStack stack = r.getStack(); + final ActivityStack stack = r.getActivityStack(); if (stack != null) { activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle"); } @@ -2007,7 +2003,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { void activitySleptLocked(ActivityRecord r) { mGoingToSleepActivities.remove(r); - final ActivityStack s = r.getStack(); + final ActivityStack s = r.getActivityStack(); if (s != null) { s.checkReadyForSleep(); } else { @@ -2043,7 +2039,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // A resumed activity cannot be stopping. remove from list mStoppingActivities.remove(r); - final ActivityStack stack = r.getStack(); + final ActivityStack stack = r.getActivityStack(); if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) { mService.updateUsageStats(r, true); } @@ -2059,7 +2055,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // Called when WindowManager has finished animating the launchingBehind activity to the back. private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) { - final TaskRecord task = r.getTask(); + final TaskRecord task = r.getTaskRecord(); final ActivityStack stack = task.getStack(); r.mLaunchTaskBehind = false; @@ -2071,7 +2067,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // task has been shown briefly final ActivityRecord top = stack.getTopActivity(); if (top != null) { - top.getTask().touchActiveTime(); + top.getTaskRecord().touchActiveTime(); } } @@ -2126,7 +2122,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } } if (remove) { - final ActivityStack stack = s.getStack(); + final ActivityStack stack = s.getActivityStack(); final boolean shouldSleepOrShutDown = stack != null ? stack.shouldSleepOrShutDownActivities() : mService.isSleepingOrShuttingDownLocked(); @@ -2216,8 +2212,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { pw.println(header); header = null; } - if (lastTask != r.getTask()) { - lastTask = r.getTask(); + if (lastTask != r.getTaskRecord()) { + lastTask = r.getTaskRecord(); pw.print(prefix); pw.print(full ? "* " : " "); pw.println(lastTask); @@ -2374,7 +2370,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { mWindowManager.notifyAppRelaunchingFinished(token); final ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r != null) { - if (r.getStack().shouldSleepOrShutDownActivities()) { + if (r.getActivityStack().shouldSleepOrShutDownActivities()) { r.setSleeping(true, true); } } diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java index 54a63a168588..b8442a887dac 100644 --- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java @@ -282,10 +282,10 @@ class ActivityStartInterceptor { } 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. - mActivityOptions.setLaunchTaskId(homeActivityRecord.getTask().taskId); + if (homeActivityRecord != null && homeActivityRecord.getTaskRecord() != null) { + // Showing credential confirmation activity in home task to avoid stopping + // multi-windowed mode after showing the full-screen credential confirmation activity. + mActivityOptions.setLaunchTaskId(homeActivityRecord.getTaskRecord().taskId); } final UserInfo parent = mUserManager.getProfileParent(mUserId); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index cba104401b49..bc2136ebcf15 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -678,7 +678,7 @@ class ActivityStarter { } if (err == ActivityManager.START_SUCCESS && sourceRecord != null - && sourceRecord.getTask().voiceSession != null) { + && sourceRecord.getTaskRecord().voiceSession != null) { // If this activity is being launched as part of a voice session, we need // to ensure that it is safe to do so. If the upcoming activity will also // be part of the voice session, we can only launch it if it has explicitly @@ -718,7 +718,8 @@ class ActivityStarter { } } - final ActivityStack resultStack = resultRecord == null ? null : resultRecord.getStack(); + final ActivityStack resultStack = resultRecord == null + ? null : resultRecord.getActivityStack(); if (err != START_SUCCESS) { if (resultRecord != null) { @@ -738,7 +739,8 @@ class ActivityStarter { // not sure if we need to create START_ABORTED_BACKGROUND so for now piggybacking // on START_ABORTED if (!abort) { - abort |= shouldAbortBackgroundActivityStart(callingUid, callingPackage, callerApp); + abort |= shouldAbortBackgroundActivityStart(callingUid, callingPackage, realCallingUid, + callerApp); } // Merge the two options bundles, while realCallerOptions takes precedence. @@ -886,7 +888,7 @@ class ActivityStarter { } private boolean shouldAbortBackgroundActivityStart(int callingUid, final String callingPackage, - WindowProcessController callerApp) { + int realCallingUid, WindowProcessController callerApp) { if (mService.isBackgroundActivityStartsEnabled()) { return false; } @@ -898,12 +900,12 @@ class ActivityStarter { if (callerApp != null && callerApp.hasForegroundActivities()) { return false; } - // don't abort if the callingUid's process is important enough - if (mService.getUidStateLocked(callingUid) <= ActivityManager.PROCESS_STATE_TOP) { + // don't abort if the callingUid is in the foreground + if (isUidForeground(callingUid)) { return false; } - // don't abort if the callingUid has any visible window - if (mService.mWindowManager.isAnyWindowVisibleForUid(callingUid)) { + // 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 @@ -920,6 +922,12 @@ class ActivityStarter { 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, boolean abortedStart) { @@ -1227,7 +1235,7 @@ class ActivityStarter { } case START_DELIVERED_TO_TOP: { outResult.timeout = false; - outResult.who = r.realActivity; + outResult.who = r.mActivityComponent; outResult.totalTime = 0; break; } @@ -1236,11 +1244,12 @@ class ActivityStarter { // in the resumed state. if (r.nowVisible && r.isState(RESUMED)) { outResult.timeout = false; - outResult.who = r.realActivity; + outResult.who = r.mActivityComponent; outResult.totalTime = 0; } else { final long startTimeMs = SystemClock.uptimeMillis(); - mSupervisor.waitActivityVisible(r.realActivity, outResult, startTimeMs); + mSupervisor.waitActivityVisible( + r.mActivityComponent, outResult, startTimeMs); // Note: the timeout variable is not currently not ever set. do { try { @@ -1286,7 +1295,7 @@ class ActivityStarter { result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, startFlags, doResume, options, inTask, outActivity); } finally { - final ActivityStack currentStack = r.getStack(); + final ActivityStack currentStack = r.getActivityStack(); startedActivityStack = currentStack != null ? currentStack : mTargetStack; if (ActivityManager.isStartResultSuccessful(result)) { @@ -1306,7 +1315,7 @@ class ActivityStarter { // If we are not able to proceed, disassociate the activity from the task. // Leaving an activity in an incomplete state can lead to issues, such as // performing operations without a window container. - final ActivityStack stack = mStartActivity.getStack(); + final ActivityStack stack = mStartActivity.getActivityStack(); if (stack != null) { stack.finishActivityLocked(mStartActivity, RESULT_CANCELED, null /* intentResultData */, "startActivity", true /* oomAdj */); @@ -1351,7 +1360,8 @@ class ActivityStarter { // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but // still needs to be a lock task mode violation since the task gets cleared out and // the device would otherwise leave the locked task. - if (mService.getLockTaskController().isLockTaskModeViolation(reusedActivity.getTask(), + if (mService.getLockTaskController().isLockTaskModeViolation( + reusedActivity.getTaskRecord(), (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) { Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode"); @@ -1368,14 +1378,14 @@ class ActivityStarter { // If mStartActivity does not have a task associated with it, associate it with the // reused activity's task. Do not do so if we're clearing top and resetting for a // standard launchMode activity. - if (mStartActivity.getTask() == null && !clearTopAndResetStandardLaunchMode) { - mStartActivity.setTask(reusedActivity.getTask()); + if (mStartActivity.getTaskRecord() == null && !clearTopAndResetStandardLaunchMode) { + mStartActivity.setTask(reusedActivity.getTaskRecord()); } - if (reusedActivity.getTask().intent == null) { + if (reusedActivity.getTaskRecord().intent == null) { // This task was started because of movement of the activity based on affinity... // Now that we are actually launching it, we can assign the base intent. - reusedActivity.getTask().setIntent(mStartActivity); + reusedActivity.getTaskRecord().setIntent(mStartActivity); } // This code path leads to delivering a new intent, we want to make sure we schedule it @@ -1384,7 +1394,7 @@ class ActivityStarter { if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0 || isDocumentLaunchesIntoExisting(mLaunchFlags) || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) { - final TaskRecord task = reusedActivity.getTask(); + final TaskRecord task = reusedActivity.getTaskRecord(); // In this situation we want to remove all activities from the task up to the one // being started. In most cases this means we are resetting the task to its initial @@ -1396,7 +1406,7 @@ class ActivityStarter { // the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The // task reference is needed in the call below to // {@link setTargetStackAndMoveToFrontIfNeeded}. - if (reusedActivity.getTask() == null) { + if (reusedActivity.getTaskRecord() == null) { reusedActivity.setTask(task); } @@ -1404,7 +1414,7 @@ class ActivityStarter { if (top.frontOfTask) { // Activity aliases may mean we use different intents for the top activity, // so make sure the task now has the identity of the new intent. - top.getTask().setIntent(mStartActivity); + top.getTaskRecord().setIntent(mStartActivity); } deliverNewIntent(top); } @@ -1451,7 +1461,7 @@ class ActivityStarter { if (mStartActivity.packageName == null) { final ActivityStack sourceStack = mStartActivity.resultTo != null - ? mStartActivity.resultTo.getStack() : null; + ? mStartActivity.resultTo.getActivityStack() : null; if (sourceStack != null) { sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo, mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED, @@ -1467,8 +1477,8 @@ class ActivityStarter { final ActivityRecord topFocused = topStack.getTopActivity(); final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop); final boolean dontStart = top != null && mStartActivity.resultTo == null - && top.realActivity.equals(mStartActivity.realActivity) - && top.userId == mStartActivity.userId + && top.mActivityComponent.equals(mStartActivity.mActivityComponent) + && top.mUserId == mStartActivity.mUserId && top.attachedToProcess() && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK)) @@ -1493,7 +1503,7 @@ class ActivityStarter { // Don't use mStartActivity.task to show the toast. We're not starting a new activity // but reusing 'top'. Fields in mStartActivity may not be fully initialized. - mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredWindowingMode, + mSupervisor.handleNonResizableTaskIfNeeded(top.getTaskRecord(), preferredWindowingMode, mPreferredDisplayId, topStack); return START_DELIVERED_TO_TOP; @@ -1501,7 +1511,7 @@ class ActivityStarter { boolean newTask = false; final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null) - ? mSourceRecord.getTask() : null; + ? mSourceRecord.getTaskRecord() : null; // Should this be considered a new task? int result = START_SUCCESS; @@ -1523,16 +1533,16 @@ class ActivityStarter { } mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName, - mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId); + mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.mUserId); mService.getPackageManagerInternalLocked().grantEphemeralAccess( - mStartActivity.userId, mIntent, UserHandle.getAppId(mStartActivity.appInfo.uid), + mStartActivity.mUserId, mIntent, UserHandle.getAppId(mStartActivity.appInfo.uid), UserHandle.getAppId(mCallingUid)); if (newTask) { - EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.userId, - mStartActivity.getTask().taskId); + EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.mUserId, + mStartActivity.getTaskRecord().taskId); } ActivityStack.logStartActivity( - EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTask()); + EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTaskRecord()); mTargetStack.mLastPausedActivity = null; mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded( @@ -1542,7 +1552,7 @@ class ActivityStarter { mOptions); if (mDoResume) { final ActivityRecord topTaskActivity = - mStartActivity.getTask().topRunningActivityLocked(); + mStartActivity.getTaskRecord().topRunningActivityLocked(); if (!mTargetStack.isFocusable() || (topTaskActivity != null && topTaskActivity.mTaskOverlay && mStartActivity != topTaskActivity)) { @@ -1570,12 +1580,12 @@ class ActivityStarter { mTargetStack, mStartActivity, mOptions); } } else if (mStartActivity != null) { - mSupervisor.mRecentTasks.add(mStartActivity.getTask()); + mSupervisor.mRecentTasks.add(mStartActivity.getTaskRecord()); } - mRootActivityContainer.updateUserStack(mStartActivity.userId, mTargetStack); + mRootActivityContainer.updateUserStack(mStartActivity.mUserId, mTargetStack); - mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode, - mPreferredDisplayId, mTargetStack); + mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTaskRecord(), + preferredWindowingMode, mPreferredDisplayId, mTargetStack); return START_SUCCESS; } @@ -1732,7 +1742,7 @@ class ActivityStarter { checkedCaller = mRootActivityContainer.getTopDisplayFocusedStack() .topRunningNonDelayedActivityLocked(mNotTop); } - if (!checkedCaller.realActivity.equals(r.realActivity)) { + if (!checkedCaller.mActivityComponent.equals(r.mActivityComponent)) { // Caller is not the same as launcher, so always needed. mStartFlags &= ~START_FLAG_ONLY_IF_NEEDED; } @@ -1743,7 +1753,7 @@ class ActivityStarter { private void sendNewTaskResultRequestIfNeeded() { final ActivityStack sourceStack = mStartActivity.resultTo != null - ? mStartActivity.resultTo.getStack() : null; + ? mStartActivity.resultTo.getActivityStack() : null; if (sourceStack != null && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) { // For whatever reason this activity is being launched into a new task... // yet the caller has requested a result back. Well, that is pretty messed up, @@ -1847,7 +1857,7 @@ class ActivityStarter { return; } if (!mSourceRecord.finishing) { - mSourceStack = mSourceRecord.getStack(); + mSourceStack = mSourceRecord.getActivityStack(); return; } @@ -1865,7 +1875,7 @@ class ActivityStarter { // example, if this method is being called for processing a pending activity launch, it // is possible that the activity has been removed from the task after the launch was // enqueued. - final TaskRecord sourceTask = mSourceRecord.getTask(); + final TaskRecord sourceTask = mSourceRecord.getTaskRecord(); mNewTaskIntent = sourceTask != null ? sourceTask.intent : null; } mSourceRecord = null; @@ -1928,7 +1938,7 @@ class ActivityStarter { * @return {@link ActivityRecord} brought to front. */ private ActivityRecord setTargetStackAndMoveToFrontIfNeeded(ActivityRecord intentActivity) { - mTargetStack = intentActivity.getStack(); + mTargetStack = intentActivity.getActivityStack(); mTargetStack.mLastPausedActivity = null; // If the target task is not in the front, then we need to bring it to the front... // except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have @@ -1939,9 +1949,9 @@ class ActivityStarter { final ActivityStack focusStack = mTargetStack.getDisplay().getFocusedStack(); final ActivityRecord curTop = (focusStack == null) ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop); - final TaskRecord topTask = curTop != null ? curTop.getTask() : null; + final TaskRecord topTask = curTop != null ? curTop.getTaskRecord() : null; differentTopTask = topTask != null - && (topTask != intentActivity.getTask() || topTask != focusStack.topTask()); + && (topTask != intentActivity.getTaskRecord() || topTask != focusStack.topTask()); } else { // The existing task should always be different from those in other displays. differentTopTask = true; @@ -1950,10 +1960,11 @@ class ActivityStarter { if (differentTopTask && !mAvoidMoveToFront) { mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); if (mSourceRecord == null || (mSourceStack.getTopActivity() != null && - mSourceStack.getTopActivity().getTask() == mSourceRecord.getTask())) { + mSourceStack.getTopActivity().getTaskRecord() + == mSourceRecord.getTaskRecord())) { // We really do want to push this one into the user's face, right now. if (mLaunchTaskBehind && mSourceRecord != null) { - intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask()); + intentActivity.setTaskToAffiliateWith(mSourceRecord.getTaskRecord()); } // If the launch flags carry both NEW_TASK and CLEAR_TASK, the task's activities @@ -1967,8 +1978,8 @@ class ActivityStarter { == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); if (!willClearTask) { final ActivityStack launchStack = getLaunchStack( - mStartActivity, mLaunchFlags, mStartActivity.getTask(), mOptions); - final TaskRecord intentTask = intentActivity.getTask(); + mStartActivity, mLaunchFlags, mStartActivity.getTaskRecord(), mOptions); + final TaskRecord intentTask = intentActivity.getTaskRecord(); if (launchStack == null || launchStack == mTargetStack) { // We only want to move to the front, if we aren't going to launch on a // different stack. If we launch on a different stack, we will put the @@ -1998,7 +2009,7 @@ class ActivityStarter { // Target and computed stacks are on different displays and we've // found a matching task - move the existing instance to that display and // move it to front. - intentActivity.getTask().reparent(launchStack, ON_TOP, + intentActivity.getTaskRecord().reparent(launchStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME, "reparentToDisplay"); mMovedToFront = true; @@ -2008,7 +2019,7 @@ class ActivityStarter { // For example, the activity may have been initially started with an intent // which placed it in the fullscreen stack. To ensure the proper handling of // the activity based on home stack assumptions, we must move it over. - intentActivity.getTask().reparent(launchStack, ON_TOP, + intentActivity.getTaskRecord().reparent(launchStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME, "reparentingHome"); mMovedToFront = true; @@ -2024,14 +2035,14 @@ class ActivityStarter { } // Need to update mTargetStack because if task was moved out of it, the original stack may // be destroyed. - mTargetStack = intentActivity.getStack(); + mTargetStack = intentActivity.getActivityStack(); if (!mMovedToFront && mDoResume) { if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + mTargetStack + " from " + intentActivity); mTargetStack.moveToFront("intentActivityFound"); } - mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(), + mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTaskRecord(), WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, mTargetStack); // If the caller has requested that the target task be reset, then do so. @@ -2053,14 +2064,14 @@ class ActivityStarter { // launching another activity. // TODO(b/36119896): We shouldn't trigger activity launches in this path since we are // already launching one. - final TaskRecord task = intentActivity.getTask(); + final TaskRecord task = intentActivity.getTaskRecord(); task.performClearTaskLocked(); mReuseTask = task; mReuseTask.setIntent(mStartActivity); } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0 || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) { - ActivityRecord top = intentActivity.getTask().performClearTaskLocked(mStartActivity, - mLaunchFlags); + final ActivityRecord top = intentActivity.getTaskRecord().performClearTaskLocked( + mStartActivity, mLaunchFlags); if (top == null) { // A special case: we need to start the activity because it is not currently // running, and the caller has asked to clear the current task to have this @@ -2072,7 +2083,7 @@ class ActivityStarter { // Now pretend like this activity is being started by the top of its task, so it // is put in the right place. mSourceRecord = intentActivity; - final TaskRecord task = mSourceRecord.getTask(); + final TaskRecord task = mSourceRecord.getTaskRecord(); if (task != null && task.getStack() == null) { // Target stack got cleared when we all activities were removed above. // Go ahead and reset it. @@ -2082,19 +2093,21 @@ class ActivityStarter { !mLaunchTaskBehind /* toTop */, "startActivityUnchecked"); } } - } else if (mStartActivity.realActivity.equals(intentActivity.getTask().realActivity)) { + } else if (mStartActivity.mActivityComponent.equals( + intentActivity.getTaskRecord().realActivity)) { // In this case the top activity on the task is the same as the one being launched, // so we take that as a request to bring the task to the foreground. If the top // activity in the task is the root activity, deliver this new intent to it if it // desires. if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || LAUNCH_SINGLE_TOP == mLaunchMode) - && intentActivity.realActivity.equals(mStartActivity.realActivity)) { + && intentActivity.mActivityComponent.equals( + mStartActivity.mActivityComponent)) { if (intentActivity.frontOfTask) { - intentActivity.getTask().setIntent(mStartActivity); + intentActivity.getTaskRecord().setIntent(mStartActivity); } deliverNewIntent(intentActivity); - } else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) { + } else if (!intentActivity.getTaskRecord().isSameIntentFilter(mStartActivity)) { // In this case we are launching the root activity of the task, but with a // different intent. We should start a new instance on top. mAddingToTask = true; @@ -2107,13 +2120,13 @@ class ActivityStarter { // current task. mAddingToTask = true; mSourceRecord = intentActivity; - } else if (!intentActivity.getTask().rootWasReset) { + } else if (!intentActivity.getTaskRecord().rootWasReset) { // In this case we are launching into an existing task that has not yet been started // from its front door. The current task has been brought to the front. Ideally, // we'd probably like to place this new task at the bottom of its stack, but that's // a little hard to do with the current organization of the code so for now we'll // just drop it. - intentActivity.getTask().setIntent(mStartActivity); + intentActivity.getTaskRecord().setIntent(mStartActivity); } } @@ -2123,7 +2136,7 @@ class ActivityStarter { } else { ActivityOptions.abort(mOptions); } - mRootActivityContainer.updateUserStack(mStartActivity.userId, mTargetStack); + mRootActivityContainer.updateUserStack(mStartActivity.mUserId, mTargetStack); } private int setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate) { @@ -2134,16 +2147,16 @@ class ActivityStarter { if (mReuseTask == null) { final TaskRecord task = mTargetStack.createTaskRecord( - mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), + mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId), mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info, mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession, mVoiceInteractor, !mLaunchTaskBehind /* toTop */, mStartActivity, mSourceRecord, mOptions); addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask"); - updateBounds(mStartActivity.getTask(), mLaunchParams.mBounds); + updateBounds(mStartActivity.getTaskRecord(), mLaunchParams.mBounds); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity - + " in new task " + mStartActivity.getTask()); + + " in new task " + mStartActivity.getTaskRecord()); } else { addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask"); } @@ -2152,7 +2165,8 @@ class ActivityStarter { mStartActivity.setTaskToAffiliateWith(taskToAffiliate); } - if (mService.getLockTaskController().isLockTaskModeViolation(mStartActivity.getTask())) { + if (mService.getLockTaskController().isLockTaskModeViolation( + mStartActivity.getTaskRecord())) { Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity); return START_RETURN_LOCK_TASK_MODE_VIOLATION; } @@ -2168,20 +2182,21 @@ class ActivityStarter { return; } - ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTask()); + ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTaskRecord()); activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); mIntentDelivered = true; } private int setTaskFromSourceRecord() { - if (mService.getLockTaskController().isLockTaskModeViolation(mSourceRecord.getTask())) { + if (mService.getLockTaskController().isLockTaskModeViolation( + mSourceRecord.getTaskRecord())) { Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity); return START_RETURN_LOCK_TASK_MODE_VIOLATION; } - final TaskRecord sourceTask = mSourceRecord.getTask(); - final ActivityStack sourceStack = mSourceRecord.getStack(); + final TaskRecord sourceTask = mSourceRecord.getTaskRecord(); + final ActivityStack sourceStack = mSourceRecord.getActivityStack(); // We only want to allow changing stack in two cases: // 1. If the target task is not the top one. Otherwise we would move the launching task to // the other side, rather than show two side by side. @@ -2191,8 +2206,8 @@ class ActivityStarter { final boolean moveStackAllowed = sourceStack.topTask() != sourceTask || !mStartActivity.canBeLaunchedOnDisplay(targetDisplayId); if (moveStackAllowed) { - mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.getTask(), - mOptions); + mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, + mStartActivity.getTaskRecord(), mOptions); // If target stack is not found now - we can't just rely on the source stack, as it may // be not suitable. Let's check other displays. if (mTargetStack == null && targetDisplayId != sourceStack.mDisplayId) { @@ -2229,7 +2244,7 @@ class ActivityStarter { ActivityRecord top = sourceTask.performClearTaskLocked(mStartActivity, mLaunchFlags); mKeepCurTransition = true; if (top != null) { - ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask()); + ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTaskRecord()); deliverNewIntent(top); // For paranoia, make sure we have correctly resumed the top activity. mTargetStack.mLastPausedActivity = null; @@ -2245,7 +2260,7 @@ class ActivityStarter { // stack if so. final ActivityRecord top = sourceTask.findActivityInHistoryLocked(mStartActivity); if (top != null) { - final TaskRecord task = top.getTask(); + final TaskRecord task = top.getTaskRecord(); task.moveActivityToFrontLocked(top); top.updateOptionsLocked(mOptions); ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task); @@ -2262,7 +2277,8 @@ class ActivityStarter { // the same task as the one that is starting it. addOrReparentStartingActivity(sourceTask, "setTaskFromSourceRecord"); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity - + " in existing task " + mStartActivity.getTask() + " from source " + mSourceRecord); + + " in existing task " + mStartActivity.getTaskRecord() + + " from source " + mSourceRecord); return START_SUCCESS; } @@ -2279,8 +2295,8 @@ class ActivityStarter { // Check whether we should actually launch the new activity in to the task, // or just reuse the current activity on top. ActivityRecord top = mInTask.getTopActivity(); - if (top != null && top.realActivity.equals(mStartActivity.realActivity) - && top.userId == mStartActivity.userId) { + if (top != null && top.mActivityComponent.equals(mStartActivity.mActivityComponent) + && top.mUserId == mStartActivity.mUserId) { if ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK)) { mTargetStack.moveTaskToFrontLocked(mInTask, mNoAnimation, mOptions, @@ -2322,7 +2338,7 @@ class ActivityStarter { addOrReparentStartingActivity(mInTask, "setTaskFromInTask"); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity - + " in explicit task " + mStartActivity.getTask()); + + " in explicit task " + mStartActivity.getTaskRecord()); return START_SUCCESS; } @@ -2348,17 +2364,18 @@ class ActivityStarter { mTargetStack.moveToFront("addingToTopTask"); } final ActivityRecord prev = mTargetStack.getTopActivity(); - final TaskRecord task = (prev != null) ? prev.getTask() : mTargetStack.createTaskRecord( - mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), mStartActivity.info, + final TaskRecord task = (prev != null) + ? prev.getTaskRecord() : mTargetStack.createTaskRecord( + mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId), mStartActivity.info, mIntent, null, null, true, mStartActivity, mSourceRecord, mOptions); addOrReparentStartingActivity(task, "setTaskToCurrentTopOrCreateNewTask"); mTargetStack.positionChildWindowContainerAtTop(task); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity - + " in new guessed " + mStartActivity.getTask()); + + " in new guessed " + mStartActivity.getTaskRecord()); } private void addOrReparentStartingActivity(TaskRecord parent, String reason) { - if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) { + if (mStartActivity.getTaskRecord() == null || mStartActivity.getTaskRecord() == parent) { parent.addActivityToTop(mStartActivity); } else { mStartActivity.reparent(parent, parent.mActivities.size() /* top */, reason); @@ -2394,7 +2411,7 @@ class ActivityStarter { private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, int launchFlags, ActivityOptions aOptions) { - final TaskRecord task = r.getTask(); + final TaskRecord task = r.getTaskRecord(); ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions); if (stack != null) { return stack; 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 8f99dae89316..0967afda6d2d 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -190,6 +190,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.IUserManager; import android.os.LocaleList; +import android.os.Looper; import android.os.Message; import android.os.PersistableBundle; import android.os.PowerManager; @@ -246,7 +247,6 @@ import com.android.internal.util.Preconditions; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.AppOpsService; import com.android.server.AttributeCache; -import com.android.server.DisplayThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.SystemServiceManager; @@ -753,9 +753,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return mGlobalLock; } - public void setActivityManagerService(IntentFirewall intentFirewall, - PendingIntentController intentController) { - mH = new H(); + public void initialize(IntentFirewall intentFirewall, PendingIntentController intentController, + Looper looper) { + mH = new H(looper); mUiHandler = new UiHandler(); mIntentFirewall = intentFirewall; final File systemDir = SystemServiceManager.ensureSystemDir(); @@ -1442,7 +1442,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return true; } // Keep track of the root activity of the task before we finish it - TaskRecord tr = r.getTask(); + final TaskRecord tr = r.getTaskRecord(); ActivityRecord rootR = tr.getRootActivity(); if (rootR == null) { Slog.w(TAG, "Finishing task with all activities already finished"); @@ -1457,7 +1457,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // We should consolidate. if (mController != null) { // Find the first activity that is not finishing. - ActivityRecord next = r.getStack().topRunningActivityLocked(token, 0); + final ActivityRecord next = r.getActivityStack().topRunningActivityLocked(token, 0); if (next != null) { // ask watcher if this is allowed boolean resumeOK = true; @@ -1519,7 +1519,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps // can finish. - final TaskRecord task = r.getTask(); + final TaskRecord task = r.getTaskRecord(); if (getLockTaskController().activityBlockedFromFinish(r)) { return false; } @@ -1822,7 +1822,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (r == null) { return false; } - final TaskRecord task = r.getTask(); + final TaskRecord task = r.getTaskRecord(); int index = task.mActivities.lastIndexOf(r); if (index > 0) { ActivityRecord under = task.mActivities.get(index - 1); @@ -1830,7 +1830,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final boolean translucentChanged = r.changeWindowTranslucency(false); if (translucentChanged) { - r.getStack().convertActivityToTranslucent(r); + r.getActivityStack().convertActivityToTranslucent(r); } mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); mWindowManager.setAppFullscreen(token, false); @@ -1847,7 +1847,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { ActivityRecord r = mRootActivityContainer.isInAnyStack(token); if (r != null) { - r.getStack().notifyActivityDrawnLocked(r); + r.getActivityStack().notifyActivityDrawnLocked(r); } } } @@ -1967,7 +1967,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final ActivityRecord srec = ActivityRecord.forTokenLocked(token); if (srec != null) { - return srec.getStack().shouldUpRecreateTaskLocked(srec, destAffinity); + return srec.getActivityStack().shouldUpRecreateTaskLocked(srec, destAffinity); } } return false; @@ -1980,7 +1980,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final ActivityRecord r = ActivityRecord.forTokenLocked(token); if (r != null) { - return r.getStack().navigateUpToLocked(r, destIntent, resultCode, resultData); + return r.getActivityStack().navigateUpToLocked( + r, destIntent, resultCode, resultData); } return false; } @@ -2300,7 +2301,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final long origId = Binder.clearCallingIdentity(); ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r != null) { - r.getStack().finishSubActivityLocked(r, resultWho, requestCode); + r.getActivityStack().finishSubActivityLocked(r, resultWho, requestCode); } Binder.restoreCallingIdentity(origId); } @@ -2545,7 +2546,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (r == null) { return; } - startLockTaskModeLocked(r.getTask(), false /* isSystemCaller */); + startLockTaskModeLocked(r.getTaskRecord(), false /* isSystemCaller */); } } @@ -2578,7 +2579,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (r == null) { return; } - stopLockTaskModeInternal(r.getTask(), false /* isSystemCaller */); + stopLockTaskModeInternal(r.getTaskRecord(), false /* isSystemCaller */); } } @@ -2671,7 +2672,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r != null) { r.setTaskDescription(td); - final TaskRecord task = r.getTask(); + final TaskRecord task = r.getTaskRecord(); task.updateTaskDescription(); mTaskChangeNotificationController.notifyTaskDescriptionChanged(task.taskId, td); } @@ -2727,7 +2728,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public boolean isTopOfTask(IBinder token) { synchronized (mGlobalLock) { ActivityRecord r = ActivityRecord.isInStackLocked(token); - return r != null && r.getTask().getTopActivity() == r; + return r != null && r.getTaskRecord().getTopActivity() == r; } } @@ -2766,8 +2767,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } if (structure != null) { // Pre-fill the task/activity component for all assist data receivers - structure.setTaskId(pae.activity.getTask().taskId); - structure.setActivityComponent(pae.activity.realActivity); + structure.setTaskId(pae.activity.getTaskRecord().taskId); + structure.setActivityComponent(pae.activity.mActivityComponent); structure.setHomeActivity(pae.isHome); } pae.haveResult = true; @@ -2875,9 +2876,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { + ainfo.applicationInfo.uid + ", calling uid=" + callingUid); } - final ActivityStack stack = r.getStack(); + final ActivityStack stack = r.getActivityStack(); final TaskRecord task = stack.createTaskRecord( - mStackSupervisor.getNextTaskIdForUserLocked(r.userId), ainfo, intent, + mStackSupervisor.getNextTaskIdForUserLocked(r.mUserId), ainfo, intent, null /* voiceSession */, null /* voiceInteractor */, !ON_TOP); if (!mRecentTasks.addToBottom(task)) { // The app has too many tasks already and we can't add any more @@ -2973,7 +2974,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (r == null) { return false; } - return r.getStack().safelyDestroyActivityLocked(r, "app-req"); + return r.getActivityStack().safelyDestroyActivityLocked(r, "app-req"); } finally { Binder.restoreCallingIdentity(origId); } @@ -3123,7 +3124,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { "exitFreeformMode: No activity record matching token=" + token); } - final ActivityStack stack = r.getStack(); + final ActivityStack stack = r.getActivityStack(); if (stack == null || !stack.inFreeformWindowingMode()) { throw new IllegalStateException( "exitFreeformMode: You can only go fullscreen from freeform."); @@ -3409,7 +3410,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (activity == null) { return false; } - userId = activity.userId; + userId = activity.mUserId; } return !DevicePolicyCache.getInstance().getScreenCaptureDisabled(userId); } @@ -3783,14 +3784,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } private boolean isInPictureInPictureMode(ActivityRecord r) { - if (r == null || r.getStack() == null || !r.inPinnedWindowingMode() - || r.getStack().isInStackLocked(r) == null) { + if (r == null || r.getActivityStack() == null || !r.inPinnedWindowingMode() + || r.getActivityStack().isInStackLocked(r) == null) { return false; } // If we are animating to fullscreen then we have already dispatched the PIP mode // changed, so we should reflect that check here as well. - final PinnedActivityStack stack = r.getStack(); + final PinnedActivityStack stack = r.getActivityStack(); final PinnedStackWindowController windowController = stack.getWindowContainerController(); return !windowController.isAnimatingBoundsToFullscreen(); } @@ -3826,7 +3827,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { r.pictureInPictureArgs.getSourceRectHint()); mRootActivityContainer.moveActivityToPinnedStack( r, sourceBounds, aspectRatio, "enterPictureInPictureMode"); - final PinnedActivityStack stack = r.getStack(); + final PinnedActivityStack stack = r.getActivityStack(); stack.setPictureInPictureAspectRatio(aspectRatio); stack.setPictureInPictureActions(actions); MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.appInfo.uid, @@ -3870,7 +3871,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // If the activity is already in picture-in-picture, update the pinned stack now // if it is not already expanding to fullscreen. Otherwise, the arguments will // be used the next time the activity enters PiP - final PinnedActivityStack stack = r.getStack(); + final PinnedActivityStack stack = r.getActivityStack(); if (!stack.isAnimatingBoundsToFullscreen()) { stack.setPictureInPictureAspectRatio( r.pictureInPictureArgs.getAspectRatio()); @@ -3928,8 +3929,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } if (params.hasSetAspectRatio() - && !mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId, - params.getAspectRatio())) { + && !mWindowManager.isValidPictureInPictureAspectRatio( + r.getActivityStack().mDisplayId, params.getAspectRatio())) { final float minAspectRatio = mContext.getResources().getFloat( com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio); final float maxAspectRatio = mContext.getResources().getFloat( @@ -4015,7 +4016,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } int err; - if ((err = vrService.hasVrPackage(packageName, r.userId)) != + if ((err = vrService.hasVrPackage(packageName, r.mUserId)) != VrManagerInternal.NO_ERROR) { return err; } @@ -4045,7 +4046,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (ActivityRecord.forTokenLocked(callingActivity) != activity) { throw new SecurityException("Only focused activity can call startVoiceInteraction"); } - if (mRunningVoice != null || activity.getTask().voiceSession != null + if (mRunningVoice != null || activity.getTaskRecord().voiceSession != null || activity.voiceSession != null) { Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction"); return; @@ -4255,7 +4256,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (mLastResumedActivity == null) { return getCurrentUserId(); } - return mLastResumedActivity.userId; + return mLastResumedActivity.mUserId; } } @@ -4435,7 +4436,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { + " to main stack for VR"); final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().getOrCreateStack( WINDOWING_MODE_FULLSCREEN, r.getActivityType(), true /* toTop */); - moveTaskToStack(r.getTask().taskId, stack.mStackId, true /* toTop */); + moveTaskToStack(r.getTaskRecord().taskId, stack.mStackId, true /* toTop */); } mH.post(() -> { if (!mVrController.onVrModeChanged(r)) { @@ -4511,6 +4512,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) { @@ -4631,7 +4647,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } needSep = true; synchronized (mGlobalLock) { - final TaskRecord task = r.getTask(); + final TaskRecord task = r.getTaskRecord(); if (lastTask != task) { lastTask = task; pw.print("TASK "); pw.print(lastTask.affinity); @@ -5095,7 +5111,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { /** Update AMS states when an activity is resumed. */ void setResumedActivityUncheckLocked(ActivityRecord r, String reason) { - final TaskRecord task = r.getTask(); + final TaskRecord task = r.getTaskRecord(); if (task.isActivityTypeStandard()) { if (mCurAppTimeTracker != r.appTimeTracker) { // We are switching app tracking. Complete the current one. @@ -5127,7 +5143,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (mLastResumedActivity != null) { final IVoiceInteractionSession session; - final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTask(); + final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTaskRecord(); if (lastResumedActivityTask != null && lastResumedActivityTask.voiceSession != null) { session = lastResumedActivityTask.voiceSession; @@ -5145,8 +5161,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - if (mLastResumedActivity != null && r.userId != mLastResumedActivity.userId) { - mAmInternal.sendForegroundProfileChanged(r.userId); + if (mLastResumedActivity != null && r.mUserId != mLastResumedActivity.mUserId) { + mAmInternal.sendForegroundProfileChanged(r.mUserId); } updateResumedAppTrace(r); mLastResumedActivity = r; @@ -5157,7 +5173,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { applyUpdateVrModeLocked(r); EventLogTags.writeAmSetResumedActivity( - r == null ? -1 : r.userId, + r == null ? -1 : r.mUserId, r == null ? "NULL" : r.shortComponentName, reason); } @@ -5219,7 +5235,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { void updateUsageStats(ActivityRecord component, boolean resumed) { final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::updateUsageStats, - mAmInternal, component.realActivity, component.app.mUid, component.userId, resumed); + mAmInternal, component.mActivityComponent, component.app.mUid, component.mUserId, + resumed); mH.sendMessage(m); } @@ -5254,7 +5271,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mHeavyWeightProcess = root.app; final Message m = PooledLambda.obtainMessage( ActivityTaskManagerService::postHeavyWeightProcessNotification, this, - root.app, root.intent, root.userId); + root.app, root.intent, root.mUserId); mH.sendMessage(m); } @@ -5576,8 +5593,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { static final int FIRST_ACTIVITY_STACK_MSG = 100; static final int FIRST_SUPERVISOR_STACK_MSG = 200; - public H() { - super(DisplayThread.get().getLooper()); + H(Looper looper) { + super(looper); } @Override @@ -5622,7 +5639,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final ActivityRecord homeActivity = mRootActivityContainer.getDefaultDisplayHomeActivityForUser(userId); - return homeActivity == null ? null : homeActivity.realActivity; + return homeActivity == null ? null : homeActivity.mActivityComponent; } } @@ -6116,9 +6133,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { int requestCode, int resultCode, Intent data) { synchronized (mGlobalLock) { final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken); - if (r != null && r.getStack() != null) { - r.getStack().sendActivityResultLocked(callingUid, r, resultWho, requestCode, - resultCode, data); + if (r != null && r.getActivityStack() != null) { + r.getActivityStack().sendActivityResultLocked(callingUid, r, resultWho, + requestCode, resultCode, data); } } } @@ -6392,7 +6409,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final ActivityRecord homeActivity = mRootActivityContainer.getDefaultDisplayHomeActivity(); if (homeActivity != null) { - activityOptions.setLaunchTaskId(homeActivity.getTask().taskId); + activityOptions.setLaunchTaskId(homeActivity.getTaskRecord().taskId); } mContext.startActivityAsUser(intent, activityOptions.toBundle(), UserHandle.CURRENT); @@ -6879,5 +6896,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/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java index 04368570e02e..6c3fbc1f4160 100644 --- a/services/core/java/com/android/server/wm/AppWarnings.java +++ b/services/core/java/com/android/server/wm/AppWarnings.java @@ -123,7 +123,8 @@ class AppWarnings { // TODO(b/75318890): Need to move this to when the app actually crashes. if (/*ActivityManager.isRunningInTestHarness() - &&*/ !mAlwaysShowUnsupportedCompileSdkWarningActivities.contains(r.realActivity)) { + &&*/ !mAlwaysShowUnsupportedCompileSdkWarningActivities.contains( + r.mActivityComponent)) { // Don't show warning if we are running in a test harness and we don't have to always // show for this activity. return; diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java index b9b9d31f5b71..bb38f3035a6c 100644 --- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java +++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java @@ -53,7 +53,8 @@ class AppWindowThumbnail implements Animatable { AppWindowThumbnail(Transaction t, AppWindowToken appToken, GraphicBuffer thumbnailHeader) { mAppToken = appToken; - mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, appToken.mService); + mSurfaceAnimator = + new SurfaceAnimator(this, this::onAnimationFinished, appToken.mWmService); mWidth = thumbnailHeader.getWidth(); mHeight = thumbnailHeader.getHeight(); @@ -93,11 +94,11 @@ class AppWindowThumbnail implements Animatable { void startAnimation(Transaction t, Animation anim, Point position) { anim.restrictDuration(MAX_ANIMATION_DURATION); - anim.scaleCurrentDuration(mAppToken.mService.getTransitionAnimationScaleLocked()); + anim.scaleCurrentDuration(mAppToken.mWmService.getTransitionAnimationScaleLocked()); mSurfaceAnimator.startAnimation(t, new LocalAnimationAdapter( new WindowAnimationSpec(anim, position, mAppToken.getDisplayContent().mAppTransition.canSkipFirstFrame()), - mAppToken.mService.mSurfaceAnimationRunner), false /* hidden */); + mAppToken.mWmService.mSurfaceAnimationRunner), false /* hidden */); } private void onAnimationFinished() { diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index df81c07ba530..fb93d39b33d4 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -554,7 +554,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // * or this is an opening app and windows are being replaced. boolean visibilityChanged = false; if (isHidden() == visible || (isHidden() && mIsExiting) || (visible && waitingForReplacement())) { - final AccessibilityController accessibilityController = mService.mAccessibilityController; + final AccessibilityController accessibilityController = + mWmService.mAccessibilityController; boolean changed = false; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Changing app " + this + " hidden=" + isHidden() + " performLayout=" + performLayout); @@ -595,7 +596,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // We are becoming visible, so better freeze the screen with the windows that are // getting visible so we also wait for them. - forAllWindows(mService::makeWindowFreezingScreenIfNeededLocked, true); + forAllWindows(mWmService::makeWindowFreezingScreenIfNeededLocked, true); } if (DEBUG_APP_TRANSITIONS) { @@ -606,9 +607,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (changed) { getDisplayContent().getInputMonitor().setUpdateInputWindowsNeededLw(); if (performLayout) { - mService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, + mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/); - mService.mWindowPlacerLocked.performSurfacePlacement(); + mWmService.mWindowPlacerLocked.performSurfacePlacement(); } getDisplayContent().getInputMonitor().updateInputWindowsLw(false /*force*/); } @@ -635,7 +636,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // The token was made immediately visible, there will be no entrance animation. // We need to inform the client the enter animation was finished. mEnteringAnimation = true; - mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token); + mWmService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked( + token); } // If we're becoming visible, immediately change client visibility as well. there seem @@ -658,7 +660,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // Take the screenshot before possibly hiding the WSA, otherwise the screenshot // will not be taken. - mService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible); + mWmService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible); } // If we are hidden but there is no delay needed we immediately @@ -774,7 +776,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree getDisplayContent().mOpeningApps.remove(this); getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this); - mService.mTaskSnapshotController.onAppRemoved(this); + mWmService.mTaskSnapshotController.onAppRemoved(this); waitingToShow = false; if (getDisplayContent().mClosingApps.contains(this)) { delayed = true; @@ -827,7 +829,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this + " displayId=" + dc.getDisplayId()); dc.setFocusedApp(null); - mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/); + mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/); } if (!delayed) { @@ -1141,7 +1143,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // if we got a replacement window, reset the timeout to give drawing more time if (gotReplacementWindow) { - mService.scheduleWindowReplacementTimeouts(this); + mWmService.scheduleWindowReplacementTimeouts(this); } checkKeyguardFlagsChanged(); } @@ -1271,7 +1273,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree final WindowState win = mChildren.get(i); win.onUnfreezeBounds(); } - mService.mWindowPlacerLocked.performSurfacePlacement(); + mWmService.mWindowPlacerLocked.performSurfacePlacement(); } void setAppLayoutChanges(int changes, String reason) { @@ -1279,7 +1281,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree final DisplayContent dc = getDisplayContent(); dc.pendingLayoutChanges |= changes; if (DEBUG_LAYOUT_REPEATS) { - mService.mWindowPlacerLocked.debugLayoutRepeats(reason, dc.pendingLayoutChanges); + mWmService.mWindowPlacerLocked.debugLayoutRepeats(reason, dc.pendingLayoutChanges); } } } @@ -1300,12 +1302,12 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (!hiddenRequested) { if (!mFreezingScreen) { mFreezingScreen = true; - mService.registerAppFreezeListener(this); - mService.mAppsFreezingScreen++; - if (mService.mAppsFreezingScreen == 1) { - mService.startFreezingDisplayLocked(0, 0, getDisplayContent()); - mService.mH.removeMessages(H.APP_FREEZE_TIMEOUT); - mService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000); + mWmService.registerAppFreezeListener(this); + mWmService.mAppsFreezingScreen++; + if (mWmService.mAppsFreezingScreen == 1) { + mWmService.startFreezingDisplayLocked(0, 0, getDisplayContent()); + mWmService.mH.removeMessages(H.APP_FREEZE_TIMEOUT); + mWmService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000); } } final int count = mChildren.size(); @@ -1330,15 +1332,15 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (force || unfrozeWindows) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "No longer freezing: " + this); mFreezingScreen = false; - mService.unregisterAppFreezeListener(this); - mService.mAppsFreezingScreen--; - mService.mLastFinishedFreezeSource = this; + mWmService.unregisterAppFreezeListener(this); + mWmService.mAppsFreezingScreen--; + mWmService.mLastFinishedFreezeSource = this; } if (unfreezeSurfaceNow) { if (unfrozeWindows) { - mService.mWindowPlacerLocked.performSurfacePlacement(); + mWmService.mWindowPlacerLocked.performSurfacePlacement(); } - mService.stopFreezingDisplayLocked(); + mWmService.stopFreezingDisplayLocked(); } } @@ -1429,10 +1431,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // pending opening apps. getDisplayContent().mOpeningApps.remove(this); - mService.updateFocusedWindowLocked( + mWmService.updateFocusedWindowLocked( UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/); getDisplayContent().setLayoutNeeded(); - mService.mWindowPlacerLocked.performSurfacePlacement(); + mWmService.mWindowPlacerLocked.performSurfacePlacement(); } finally { Binder.restoreCallingIdentity(origId); } @@ -1602,7 +1604,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (mDisplayContent != null) { mDisplayContent.setLayoutNeeded(); } - mService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN, token).sendToTarget(); + mWmService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN, token).sendToTarget(); // Notify the pinned stack upon all windows drawn. If there was an animation in // progress then this signal will resume that animation. @@ -1636,8 +1638,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return false; } - if (mLastTransactionSequence != mService.mTransactionSequence) { - mLastTransactionSequence = mService.mTransactionSequence; + if (mLastTransactionSequence != mWmService.mTransactionSequence) { + mLastTransactionSequence = mWmService.mTransactionSequence; mNumDrawnWindows = 0; startingDisplayed = false; @@ -1769,7 +1771,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } final ActivityManager.TaskSnapshot snapshot = - mService.mTaskSnapshotController.getSnapshot( + mWmService.mTaskSnapshotController.getSnapshot( getTask().mTaskId, getTask().mUserId, false /* restoreFromDisk */, false /* reducedResolution */); final int type = getStartingWindowType(newTask, taskSwitch, processRunning, @@ -1788,7 +1790,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (theme != 0) { AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme, com.android.internal.R.styleable.Window, - mService.mCurrentUserId); + mWmService.mCurrentUserId); if (ent == null) { // Whoops! App doesn't exist. Um. Okay. We'll just pretend like we didn't // see that. @@ -1838,7 +1840,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData"); - startingData = new SplashScreenStartingData(mService, pkg, + startingData = new SplashScreenStartingData(mWmService, pkg, theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, getMergedOverrideConfiguration()); scheduleAddStartingWindow(); @@ -1853,7 +1855,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData"); - startingData = new SnapshotStartingData(mService, snapshot); + startingData = new SnapshotStartingData(mWmService, snapshot); scheduleAddStartingWindow(); return true; } @@ -1862,9 +1864,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // 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 (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) { if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Enqueueing ADD_STARTING"); - mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow); + mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow); } } @@ -1872,9 +1874,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree @Override public void run() { - synchronized (mService.mGlobalLock) { + synchronized (mWmService.mGlobalLock) { // There can only be one adding request, silly caller! - mService.mAnimationHandler.removeCallbacks(this); + mWmService.mAnimationHandler.removeCallbacks(this); } if (startingData == null) { @@ -1898,7 +1900,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } if (surface != null) { boolean abort = false; - synchronized (mService.mGlobalLock) { + synchronized (mWmService.mGlobalLock) { // If the window was successfully added, then // we need to remove it. if (removed || startingData == null) { @@ -1998,7 +2000,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // 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(() -> { + mWmService.mAnimationHandler.post(() -> { if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Removing startingView=" + surface); try { surface.remove(); @@ -2054,7 +2056,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree final boolean containsShowWhenLocked = containsShowWhenLockedWindow(); if (containsDismissKeyguard != mLastContainsDismissKeyguardWindow || containsShowWhenLocked != mLastContainsShowWhenLockedWindow) { - mService.notifyKeyguardFlagsChanged(null /* callback */, + mWmService.notifyKeyguardFlagsChanged(null /* callback */, getDisplayContent().getDisplayId()); } mLastContainsDismissKeyguardWindow = containsDismissKeyguard; @@ -2194,7 +2196,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter, boolean isVoiceInteraction) { - if (mService.mDisableTransitionAnimation || !shouldAnimate(transit)) { + if (mWmService.mDisableTransitionAnimation || !shouldAnimate(transit)) { if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { Slog.v(TAG_WM, "applyAnimation: transition animation is disabled or skipped." + " atoken=" + this); @@ -2228,7 +2230,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree getDisplayContent().mAppTransition.canSkipFirstFrame(), appStackClipMode, true /* isAppAnimation */), - mService.mSurfaceAnimationRunner); + mWmService.mSurfaceAnimationRunner); if (a.getZAdjustment() == Animation.ZORDER_TOP) { mNeedsZBoost = true; } @@ -2311,7 +2313,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree final int containingWidth = frame.width(); final int containingHeight = frame.height(); a.initialize(containingWidth, containingHeight, width, height); - a.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked()); + a.scaleCurrentDuration(mWmService.getTransitionAnimationScaleLocked()); } return a; } @@ -2516,7 +2518,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return; } final Rect frame = win.getFrameLw(); - final int thumbnailDrawableRes = getTask().mUserId == mService.mCurrentUserId + final int thumbnailDrawableRes = getTask().mUserId == mWmService.mCurrentUserId ? R.drawable.ic_account_circle : R.drawable.ic_corp_badge; final GraphicBuffer thumbnail = diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java index fa3c7ca29284..aea071f94ba2 100644 --- a/services/core/java/com/android/server/wm/Dimmer.java +++ b/services/core/java/com/android/server/wm/Dimmer.java @@ -131,7 +131,7 @@ class Dimmer { if (!mDimming) { dimAnimatable.getPendingTransaction().destroy(mDimLayer); } - }, mHost.mService); + }, mHost.mWmService); } } @@ -333,7 +333,7 @@ class Dimmer { SurfaceControl.Transaction t, float startAlpha, float endAlpha) { mSurfaceAnimatorStarter.startAnimation(animator, t, new LocalAnimationAdapter( new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)), - mHost.mService.mSurfaceAnimationRunner), false /* hidden */); + mHost.mWmService.mSurfaceAnimationRunner), false /* hidden */); } private long getDimDuration(WindowContainer container) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c0e983653b27..05e82676a40a 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -29,12 +29,12 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.FLAG_PRIVATE; +import static android.view.InsetsState.TYPE_IME; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import static android.view.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; @@ -100,6 +100,7 @@ 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.CUSTOM_SCREEN_ROTATION; +import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE; import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS; import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION; import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER; @@ -208,21 +209,21 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** The containers below are the only child containers the display can have. */ // Contains all window containers that are related to apps (Activities) - private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(mService); + private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(mWmService); // Contains all non-app window containers that should be displayed above the app containers // (e.g. Status bar) private final AboveAppWindowContainers mAboveAppWindowsContainers = - new AboveAppWindowContainers("mAboveAppWindowsContainers", mService); + new AboveAppWindowContainers("mAboveAppWindowsContainers", mWmService); // Contains all non-app window containers that should be displayed below the app containers // (e.g. Wallpaper). private final NonAppWindowContainers mBelowAppWindowsContainers = - new NonAppWindowContainers("mBelowAppWindowsContainers", mService); + new NonAppWindowContainers("mBelowAppWindowsContainers", mWmService); // Contains all IME window containers. Note that the z-ordering of the IME windows will depend // on the IME target. We mainly have this container grouping so we can keep track of all the IME // window containers together and move them in-sync if/when needed. We use a subclass of // WindowContainer which is omitted from screen magnification, as the IME is never magnified. private final NonMagnifiableWindowContainers mImeWindowsContainers = - new NonMagnifiableWindowContainers("mImeWindowsContainers", mService); + new NonMagnifiableWindowContainers("mImeWindowsContainers", mWmService); private WindowState mTmpWindow; private WindowState mTmpWindow2; @@ -525,7 +526,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (w.performShowLocked()) { pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM; if (DEBUG_LAYOUT_REPEATS) { - mService.mWindowPlacerLocked.debugLayoutRepeats( + mWmService.mWindowPlacerLocked.debugLayoutRepeats( "updateWindowsAndWallpaperLocked 5", pendingLayoutChanges); } } @@ -556,7 +557,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final Consumer<WindowState> mScheduleToastTimeout = w -> { final int lostFocusUid = mTmpWindow.mOwnerUid; - final Handler handler = mService.mH; + final Handler handler = mWmService.mH; if (w.mAttrs.type == TYPE_TOAST && w.mOwnerUid == lostFocusUid) { if (!handler.hasMessages(WINDOW_HIDE_TIMEOUT, w)) { handler.sendMessageDelayed(handler.obtainMessage(WINDOW_HIDE_TIMEOUT, w), @@ -618,7 +619,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final Consumer<WindowState> mPerformLayout = w -> { // Don't do layout of a window if it is not visible, or soon won't be visible, to avoid // wasting time and funky changes while a window is animating away. - final boolean gone = (mTmpWindow != null && mService.mPolicy.canBeHiddenByKeyguardLw(w)) + final boolean gone = (mTmpWindow != null && mWmService.mPolicy.canBeHiddenByKeyguardLw(w)) || w.isGoneForLayoutLw(); if (DEBUG_LAYOUT && !w.mLayoutAttached) { @@ -686,7 +687,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // If this view is GONE, then skip it -- keep the current frame, and let the caller // know so they can ignore it if they want. (We do the normal layout for INVISIBLE // windows, since that means "perform layout as normal, just don't display"). - if (mTmpWindow != null && mService.mPolicy.canBeHiddenByKeyguardLw(w)) { + if (mTmpWindow != null && mWmService.mPolicy.canBeHiddenByKeyguardLw(w)) { return; } if ((w.mViewVisibility != GONE && w.mRelayoutCalled) || !w.mHaveFrame @@ -721,10 +722,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mInputMethodTarget); private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> { - final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked; + final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked; final boolean obscuredChanged = w.mObscured != mTmpApplySurfaceChangesTransactionState.obscured; - final RootWindowContainer root = mService.mRoot; + final RootWindowContainer root = mWmService.mRoot; // Update effect. w.mObscured = mTmpApplySurfaceChangesTransactionState.obscured; @@ -815,7 +816,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } if (!mLosingFocus.isEmpty() && w.isFocused() && w.isDisplayedLw()) { - mService.mH.obtainMessage(REPORT_LOSING_FOCUS, this).sendToTarget(); + mWmService.mH.obtainMessage(REPORT_LOSING_FOCUS, this).sendToTarget(); } w.updateResizingWindowIfNeeded(); @@ -840,7 +841,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mDisplay = display; mDisplayId = display.getDisplayId(); - mWallpaperController = new WallpaperController(mService, this); + mWallpaperController = new WallpaperController(mWmService, this); display.getDisplayInfo(mDisplayInfo); display.getMetrics(mDisplayMetrics); isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY; @@ -857,8 +858,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mBoundsAnimationController = new BoundsAnimationController(service.mContext, mAppTransition, SurfaceAnimationThread.getHandler(), animationHandler); - if (mService.mInputManager != null) { - final InputChannel inputChannel = mService.mInputManager.monitorInput("Display " + if (mWmService.mInputManager != null) { + final InputChannel inputChannel = mWmService.mInputManager.monitorInput("Display " + mDisplayId, mDisplayId); mPointerEventDispatcher = inputChannel != null ? new PointerEventDispatcher(inputChannel) : null; @@ -870,18 +871,18 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (isDefaultDisplay) { // The policy may be invoked right after here, so it requires the necessary default // fields of this display content. - mService.mPolicy.setDefaultDisplay(this); + mWmService.mPolicy.setDefaultDisplay(this); } - if (mService.mDisplayReady) { + if (mWmService.mDisplayReady) { mDisplayPolicy.onConfigurationChanged(); } - if (mService.mSystemReady) { + if (mWmService.mSystemReady) { mDisplayPolicy.systemReady(); } mDividerControllerLocked = new DockedStackDividerController(service, this); mPinnedStackControllerLocked = new PinnedStackController(service, this); - final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession).setOpaque(true); + final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession).setOpaque(true); mWindowingLayer = b.setName("Display Root").build(); mOverlayLayer = b.setName("Display Overlays").build(); @@ -900,20 +901,20 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo super.addChild(mImeWindowsContainers, null); // Add itself as a child to the root container. - mService.mRoot.addChild(this, null); + mWmService.mRoot.addChild(this, null); // TODO(b/62541591): evaluate whether this is the best spot to declare the // {@link DisplayContent} ready for use. mDisplayReady = true; - mService.mAnimator.addDisplayLocked(mDisplayId); + mWmService.mAnimator.addDisplayLocked(mDisplayId); mInputMonitor = new InputMonitor(service, mDisplayId); mInsetsStateController = new InsetsStateController(this); } boolean isReady() { // The display is ready when the system and the individual display are both ready. - return mService.mDisplayReady && mDisplayReady; + return mWmService.mDisplayReady && mDisplayReady; } int getDisplayId() { @@ -933,7 +934,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } private void addWindowToken(IBinder binder, WindowToken token) { - final DisplayContent dc = mService.mRoot.getWindowTokenDisplay(token); + final DisplayContent dc = mWmService.mRoot.getWindowTokenDisplay(token); if (dc != null) { // We currently don't support adding a window token to the display if the display // already has the binder mapped to another token. If there is a use case for supporting @@ -1139,7 +1140,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** Notify the configuration change of this display. */ void sendNewConfiguration() { - mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, this).sendToTarget(); + mWmService.mH.obtainMessage(SEND_NEW_CONFIGURATION, this).sendToTarget(); } /** @@ -1214,7 +1215,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } screenRotationAnimation = - mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId); + mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId); if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) { // Rotation updates cannot be performed while the previous rotation change // animation is still in progress. Skip this update. We will try updating @@ -1222,7 +1223,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, animation in progress."); return false; } - if (mService.mDisplayFrozen) { + if (mWmService.mDisplayFrozen) { // Even if the screen rotation animation has finished (e.g. isAnimating // returns false), there is still some time where we haven't yet unfrozen // the display. We also need to abort rotation here. @@ -1232,7 +1233,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } - if (!mService.mDisplayEnabled) { + if (!mWmService.mDisplayEnabled) { // No point choosing a rotation if the display is not enabled. if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, display is not enabled."); return false; @@ -1263,8 +1264,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (hasPinnedStack()) { mayRotateSeamlessly = false; } - for (int i = 0; i < mService.mSessions.size(); i++) { - if (mService.mSessions.valueAt(i).hasAlertWindowSurfaces()) { + for (int i = 0; i < mWmService.mSessions.size(); i++) { + if (mWmService.mSessions.valueAt(i).hasAlertWindowSurfaces()) { mayRotateSeamlessly = false; break; } @@ -1302,8 +1303,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mRotation = rotation; mAltOrientation = altOrientation; - mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE; - mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT, + mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE; + mWmService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT, this, WINDOW_FREEZE_TIMEOUT_DURATION); setLayoutNeeded(); @@ -1311,14 +1312,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mDisplayPolicy.selectRotationAnimationLw(anim); if (!rotateSeamlessly) { - mService.startFreezingDisplayLocked(anim[0], anim[1], this); + mWmService.startFreezingDisplayLocked(anim[0], anim[1], this); // startFreezingDisplayLocked can reset the ScreenRotationAnimation. } else { // The screen rotation animation uses a screenshot to freeze the screen // while windows resize underneath. // When we are rotating seamlessly, we allow the elements to transition // to their rotated state independently and without a freeze required. - mService.startSeamlessRotation(); + mWmService.startSeamlessRotation(); } return true; @@ -1333,9 +1334,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ void applyRotationLocked(final int oldRotation, final int rotation) { mDisplayRotation.setRotation(rotation); - final boolean rotateSeamlessly = mService.isRotatingSeamlessly(); + final boolean rotateSeamlessly = mWmService.isRotatingSeamlessly(); ScreenRotationAnimation screenRotationAnimation = rotateSeamlessly - ? null : mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId); + ? null : mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId); // We need to update our screen size information to match the new rotation. If the rotation // has actually changed then this method will return true and, according to the comment at // the top of the method, the caller is obligated to call computeNewConfigurationLocked(). @@ -1348,9 +1349,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()) { if (screenRotationAnimation.setRotation(getPendingTransaction(), rotation, - MAX_ANIMATION_DURATION, mService.getTransitionAnimationScaleLocked(), + MAX_ANIMATION_DURATION, mWmService.getTransitionAnimationScaleLocked(), mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight)) { - mService.scheduleAnimationLocked(); + mWmService.scheduleAnimationLocked(); } } @@ -1359,27 +1360,27 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo rotateSeamlessly); }, true /* traverseTopToBottom */); - mService.mDisplayManagerInternal.performTraversal(getPendingTransaction()); + mWmService.mDisplayManagerInternal.performTraversal(getPendingTransaction()); scheduleAnimation(); forAllWindows(w -> { if (w.mHasSurface && !rotateSeamlessly) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Set mOrientationChanging of " + w); w.setOrientationChanging(true); - mService.mRoot.mOrientationChangeComplete = false; + mWmService.mRoot.mOrientationChangeComplete = false; w.mLastFreezeDuration = 0; } w.mReportOrientationChanged = true; }, true /* traverseTopToBottom */); if (rotateSeamlessly) { - mService.mH.sendNewMessageDelayed(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT, + mWmService.mH.sendNewMessageDelayed(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT, this, SEAMLESS_ROTATION_TIMEOUT_DURATION); } - for (int i = mService.mRotationWatchers.size() - 1; i >= 0; i--) { + for (int i = mWmService.mRotationWatchers.size() - 1; i >= 0; i--) { final WindowManagerService.RotationWatcher rotationWatcher - = mService.mRotationWatchers.get(i); + = mWmService.mRotationWatchers.get(i); if (rotationWatcher.mDisplayId == mDisplayId) { try { rotationWatcher.mWatcher.onRotationChanged(rotation); @@ -1392,9 +1393,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // TODO (multi-display): Magnification is supported only for the default display. // Announce rotation only if we will not animate as we already have the // windows in final state. Otherwise, we make this call at the rotation end. - if (screenRotationAnimation == null && mService.mAccessibilityController != null + if (screenRotationAnimation == null && mWmService.mAccessibilityController != null && isDefaultDisplay) { - mService.mAccessibilityController.onRotationChangedLocked(this); + mWmService.mAccessibilityController.onRotationChangedLocked(this); } } @@ -1428,9 +1429,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo Slog.d(TAG, "Registering PointerEventListener for DisplayId: " + mDisplayId); } - mTapDetector = new TaskTapPointerEventListener(mService, this); + mTapDetector = new TaskTapPointerEventListener(mWmService, this); registerPointerEventListener(mTapDetector); - registerPointerEventListener(mService.mMousePositionTracker); + registerPointerEventListener(mWmService.mMousePositionTracker); } } @@ -1495,7 +1496,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // because we don't want letter-/pillar-boxing during resize. final DisplayInfo overrideDisplayInfo = mShouldOverrideDisplayConfiguration ? mDisplayInfo : null; - mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId, + mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId, overrideDisplayInfo); mBaseDisplayRect.set(0, 0, dw, dh); @@ -1581,10 +1582,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo config.densityDpi = displayInfo.logicalDensityDpi; config.colorMode = - ((displayInfo.isHdr() && mService.hasHdrSupport()) + ((displayInfo.isHdr() && mWmService.hasHdrSupport()) ? Configuration.COLOR_MODE_HDR_YES : Configuration.COLOR_MODE_HDR_NO) - | (displayInfo.isWideColorGamut() && mService.hasWideColorGamutSupport() + | (displayInfo.isWideColorGamut() && mWmService.hasWideColorGamutSupport() ? Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES : Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_NO); @@ -1596,7 +1597,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo int keyboardPresence = 0; int navigationPresence = 0; - final InputDevice[] devices = mService.mInputManager.getInputDevices(); + final InputDevice[] devices = mWmService.mInputManager.getInputDevices(); final int len = devices != null ? devices.length : 0; for (int i = 0; i < len; i++) { InputDevice device = devices[i]; @@ -1607,7 +1608,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo WindowManagerPolicy.PRESENCE_INTERNAL; // TODO(multi-display): Configure on per-display basis. - if (mService.mIsTouchDevice) { + if (mWmService.mIsTouchDevice) { if ((sources & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN) { config.touchscreen = Configuration.TOUCHSCREEN_FINGER; @@ -1632,7 +1633,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } - if (config.navigation == Configuration.NAVIGATION_NONAV && mService.mHasPermanentDpad) { + if (config.navigation == Configuration.NAVIGATION_NONAV && mWmService.mHasPermanentDpad) { config.navigation = Configuration.NAVIGATION_DPAD; navigationPresence |= WindowManagerPolicy.PRESENCE_INTERNAL; } @@ -1640,10 +1641,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // Determine whether a hard keyboard is available and enabled. // TODO(multi-display): Should the hardware keyboard be tied to a display or to a device? boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS; - if (hardKeyboardAvailable != mService.mHardKeyboardAvailable) { - mService.mHardKeyboardAvailable = hardKeyboardAvailable; - mService.mH.removeMessages(WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE); - mService.mH.sendEmptyMessage(WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE); + if (hardKeyboardAvailable != mWmService.mHardKeyboardAvailable) { + mWmService.mHardKeyboardAvailable = hardKeyboardAvailable; + mWmService.mH.removeMessages(REPORT_HARD_KEYBOARD_STATUS_CHANGE); + mWmService.mH.sendEmptyMessage(REPORT_HARD_KEYBOARD_STATUS_CHANGE); } mDisplayPolicy.updateConfigurationDependentBehaviors(); @@ -1652,7 +1653,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO; config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO; - mService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence); + mWmService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence); } private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh, @@ -1949,7 +1950,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override void onAppTransitionDone() { super.onAppTransitionDone(); - mService.mWindowsChanged = true; + mWmService.mWindowsChanged = true; } /** @@ -2001,9 +2002,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override int getOrientation() { - final WindowManagerPolicy policy = mService.mPolicy; + final WindowManagerPolicy policy = mWmService.mPolicy; - if (mService.mDisplayFrozen) { + if (mWmService.mDisplayFrozen) { if (mLastWindowForcedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId + " is frozen, return " + mLastWindowForcedOrientation); @@ -2044,7 +2045,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } void initializeDisplayBaseInfo() { - final DisplayManagerInternal displayManagerInternal = mService.mDisplayManagerInternal; + final DisplayManagerInternal displayManagerInternal = mWmService.mDisplayManagerInternal; if (displayManagerInternal != null) { // Bootstrap the default logical display from the display manager. final DisplayInfo newDisplayInfo = displayManagerInternal.getDisplayInfo(mDisplayId); @@ -2067,7 +2068,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ private void updateBaseDisplayMetricsIfNeeded() { // Get real display metrics without overrides from WM. - mService.mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mDisplayInfo); + mWmService.mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mDisplayInfo); final int orientation = mDisplayInfo.rotation; final boolean rotated = (orientation == ROTATION_90 || orientation == ROTATION_270); final int newWidth = rotated ? mDisplayInfo.logicalHeight : mDisplayInfo.logicalWidth; @@ -2096,7 +2097,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mInitialDisplayHeight = newHeight; mInitialDisplayDensity = newDensity; mInitialDisplayCutout = newCutout; - mService.reconfigureDisplayLocked(this); + mWmService.reconfigureDisplayLocked(this); } } @@ -2147,9 +2148,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo void setForcedDensity(int density, int userId) { final boolean clear = density == mInitialDisplayDensity; final boolean updateCurrent = userId == UserHandle.USER_CURRENT; - if (mService.mCurrentUserId == userId || updateCurrent) { + if (mWmService.mCurrentUserId == userId || updateCurrent) { mBaseDisplayDensity = density; - mService.reconfigureDisplayLocked(this); + mWmService.reconfigureDisplayLocked(this); } if (updateCurrent) { // We are applying existing settings so no need to save it again. @@ -2159,7 +2160,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (density == mInitialDisplayDensity) { density = 0; } - mService.mDisplayWindowSettings.setForcedDensity(this, density, userId); + mWmService.mDisplayWindowSettings.setForcedDensity(this, density, userId); } /** @param mode {@link #FORCE_SCALING_MODE_AUTO} or {@link #FORCE_SCALING_MODE_DISABLED}. */ @@ -2170,9 +2171,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mDisplayScalingDisabled = (mode != FORCE_SCALING_MODE_AUTO); Slog.i(TAG_WM, "Using display scaling mode: " + (mDisplayScalingDisabled ? "off" : "auto")); - mService.reconfigureDisplayLocked(this); + mWmService.reconfigureDisplayLocked(this); - mService.mDisplayWindowSettings.setForcedScalingMode(this, mode); + mWmService.mDisplayWindowSettings.setForcedScalingMode(this, mode); } /** If the given width and height equal to initial size, the setting will be cleared. */ @@ -2189,12 +2190,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo Slog.i(TAG_WM, "Using new display size: " + width + "x" + height); updateBaseDisplayMetrics(width, height, mBaseDisplayDensity); - mService.reconfigureDisplayLocked(this); + mWmService.reconfigureDisplayLocked(this); if (clear) { width = height = 0; } - mService.mDisplayWindowSettings.setForcedSize(this, width, height); + mWmService.mDisplayWindowSettings.setForcedSize(this, width, height); } void getStableRect(Rect out) { @@ -2205,7 +2206,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (DEBUG_STACK) Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId=" + mDisplayId); - final TaskStack stack = new TaskStack(mService, stackId, controller); + final TaskStack stack = new TaskStack(mWmService, stackId, controller); mTaskStackContainers.addStackToDisplay(stack, onTop); return stack; } @@ -2361,7 +2362,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override void switchUser() { super.switchUser(); - mService.mWindowsChanged = true; + mWmService.mWindowsChanged = true; } private void resetAnimationBackgroundAnimator() { @@ -2389,15 +2390,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mUnknownAppVisibilityController.clear(); mAppTransition.removeAppTransitionTimeoutCallbacks(); handleAnimatingStoppedAndTransition(); - mService.stopFreezingDisplayLocked(); + mWmService.stopFreezingDisplayLocked(); super.removeImmediately(); if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this); if (mPointerEventDispatcher != null && mTapDetector != null) { unregisterPointerEventListener(mTapDetector); - unregisterPointerEventListener(mService.mMousePositionTracker); + unregisterPointerEventListener(mWmService.mMousePositionTracker); mTapDetector = null; } - mService.mAnimator.removeDisplayLocked(mDisplayId); + mWmService.mAnimator.removeDisplayLocked(mDisplayId); mWindowingLayer.release(); mOverlayLayer.release(); } finally { @@ -2406,7 +2407,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } mDisplayPolicy.onDisplayRemoved(); - mService.mWindowPlacerLocked.requestTraversal(); + mWmService.mWindowPlacerLocked.requestTraversal(); } /** Returns true if a removal action is still being deferred. */ @@ -2483,7 +2484,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw() && !mDividerControllerLocked.isImeHideRequested(); final boolean dockVisible = isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - final TaskStack imeTargetStack = mService.getImeFocusStackLocked(); + final TaskStack imeTargetStack = mWmService.getImeFocusStackLocked(); final int imeDockSide = (dockVisible && imeTargetStack != null) ? imeTargetStack.getDockSide() : DOCKED_INVALID; final boolean imeOnTop = (imeDockSide == DOCKED_TOP); @@ -2610,7 +2611,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mDisplayInfo.writeToProto(proto, DISPLAY_INFO); proto.write(ROTATION, mRotation); final ScreenRotationAnimation screenRotationAnimation = - mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId); + mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId); if (screenRotationAnimation != null) { screenRotationAnimation.writeToProto(proto, SCREEN_ROTATION_ANIMATION); } @@ -2855,11 +2856,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } if (imWindowChanged) { - mService.mWindowsChanged = true; + mWmService.mWindowsChanged = true; setLayoutNeeded(); } - if (DEBUG_FOCUS_LIGHT || mService.localLOGV) Slog.v(TAG_WM, "Changing focus from " + if (DEBUG_FOCUS_LIGHT || mWmService.localLOGV) Slog.v(TAG_WM, "Changing focus from " + mCurrentFocus + " to " + newFocus + " displayId=" + getDisplayId() + " Callers=" + Debug.getCallers(4)); final WindowState oldFocus = mCurrentFocus; @@ -2898,7 +2899,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (mode == UPDATE_FOCUS_PLACING_SURFACES) { performLayout(true /*initial*/, updateInputWindows); } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) { - mService.mRoot.performSurfacePlacement(false); + mWmService.mRoot.performSurfacePlacement(false); } } @@ -2963,16 +2964,16 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // moving containers or resizing them. Need to investigate the best way to have it automatically // happen so we don't run into issues with programmers forgetting to do it. void layoutAndAssignWindowLayersIfNeeded() { - mService.mWindowsChanged = true; + mWmService.mWindowsChanged = true; setLayoutNeeded(); - if (!mService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, + if (!mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/)) { assignWindowLayers(false /* setLayoutNeeded */); } mInputMonitor.setUpdateInputWindowsNeededLw(); - mService.mWindowPlacerLocked.performSurfacePlacement(); + mWmService.mWindowPlacerLocked.performSurfacePlacement(); mInputMonitor.updateInputWindowsLw(false /*force*/); } @@ -2985,14 +2986,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (wsa.mSurfaceController == null) { return; } - if (!mService.mSessions.contains(wsa.mSession)) { + if (!mWmService.mSessions.contains(wsa.mSession)) { Slog.w(TAG_WM, "LEAKED SURFACE (session doesn't exist): " + w + " surface=" + wsa.mSurfaceController + " token=" + w.mToken + " pid=" + w.mSession.mPid + " uid=" + w.mSession.mUid); wsa.destroySurface(); - mService.mForceRemoves.add(w); + mWmService.mForceRemoves.add(w); mTmpWindow = w; } else if (w.mAppToken != null && w.mAppToken.isClientHidden()) { Slog.w(TAG_WM, "LEAKED SURFACE (app token hidden): " @@ -3016,7 +3017,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // Update display configuration for IME process. if (mInputMethodWindow != null) { final int imePid = mInputMethodWindow.mSession.mPid; - mService.mAtmInternal.onImeWindowSetOnDisplay(imePid, + mWmService.mAtmInternal.onImeWindowSetOnDisplay(imePid, mInputMethodWindow.getDisplayId()); } computeImeTarget(true /* updateImeTarget */); @@ -3237,7 +3238,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * Starts the Keyguard exit animation on all windows that don't belong to an app token. */ void startKeyguardExitOnNonAppWindows(boolean onWallpaper, boolean goingToShade) { - final WindowManagerPolicy policy = mService.mPolicy; + final WindowManagerPolicy policy = mWmService.mPolicy; forAllWindows(w -> { if (w.mAppToken == null && policy.canBeHiddenByKeyguardLw(w) && w.wouldBeVisibleIfPolicyIgnored() && !w.isVisible()) { @@ -3266,7 +3267,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } else if (w.mAttrs.type == TYPE_WALLPAPER) { mHaveWallpaper = true; } else if (w.mAttrs.type == TYPE_STATUS_BAR) { - mHaveKeyguard = mService.mPolicy.isKeyguardDrawnLw(); + mHaveKeyguard = mWmService.mPolicy.isKeyguardDrawnLw(); } } return false; @@ -3279,28 +3280,28 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // if the wallpaper service is disabled on the device, we're never going to have // wallpaper, don't bother waiting for it - boolean wallpaperEnabled = mService.mContext.getResources().getBoolean( + boolean wallpaperEnabled = mWmService.mContext.getResources().getBoolean( com.android.internal.R.bool.config_enableWallpaperService) - && mService.mContext.getResources().getBoolean( + && mWmService.mContext.getResources().getBoolean( com.android.internal.R.bool.config_checkWallpaperAtBoot) - && !mService.mOnlyCore; + && !mWmService.mOnlyCore; if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG_WM, - "******** booted=" + mService.mSystemBooted - + " msg=" + mService.mShowingBootMessages + "******** booted=" + mWmService.mSystemBooted + + " msg=" + mWmService.mShowingBootMessages + " haveBoot=" + mHaveBootMsg + " haveApp=" + mHaveApp + " haveWall=" + mHaveWallpaper + " wallEnabled=" + wallpaperEnabled + " haveKeyguard=" + mHaveKeyguard); // If we are turning on the screen to show the boot message, don't do it until the boot // message is actually displayed. - if (!mService.mSystemBooted && !mHaveBootMsg) { + if (!mWmService.mSystemBooted && !mHaveBootMsg) { return true; } // If we are turning on the screen after the boot is completed normally, don't do so until // we have the application and wallpaper. - if (mService.mSystemBooted + if (mWmService.mSystemBooted && ((!mHaveApp && !mHaveKeyguard) || (wallpaperEnabled && !mHaveWallpaper))) { return true; } @@ -3366,7 +3367,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mLastDispatchedSystemUiVisibility = visibility; if (isDefaultDisplay) { - mService.mInputManager.setSystemUiVisibility(visibility); + mWmService.mInputManager.setSystemUiVisibility(visibility); } updateSystemUiVisibility(visibility, globalDiff); return true; @@ -3395,13 +3396,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo void reevaluateStatusBarVisibility() { int visibility = getDisplayPolicy().adjustSystemUiVisibilityLw(mLastStatusBarVisibility); if (updateStatusBarVisibilityLocked(visibility)) { - mService.mWindowPlacerLocked.requestTraversal(); + mWmService.mWindowPlacerLocked.requestTraversal(); } } void onWindowFreezeTimeout() { Slog.w(TAG_WM, "Window freeze timeout expired."); - mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT; + mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT; forAllWindows(w -> { if (!w.getOrientationChanging()) { @@ -3409,28 +3410,28 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } w.orientationChangeTimedOut(); w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime() - - mService.mDisplayFreezeTime); + - mWmService.mDisplayFreezeTime); Slog.w(TAG_WM, "Force clearing orientation change: " + w); }, true /* traverseTopToBottom */); - mService.mWindowPlacerLocked.performSurfacePlacement(); + mWmService.mWindowPlacerLocked.performSurfacePlacement(); } void waitForAllWindowsDrawn() { - final WindowManagerPolicy policy = mService.mPolicy; + final WindowManagerPolicy policy = mWmService.mPolicy; forAllWindows(w -> { final boolean keyguard = policy.isKeyguardHostWindow(w.mAttrs); if (w.isVisibleLw() && (w.mAppToken != null || keyguard)) { w.mWinAnimator.mDrawState = DRAW_PENDING; // Force add to mResizingWindows. w.resetLastContentInsets(); - mService.mWaitingForDrawn.add(w); + mWmService.mWaitingForDrawn.add(w); } }, true /* traverseTopToBottom */); } // TODO: Super crazy long method that should be broken down... void applySurfaceChangesTransaction(boolean recoveringMemory) { - final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked; + final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked; mTmpUpdateAllDrawn.clear(); @@ -3487,7 +3488,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo prepareSurfaces(); mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent; - mService.mDisplayManagerInternal.setDisplayProperties(mDisplayId, + mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId, mLastHasContent, mTmpApplySurfaceChangesTransactionState.preferredRefreshRate, mTmpApplySurfaceChangesTransactionState.preferredModeId, @@ -3496,7 +3497,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible(); if (wallpaperVisible != mLastWallpaperVisible) { mLastWallpaperVisible = wallpaperVisible; - mService.mWallpaperVisibilityListeners.notifyWallpaperVisibilityChanged(this); + mWmService.mWallpaperVisibilityListeners.notifyWallpaperVisibilityChanged(this); } while (!mTmpUpdateAllDrawn.isEmpty()) { @@ -3594,7 +3595,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mInputMonitor.updateInputWindowsLw(false /*force*/); } - mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER); + mWmService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER); } /** @@ -3604,7 +3605,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * @param config of the output bitmap */ Bitmap screenshotDisplayLocked(Bitmap.Config config) { - if (!mService.mPolicy.isScreenOn()) { + if (!mWmService.mPolicy.isScreenOn()) { if (DEBUG_SCREENSHOT) { Slog.i(TAG_WM, "Attempted to take screenshot while display was off."); } @@ -3632,7 +3633,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo convertCropForSurfaceFlinger(frame, rot, dw, dh); final ScreenRotationAnimation screenRotationAnimation = - mService.mAnimator.getScreenRotationAnimationLocked(DEFAULT_DISPLAY); + mWmService.mAnimator.getScreenRotationAnimationLocked(DEFAULT_DISPLAY); final boolean inRotation = screenRotationAnimation != null && screenRotationAnimation.isAnimating(); if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG_WM, "Taking screenshot while rotating"); @@ -3688,11 +3689,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mTmpWindow = w; w.setDisplayLayoutNeeded(); w.finishSeamlessRotation(true /* timeout */); - mService.markForSeamlessRotation(w, false); + mWmService.markForSeamlessRotation(w, false); }, true /* traverseTopToBottom */); if (mTmpWindow != null) { - mService.mWindowPlacerLocked.performSurfacePlacement(); + mWmService.mWindowPlacerLocked.performSurfacePlacement(); } } @@ -3720,20 +3721,20 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override void onDescendantOverrideConfigurationChanged() { setLayoutNeeded(); - mService.requestTraversal(); + mWmService.requestTraversal(); } boolean okToDisplay() { if (mDisplayId == DEFAULT_DISPLAY) { - return !mService.mDisplayFrozen - && mService.mDisplayEnabled && mService.mPolicy.isScreenOn(); + return !mWmService.mDisplayFrozen + && mWmService.mDisplayEnabled && mWmService.mPolicy.isScreenOn(); } return mDisplayInfo.state == Display.STATE_ON; } boolean okToAnimate() { return okToDisplay() && - (mDisplayId != DEFAULT_DISPLAY || mService.mPolicy.okToAnimate()); + (mDisplayId != DEFAULT_DISPLAY || mWmService.mPolicy.okToAnimate()); } static final class TaskForResizePointSearchResult { @@ -3951,7 +3952,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } else if (stack == mSplitScreenPrimaryStack) { mSplitScreenPrimaryStack = null; // Re-set the split-screen create mode whenever the split-screen stack is removed. - mService.setDockedStackCreateStateLocked( + mWmService.setDockedStackCreateStateLocked( SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */); mDividerControllerLocked.notifyDockedStackExistsChanged(false); } @@ -4169,7 +4170,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } final int orientation = super.getOrientation(); - boolean isCar = mService.mContext.getPackageManager().hasSystemFeature( + boolean isCar = mWmService.mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_AUTOMOTIVE); if (isCar) { // In a car, you cannot physically rotate the screen, so it doesn't make sense to @@ -4350,10 +4351,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo wt.assignLayer(t, j); wt.assignChildLayers(t); - int layer = mService.mPolicy.getWindowLayerFromTypeLw( + int layer = mWmService.mPolicy.getWindowLayerFromTypeLw( wt.windowType, wt.mOwnerCanManageAppTokens); - if (needAssignIme && layer >= mService.mPolicy.getWindowLayerFromTypeLw( + if (needAssignIme && layer >= mWmService.mPolicy.getWindowLayerFromTypeLw( TYPE_INPUT_METHOD_DIALOG, true)) { imeContainer.assignRelativeLayer(t, wt.getSurfaceControl(), -1); needAssignIme = false; @@ -4376,9 +4377,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ private final Comparator<WindowToken> mWindowComparator = (token1, token2) -> // Tokens with higher base layer are z-ordered on-top. - mService.mPolicy.getWindowLayerFromTypeLw(token1.windowType, + mWmService.mPolicy.getWindowLayerFromTypeLw(token1.windowType, token1.mOwnerCanManageAppTokens) - < mService.mPolicy.getWindowLayerFromTypeLw(token2.windowType, + < mWmService.mPolicy.getWindowLayerFromTypeLw(token2.windowType, token2.mOwnerCanManageAppTokens) ? -1 : 1; private final Predicate<WindowState> mGetOrientingWindow = w -> { @@ -4408,7 +4409,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override int getOrientation() { - final WindowManagerPolicy policy = mService.mPolicy; + final WindowManagerPolicy policy = mWmService.mPolicy; // Find a window requesting orientation. final WindowState win = getWindow(mGetOrientingWindow); @@ -4416,7 +4417,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final int req = win.mAttrs.screenOrientation; if (policy.isKeyguardHostWindow(win.mAttrs)) { mLastKeyguardForcedOrientation = req; - if (mService.mKeyguardGoingAway) { + if (mWmService.mKeyguardGoingAway) { // Keyguard can't affect the orientation if it is going away... mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED; return SCREEN_ORIENTATION_UNSET; @@ -4474,7 +4475,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo }; SurfaceControl.Builder makeSurface(SurfaceSession s) { - return mService.makeSurfaceBuilder(s) + return mWmService.makeSurfaceBuilder(s) .setParent(mWindowingLayer); } @@ -4486,7 +4487,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override SurfaceControl.Builder makeChildSurface(WindowContainer child) { SurfaceSession s = child != null ? child.getSession() : getSession(); - final SurfaceControl.Builder b = mService.makeSurfaceBuilder(s); + final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(s); if (child == null) { return b; } @@ -4502,7 +4503,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * and other potpourii. */ SurfaceControl.Builder makeOverlay() { - return mService.makeSurfaceBuilder(mSession) + return mWmService.makeSurfaceBuilder(mSession) .setParent(mOverlayLayer); } @@ -4601,7 +4602,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override void prepareSurfaces() { final ScreenRotationAnimation screenRotationAnimation = - mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId); + mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId); if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) { screenRotationAnimation.getEnterTransformation().getMatrix().getValues(mTmpFloats); mPendingTransaction.setMatrix(mWindowingLayer, @@ -4689,7 +4690,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo + mDisplayId + " Callers=" + Debug.getCallers(5)); } mAppTransition.setReady(); - mService.mWindowPlacerLocked.requestTraversal(); + mWmService.mWindowPlacerLocked.requestTraversal(); } } @@ -4719,7 +4720,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mWallpaperMayChange = true; // Since the window list has been rebuilt, focus might have to be recomputed since the // actual order of windows might have changed again. - mService.mFocusMayChange = true; + mWmService.mFocusMayChange = true; pendingLayoutChanges |= changes; } @@ -4732,7 +4733,19 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mDisplay.supportsSystemDecorations() // TODO (b/111363427): Remove this and set the new FLAG_SHOULD_SHOW_LAUNCHER flag // (b/114338689) whenever vr 2d display id is set. - || mDisplayId == mService.mVr2dDisplayId - || mService.mForceDesktopModeOnExternalDisplays; + || mDisplayId == mWmService.mVr2dDisplayId + || mWmService.mForceDesktopModeOnExternalDisplays; + } + + /** + * Re-parent the DisplayContent's top surfaces, {@link #mWindowingLayer} and + * {@link #mOverlayLayer} to the specified surfaceControl. + * + * @param surfaceControlHandle The handle for the new SurfaceControl, where the DisplayContent's + * surfaces will be re-parented to. + */ + void reparentDisplayContent(IBinder surfaceControlHandle) { + mPendingTransaction.reparent(mWindowingLayer, surfaceControlHandle) + .reparent(mOverlayLayer, surfaceControlHandle); } } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 0e5947af0c61..f1d77b9961cf 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -45,7 +45,6 @@ import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR; @@ -2322,12 +2321,6 @@ public class DisplayPolicy { && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { mTopDockedOpaqueOrDimmingWindowState = win; } - - // Take note if a window wants to acquire a sleep token. - if ((attrs.privateFlags & PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN) != 0 - && win.canAcquireSleepToken()) { - mWindowSleepTokenNeeded = true; - } } /** 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/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java index 864f7e1aafae..55b3def7817b 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowController.java +++ b/services/core/java/com/android/server/wm/DisplayWindowController.java @@ -90,7 +90,7 @@ public class DisplayWindowController public void onOverrideConfigurationChanged(Configuration overrideConfiguration) { synchronized (mGlobalLock) { if (mContainer != null) { - mContainer.mService.setNewDisplayOverrideConfiguration(overrideConfiguration, + mContainer.mWmService.setNewDisplayOverrideConfiguration(overrideConfiguration, mContainer); } } 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/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 da9a5071b100..3062d34780b6 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java +++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java @@ -246,8 +246,8 @@ class LaunchParamsPersister { } void getLaunchParams(TaskRecord task, ActivityRecord activity, LaunchParams outParams) { - final ComponentName name = task != null ? task.realActivity : activity.realActivity; - final int userId = task != null ? task.userId : activity.userId; + final ComponentName name = task != null ? task.realActivity : activity.mActivityComponent; + final int userId = task != null ? task.userId : activity.mUserId; outParams.reset(); Map<ComponentName, PersistableLaunchParams> map = mMap.get(userId); @@ -269,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); @@ -310,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/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index 80dc2458d7f5..d2f2863b8fd1 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -225,7 +225,7 @@ public class LockTaskController { * of the last locked task and finishing it would mean that lock task mode is ended illegally. */ boolean activityBlockedFromFinish(ActivityRecord activity) { - final TaskRecord task = activity.getTask(); + final TaskRecord task = activity.getTaskRecord(); if (activity == task.getRootActivity() && activity == task.getTopActivity() && task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV @@ -647,7 +647,7 @@ public class LockTaskController { } final ActivityRecord r = mSupervisor.mRootActivityContainer.topRunningActivity(); - final TaskRecord task = (r != null) ? r.getTask() : null; + final TaskRecord task = (r != null) ? r.getTaskRecord() : null; if (mLockTaskModeTasks.isEmpty() && task!= null && task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) { // This task must have just been authorized. diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index 24c5228ce0ec..1e287b4e412e 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -148,8 +148,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // If there are multiple tasks in the target stack (ie. the home stack, with 3p // and default launchers coexisting), then move the task to the top as a part of // moving the stack to the front - if (targetStack.topTask() != targetActivity.getTask()) { - targetStack.addTask(targetActivity.getTask(), true /* toTop */, + if (targetStack.topTask() != targetActivity.getTaskRecord()) { + targetStack.addTask(targetActivity.getTaskRecord(), true /* toTop */, "startRecentsActivity"); } } else { diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index 4dd48c46b891..1d86b4684b32 100644 --- a/services/core/java/com/android/server/wm/RootActivityContainer.java +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -414,7 +414,7 @@ class RootActivityContainer extends ConfigurationContainer // Only resume home activity if isn't finishing. if (r != null && !r.finishing) { r.moveFocusableActivityToTop(myReason); - return resumeFocusedStacksTopActivities(r.getStack(), prev, null); + return resumeFocusedStacksTopActivities(r.getActivityStack(), prev, null); } return startHomeOnDisplay(mCurrentUser, myReason, displayId); } @@ -843,7 +843,7 @@ class RootActivityContainer extends ConfigurationContainer mWindowManager.deferSurfaceLayout(); - final ActivityDisplay display = r.getStack().getDisplay(); + final ActivityDisplay display = r.getActivityStack().getDisplay(); PinnedActivityStack stack = display.getPinnedStack(); // This will clear the pinned stack by moving an existing task to the full screen stack, @@ -860,7 +860,7 @@ class RootActivityContainer extends ConfigurationContainer final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio); try { - final TaskRecord task = r.getTask(); + final TaskRecord task = r.getTaskRecord(); // 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. @@ -882,7 +882,7 @@ class RootActivityContainer extends ConfigurationContainer // 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, + mStackSupervisor.getNextTaskIdForUserLocked(r.mUserId), r.info, r.intent, null, null, true); r.reparent(newTask, MAX_VALUE, "moveActivityToStack"); @@ -1538,7 +1538,7 @@ class RootActivityContainer extends ConfigurationContainer stack = candidateTask.getStack(); } if (stack == null && r != null) { - stack = r.getStack(); + stack = r.getActivityStack(); } if (stack != null) { display = stack.getDisplay(); @@ -1604,7 +1604,7 @@ class RootActivityContainer extends ConfigurationContainer // 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) { + if (r.getDisplayId() == displayId && r.getTaskRecord() == candidateTask) { return candidateTask.getStack(); } @@ -1950,8 +1950,8 @@ class RootActivityContainer extends ConfigurationContainer final ActivityRecord activityRecord = task.getTopActivity(); final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null); - return (activityRecord != null && activityRecord.userId == userId) - || (resultTo != null && resultTo.userId == userId); + return (activityRecord != null && activityRecord.mUserId == userId) + || (resultTo != null && resultTo.mUserId == userId); } void cancelInitializingActivities() { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 80d1368427a3..b98d15479cf5 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -182,7 +182,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mTopFocusedDisplayId != topFocusedDisplayId && mode == UPDATE_FOCUS_NORMAL; if (mTopFocusedDisplayId != topFocusedDisplayId) { mTopFocusedDisplayId = topFocusedDisplayId; - mService.mInputManager.setFocusedDisplay(mTopFocusedDisplayId); + mWmService.mInputManager.setFocusedDisplay(mTopFocusedDisplayId); if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "New topFocusedDisplayId=" + mTopFocusedDisplayId); } @@ -194,10 +194,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final boolean isTopFocusedDisplay = topFocusedDisplayChanged && dc.getDisplayId() == mTopFocusedDisplayId; if (windowFocusChanged || isTopFocusedDisplay) { - final Message msg = mService.mH.obtainMessage( + final Message msg = mWmService.mH.obtainMessage( WindowManagerService.H.REPORT_FOCUS_CHANGE, dc); msg.arg1 = topFocusedDisplayChanged ? 1 : 0; - mService.mH.sendMessage(msg); + mWmService.mH.sendMessage(msg); } }); @@ -211,7 +211,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> @Override void onChildPositionChanged() { - mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */); + mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */); } DisplayContent getDisplayContent(int displayId) { @@ -238,20 +238,20 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return existing; } - final DisplayContent dc = new DisplayContent(display, mService, controller); + final DisplayContent dc = new DisplayContent(display, mWmService, controller); if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display); - mService.mDisplayWindowSettings.applySettingsToDisplayLocked(dc); + mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(dc); initializeDisplayOverrideConfiguration(controller, dc); - if (mService.mDisplayManagerInternal != null) { - mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager( + if (mWmService.mDisplayManagerInternal != null) { + mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager( displayId, dc.getDisplayInfo()); dc.configureDisplayPolicy(); } - mService.reconfigureDisplayLocked(dc); + mWmService.reconfigureDisplayLocked(dc); return dc; } @@ -436,11 +436,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent> void removeReplacedWindows() { if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION removeReplacedWindows"); - mService.openSurfaceTransaction(); + mWmService.openSurfaceTransaction(); try { forAllWindows(sRemoveReplacedWindowsConsumer, true /* traverseTopToBottom */); } finally { - mService.closeSurfaceTransaction("removeReplacedWindows"); + mWmService.closeSurfaceTransaction("removeReplacedWindows"); if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION removeReplacedWindows"); } } @@ -488,7 +488,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final SparseIntArray pidCandidates = new SparseIntArray(); for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { mChildren.get(displayNdx).forAllWindows((w) -> { - if (mService.mForceRemoves.contains(w)) { + if (mWmService.mForceRemoves.contains(w)) { return; } final WindowStateAnimator wsa = w.mWinAnimator; @@ -503,7 +503,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> pids[i] = pidCandidates.keyAt(i); } try { - if (mService.mActivityManager.killPids(pids, "Free memory", secure)) { + if (mWmService.mActivityManager.killPids(pids, "Free memory", secure)) { killedApps = true; } } catch (RemoteException e) { @@ -547,9 +547,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> int i; boolean updateInputWindowsNeeded = false; - if (mService.mFocusMayChange) { - mService.mFocusMayChange = false; - updateInputWindowsNeeded = mService.updateFocusedWindowLocked( + if (mWmService.mFocusMayChange) { + mWmService.mFocusMayChange = false; + updateInputWindowsNeeded = mWmService.updateFocusedWindowLocked( UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/); } @@ -565,31 +565,31 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mUserActivityTimeout = -1; mObscureApplicationContentOnSecondaryDisplays = false; mSustainedPerformanceModeCurrent = false; - mService.mTransactionSequence++; + mWmService.mTransactionSequence++; // TODO(multi-display): recents animation & wallpaper need support multi-display. - final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked(); - final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked; + final DisplayContent defaultDisplay = mWmService.getDefaultDisplayContentLocked(); + final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked; if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces"); - mService.openSurfaceTransaction(); + mWmService.openSurfaceTransaction(); try { applySurfaceChangesTransaction(recoveringMemory); } catch (RuntimeException e) { Slog.wtf(TAG, "Unhandled exception in Window Manager", e); } finally { - mService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces"); + mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces"); if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces"); } - mService.mAnimator.executeAfterPrepareSurfacesRunnables(); + mWmService.mAnimator.executeAfterPrepareSurfacesRunnables(); checkAppTransitionReady(surfacePlacer); // Defer starting the recents animation until the wallpaper has drawn final RecentsAnimationController recentsAnimationController = - mService.getRecentsAnimationController(); + mWmService.getRecentsAnimationController(); if (recentsAnimationController != null) { recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController); } @@ -606,9 +606,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } - if (mService.mFocusMayChange) { - mService.mFocusMayChange = false; - if (mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, + if (mWmService.mFocusMayChange) { + mWmService.mFocusMayChange = false; + if (mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, false /*updateInputWindows*/)) { updateInputWindowsNeeded = true; } @@ -622,23 +622,23 @@ class RootWindowContainer extends WindowContainer<DisplayContent> handleResizingWindows(); - if (DEBUG_ORIENTATION && mService.mDisplayFrozen) Slog.v(TAG, + if (DEBUG_ORIENTATION && mWmService.mDisplayFrozen) Slog.v(TAG, "With display frozen, orientationChangeComplete=" + mOrientationChangeComplete); if (mOrientationChangeComplete) { - if (mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) { - mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE; - mService.mLastFinishedFreezeSource = mLastWindowFreezeSource; - mService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT); + if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) { + mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE; + mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource; + mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT); } - mService.stopFreezingDisplayLocked(); + mWmService.stopFreezingDisplayLocked(); } // Destroy the surface of any windows that are no longer visible. - i = mService.mDestroySurface.size(); + i = mWmService.mDestroySurface.size(); if (i > 0) { do { i--; - WindowState win = mService.mDestroySurface.get(i); + WindowState win = mWmService.mDestroySurface.get(i); win.mDestroying = false; final DisplayContent displayContent = win.getDisplayContent(); if (displayContent.mInputMethodWindow == win) { @@ -650,7 +650,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> win.destroySurfaceUnchecked(); win.mWinAnimator.destroyPreservedSurfaceLocked(); } while (i > 0); - mService.mDestroySurface.clear(); + mWmService.mDestroySurface.clear(); } // Time to remove any exiting tokens? @@ -671,8 +671,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> dc.getInputMonitor().updateInputWindowsLw(true /*force*/); }); - mService.setHoldScreenLocked(mHoldScreen); - if (!mService.mDisplayFrozen) { + mWmService.setHoldScreenLocked(mHoldScreen); + if (!mWmService.mDisplayFrozen) { final int brightness = mScreenBrightness < 0 || mScreenBrightness > 1.0f ? -1 : toBrightnessOverride(mScreenBrightness); @@ -684,7 +684,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> if (mSustainedPerformanceModeCurrent != mSustainedPerformanceModeEnabled) { mSustainedPerformanceModeEnabled = mSustainedPerformanceModeCurrent; - mService.mPowerManagerInternal.powerHint( + mWmService.mPowerManagerInternal.powerHint( PowerHint.SUSTAINED_PERFORMANCE, (mSustainedPerformanceModeEnabled ? 1 : 0)); } @@ -694,22 +694,22 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mUpdateRotation = updateRotationUnchecked(); } - if (mService.mWaitingForDrawnCallback != null + if (mWmService.mWaitingForDrawnCallback != null || (mOrientationChangeComplete && !isLayoutNeeded() && !mUpdateRotation)) { - mService.checkDrawnWindowsLocked(); + mWmService.checkDrawnWindowsLocked(); } - final int N = mService.mPendingRemove.size(); + final int N = mWmService.mPendingRemove.size(); if (N > 0) { - if (mService.mPendingRemoveTmp.length < N) { - mService.mPendingRemoveTmp = new WindowState[N + 10]; + if (mWmService.mPendingRemoveTmp.length < N) { + mWmService.mPendingRemoveTmp = new WindowState[N + 10]; } - mService.mPendingRemove.toArray(mService.mPendingRemoveTmp); - mService.mPendingRemove.clear(); + mWmService.mPendingRemove.toArray(mWmService.mPendingRemoveTmp); + mWmService.mPendingRemove.clear(); ArrayList<DisplayContent> displayList = new ArrayList(); for (i = 0; i < N; i++) { - final WindowState w = mService.mPendingRemoveTmp[i]; + final WindowState w = mWmService.mPendingRemoveTmp[i]; w.removeImmediately(); final DisplayContent displayContent = w.getDisplayContent(); if (displayContent != null && !displayList.contains(displayContent)) { @@ -737,12 +737,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Check to see if we are now in a state where the screen should // be enabled, because the window obscured flags have changed. - mService.enableScreenIfNeededLocked(); + mWmService.enableScreenIfNeededLocked(); - mService.scheduleAnimationLocked(); + mWmService.scheduleAnimationLocked(); if (DEBUG_WINDOW_TRACE) Slog.e(TAG, - "performSurfacePlacementInner exit: animating=" + mService.mAnimator.isAnimating()); + "performSurfacePlacementInner exit: animating=" + + mWmService.mAnimator.isAnimating()); } private void checkAppTransitionReady(WindowSurfacePlacer surfacePlacer) { @@ -782,23 +783,23 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mObscuringWindow = null; // TODO(multi-display): Support these features on secondary screens. - final DisplayContent defaultDc = mService.getDefaultDisplayContentLocked(); + final DisplayContent defaultDc = mWmService.getDefaultDisplayContentLocked(); final DisplayInfo defaultInfo = defaultDc.getDisplayInfo(); final int defaultDw = defaultInfo.logicalWidth; final int defaultDh = defaultInfo.logicalHeight; - if (mService.mWatermark != null) { - mService.mWatermark.positionSurface(defaultDw, defaultDh); + if (mWmService.mWatermark != null) { + mWmService.mWatermark.positionSurface(defaultDw, defaultDh); } - if (mService.mStrictModeFlash != null) { - mService.mStrictModeFlash.positionSurface(defaultDw, defaultDh); + if (mWmService.mStrictModeFlash != null) { + mWmService.mStrictModeFlash.positionSurface(defaultDw, defaultDh); } - if (mService.mCircularDisplayMask != null) { - mService.mCircularDisplayMask.positionSurface(defaultDw, defaultDh, - mService.getDefaultDisplayRotation()); + if (mWmService.mCircularDisplayMask != null) { + mWmService.mCircularDisplayMask.positionSurface(defaultDw, defaultDh, + mWmService.getDefaultDisplayRotation()); } - if (mService.mEmulatorDisplayOverlay != null) { - mService.mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh, - mService.getDefaultDisplayRotation()); + if (mWmService.mEmulatorDisplayOverlay != null) { + mWmService.mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh, + mWmService.getDefaultDisplayRotation()); } final int count = mChildren.size(); @@ -809,7 +810,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Give the display manager a chance to adjust properties like display rotation if it needs // to. - mService.mDisplayManagerInternal.performTraversal(mDisplayTransaction); + mWmService.mDisplayManagerInternal.performTraversal(mDisplayTransaction); SurfaceControl.mergeToGlobalTransaction(mDisplayTransaction); } @@ -817,14 +818,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent> * Handles resizing windows during surface placement. */ private void handleResizingWindows() { - for (int i = mService.mResizingWindows.size() - 1; i >= 0; i--) { - WindowState win = mService.mResizingWindows.get(i); + for (int i = mWmService.mResizingWindows.size() - 1; i >= 0; i--) { + WindowState win = mWmService.mResizingWindows.get(i); if (win.mAppFreezing) { // Don't remove this window until rotation has completed. continue; } win.reportResized(); - mService.mResizingWindows.remove(i); + mWmService.mResizingWindows.remove(i); } } @@ -862,7 +863,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> if ((attrFlags & FLAG_KEEP_SCREEN_ON) != 0) { mHoldScreen = w.mSession; mHoldScreenWindow = w; - } else if (DEBUG_KEEP_SCREEN_ON && w == mService.mLastWakeLockHoldingWindow) { + } else if (DEBUG_KEEP_SCREEN_ON && w == mWmService.mLastWakeLockHoldingWindow) { Slog.d(TAG_KEEP_SCREEN_ON, "handleNotObscuredLocked: " + w + " was holding " + "screen wakelock but no longer has FLAG_KEEP_SCREEN_ON!!! called by" + Debug.getCallers(10)); @@ -911,7 +912,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> boolean copyAnimToLayoutParams() { boolean doRequest = false; - final int bulkUpdateParams = mService.mAnimator.mBulkUpdateParams; + final int bulkUpdateParams = mWmService.mAnimator.mBulkUpdateParams; if ((bulkUpdateParams & SET_UPDATE_ROTATION) != 0) { mUpdateRotation = true; doRequest = true; @@ -920,8 +921,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mOrientationChangeComplete = false; } else { mOrientationChangeComplete = true; - mLastWindowFreezeSource = mService.mAnimator.mLastWindowFreezeSource; - if (mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) { + mLastWindowFreezeSource = mWmService.mAnimator.mLastWindowFreezeSource; + if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) { doRequest = true; } } @@ -947,12 +948,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent> public void handleMessage(Message msg) { switch (msg.what) { case SET_SCREEN_BRIGHTNESS_OVERRIDE: - mService.mPowerManagerInternal.setScreenBrightnessOverrideFromWindowManager( + mWmService.mPowerManagerInternal.setScreenBrightnessOverrideFromWindowManager( msg.arg1); break; case SET_USER_ACTIVITY_TIMEOUT: - mService.mPowerManagerInternal.setUserActivityTimeoutOverrideFromWindowManager( - (Long) msg.obj); + mWmService.mPowerManagerInternal. + setUserActivityTimeoutOverrideFromWindowManager((Long) msg.obj); break; default: break; @@ -962,7 +963,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> void dumpDisplayContents(PrintWriter pw) { pw.println("WINDOW MANAGER DISPLAY CONTENTS (dumpsys window displays)"); - if (mService.mDisplayReady) { + if (mWmService.mDisplayReady) { final int count = mChildren.size(); for (int i = 0; i < count; ++i) { final DisplayContent displayContent = mChildren.get(i); @@ -1015,7 +1016,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> public void writeToProto(ProtoOutputStream proto, long fieldId, boolean trim) { final long token = proto.start(fieldId); super.writeToProto(proto, WINDOW_CONTAINER, trim); - if (mService.mDisplayReady) { + if (mWmService.mDisplayReady) { final int count = mChildren.size(); for (int i = 0; i < count; ++i) { final DisplayContent displayContent = mChildren.get(i); @@ -1050,7 +1051,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> @Override void scheduleAnimation() { - mService.scheduleAnimationLocked(); + mWmService.scheduleAnimationLocked(); } /** diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index ee0e89b24769..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; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 6904ef58dc24..d3db5dfa5e0f 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -332,7 +332,7 @@ class Task extends WindowContainer<AppWindowToken> { boolean isResizeable() { return ActivityInfo.isResizeableMode(mResizeMode) || mSupportsPictureInPicture - || mService.mForceResizableTasks; + || mWmService.mForceResizableTasks; } /** @@ -581,7 +581,7 @@ class Task extends WindowContainer<AppWindowToken> { } boolean isTaskAnimating() { - final RecentsAnimationController recentsAnim = mService.getRecentsAnimationController(); + final RecentsAnimationController recentsAnim = mWmService.getRecentsAnimationController(); if (recentsAnim != null) { if (recentsAnim.isAnimatingTask(this)) { return true; @@ -628,13 +628,13 @@ class Task extends WindowContainer<AppWindowToken> { } void forceWindowsScaleable(boolean force) { - mService.openSurfaceTransaction(); + mWmService.openSurfaceTransaction(); try { for (int i = mChildren.size() - 1; i >= 0; i--) { mChildren.get(i).forceWindowsScaleableInTransaction(force); } } finally { - mService.closeSurfaceTransaction("forceWindowsScaleable"); + mWmService.closeSurfaceTransaction("forceWindowsScaleable"); } } diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index 3b3feacc5cda..bb3df02146d7 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -283,8 +283,8 @@ class TaskChangeNotificationController { void notifyActivityPinned(ActivityRecord r) { mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG); final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG, - r.getTask().taskId, r.getStackId(), r.packageName); - msg.sendingUid = r.userId; + r.getTaskRecord().taskId, r.getStackId(), r.packageName); + msg.sendingUid = r.mUserId; forAllLocalListeners(mNotifyActivityPinned, msg); msg.sendToTarget(); } diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index 8a3dbada8d3f..30751bcebe07 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -903,7 +903,7 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont // Correct the activity intent for aliasing. The task record intent will always be based on // the real activity that will be launched not the alias, so we need to use an intent with // the component name pointing to the real activity not the alias in the activity record. - intent.setComponent(r.realActivity); + intent.setComponent(r.mActivityComponent); return intent.filterEquals(this.intent); } @@ -1211,7 +1211,7 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont * be in the current task or unparented to any task. */ void addActivityAtIndex(int index, ActivityRecord r) { - TaskRecord task = r.getTask(); + TaskRecord task = r.getTaskRecord(); if (task != null && task != this) { throw new IllegalArgumentException("Can not add r=" + " to task=" + this + " current parent=" + task); @@ -1285,7 +1285,7 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont } boolean removeActivity(ActivityRecord r, boolean reparenting) { - if (r.getTask() != this) { + if (r.getTaskRecord() != this) { throw new IllegalArgumentException( "Activity=" + r + " does not belong to task=" + this); } @@ -1401,7 +1401,7 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont if (r.finishing) { continue; } - if (r.realActivity.equals(newR.realActivity)) { + if (r.mActivityComponent.equals(newR.mActivityComponent)) { // Here it is! Now finish everything in front... final ActivityRecord ret = r; @@ -1559,13 +1559,13 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont * the index within the history at which it's found, or < 0 if not found. */ final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) { - final ComponentName realActivity = r.realActivity; + final ComponentName realActivity = r.mActivityComponent; for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) { ActivityRecord candidate = mActivities.get(activityNdx); if (candidate.finishing) { continue; } - if (candidate.realActivity.equals(realActivity)) { + if (candidate.mActivityComponent.equals(realActivity)) { return candidate; } } @@ -1990,7 +1990,7 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont ? reuseActivitiesReport.base.intent.getComponent() : null; info.topActivity = reuseActivitiesReport.top != null - ? reuseActivitiesReport.top.realActivity + ? reuseActivitiesReport.top.mActivityComponent : null; info.origActivity = origActivity; info.realActivity = realActivity; @@ -2041,7 +2041,7 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont pw.println(origActivity.flattenToShortString()); } if (realActivity != null) { - pw.print(prefix); pw.print("realActivity="); + pw.print(prefix); pw.print("mActivityComponent="); pw.println(realActivity.flattenToShortString()); } if (autoRemoveRecents || isPersistable || !isActivityTypeStandard() || numFullscreen != 0) { diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 5deb4f1ab7d4..b1f95f894490 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -450,7 +450,7 @@ public class TaskStack extends WindowContainer<Task> implements final int newDockSide = getDockSide(parentConfig, inOutBounds); // Update the dock create mode and clear the dock create bounds, these // might change after a rotation and the original values will be invalid. - mService.setDockedStackCreateStateLocked( + mWmService.setDockedStackCreateStateLocked( (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP) ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, @@ -523,7 +523,7 @@ public class TaskStack extends WindowContainer<Task> implements mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, displayWidth, displayHeight, displayCutout, outBounds); final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm( - mService.mContext.getResources(), displayWidth, displayHeight, + mWmService.mContext.getResources(), displayWidth, displayHeight, dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds, getDockSide(), isMinimizedDockAndHomeStackResizable()); final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition); @@ -594,7 +594,7 @@ public class TaskStack extends WindowContainer<Task> implements private int findPositionForTask(Task task, int targetPosition, boolean showForAllUsers, boolean addingNew) { final boolean canShowTask = - showForAllUsers || mService.isCurrentProfileLocked(task.mUserId); + showForAllUsers || mWmService.isCurrentProfileLocked(task.mUserId); final int stackSize = mChildren.size(); int minPosition = 0; @@ -628,7 +628,7 @@ public class TaskStack extends WindowContainer<Task> implements final Task tmpTask = mChildren.get(minPosition); final boolean canShowTmpTask = tmpTask.showForAllUsers() - || mService.isCurrentProfileLocked(tmpTask.mUserId); + || mWmService.isCurrentProfileLocked(tmpTask.mUserId); if (canShowTmpTask) { break; } @@ -647,7 +647,7 @@ public class TaskStack extends WindowContainer<Task> implements final Task tmpTask = mChildren.get(maxPosition); final boolean canShowTmpTask = tmpTask.showForAllUsers() - || mService.isCurrentProfileLocked(tmpTask.mUserId); + || mWmService.isCurrentProfileLocked(tmpTask.mUserId); if (!canShowTmpTask) { break; } @@ -728,7 +728,7 @@ public class TaskStack extends WindowContainer<Task> implements // We multiply by two to match the client logic for converting view elevation // to insets, as in {@link WindowManager.LayoutParams#setSurfaceInsets} - return (int)Math.ceil(mService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP, + return (int)Math.ceil(mWmService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP, displayMetrics) * 2); } return 0; @@ -791,7 +791,7 @@ public class TaskStack extends WindowContainer<Task> implements if (dockedBounds == null || dockedBounds.isEmpty()) { // Calculate the primary docked bounds. - final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode + final boolean dockedOnTopOrLeft = mWmService.mDockedStackCreateMode == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; getStackDockedModeBounds(parentConfig, true /* primary */, outStackBounds, dockedBounds, @@ -856,8 +856,8 @@ public class TaskStack extends WindowContainer<Task> implements outBounds.set(displayRect); if (primary) { - if (mService.mDockedStackCreateBounds != null) { - outBounds.set(mService.mDockedStackCreateBounds); + if (mWmService.mDockedStackCreateBounds != null) { + outBounds.set(mWmService.mDockedStackCreateBounds); return; } @@ -868,7 +868,7 @@ public class TaskStack extends WindowContainer<Task> implements mDisplayContent.getDisplayPolicy().getStableInsetsLw( parentConfig.windowConfiguration.getRotation(), displayRect.width(), displayRect.height(), displayCutout, mTmpRect2); - final int position = new DividerSnapAlgorithm(mService.mContext.getResources(), + final int position = new DividerSnapAlgorithm(mWmService.mContext.getResources(), displayRect.width(), displayRect.height(), dockDividerWidth, @@ -913,7 +913,7 @@ public class TaskStack extends WindowContainer<Task> implements throw new IllegalStateException("Not a docked stack=" + this); } - mService.mDockedStackCreateBounds = null; + mWmService.mDockedStackCreateBounds = null; final Rect bounds = new Rect(); final Rect tempBounds = new Rect(); @@ -955,7 +955,7 @@ public class TaskStack extends WindowContainer<Task> implements } mDisplayContent = null; - mService.mWindowPlacerLocked.requestTraversal(); + mWmService.mWindowPlacerLocked.requestTraversal(); } void resetAnimationBackgroundAnimator() { @@ -977,7 +977,7 @@ public class TaskStack extends WindowContainer<Task> implements int top = mChildren.size(); for (int taskNdx = 0; taskNdx < top; ++taskNdx) { Task task = mChildren.get(taskNdx); - if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) { + if (mWmService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) { mChildren.remove(taskNdx); mChildren.add(task); --top; @@ -1046,7 +1046,7 @@ public class TaskStack extends WindowContainer<Task> implements } mAdjustedForIme = false; updateAdjustedBounds(); - mService.setResizeDimLayer(false, getWindowingMode(), 1.0f); + mWmService.setResizeDimLayer(false, getWindowingMode(), 1.0f); } else { mImeGoingAway |= mAdjustedForIme; } @@ -1179,7 +1179,7 @@ public class TaskStack extends WindowContainer<Task> implements } if (dockSide == DOCKED_TOP) { - mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect); + mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect); int topInset = mTmpRect.top; mTmpAdjustedBounds.set(getRawBounds()); mTmpAdjustedBounds.bottom = (int) (minimizeAmount * topInset + (1 - minimizeAmount) @@ -1215,7 +1215,7 @@ public class TaskStack extends WindowContainer<Task> implements } if (dockSide == DOCKED_TOP) { - mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect); + mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect); int topInset = mTmpRect.top; return getRawBounds().bottom - topInset; } else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) { @@ -1240,11 +1240,11 @@ public class TaskStack extends WindowContainer<Task> implements } setAdjustedBounds(mTmpAdjustedBounds); - final boolean isImeTarget = (mService.getImeFocusStackLocked() == this); + final boolean isImeTarget = (mWmService.getImeFocusStackLocked() == this); if (mAdjustedForIme && adjust && !isImeTarget) { final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount) * IME_ADJUST_DIM_AMOUNT; - mService.setResizeDimLayer(true, getWindowingMode(), alpha); + mWmService.setResizeDimLayer(true, getWindowingMode(), alpha); } } @@ -1510,14 +1510,14 @@ public class TaskStack extends WindowContainer<Task> implements public boolean setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds) { // Hold the lock since this is called from the BoundsAnimator running on the UiThread - synchronized (mService.mGlobalLock) { + synchronized (mWmService.mGlobalLock) { if (mCancelCurrentBoundsAnimation) { return false; } } try { - mService.mActivityTaskManager.resizePinnedStack(stackBounds, tempTaskBounds); + mWmService.mActivityTaskManager.resizePinnedStack(stackBounds, tempTaskBounds); } catch (RemoteException e) { // I don't believe you. } @@ -1535,7 +1535,7 @@ public class TaskStack extends WindowContainer<Task> implements @Override // AnimatesBounds public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) { // Hold the lock since this is called from the BoundsAnimator running on the UiThread - synchronized (mService.mGlobalLock) { + synchronized (mWmService.mGlobalLock) { if (!isAttached()) { // Don't run the animation if the stack is already detached return false; @@ -1555,7 +1555,7 @@ public class TaskStack extends WindowContainer<Task> implements if (inPinnedWindowingMode()) { try { - mService.mActivityTaskManager.notifyPinnedStackAnimationStarted(); + mWmService.mActivityTaskManager.notifyPinnedStackAnimationStarted(); } catch (RemoteException e) { // I don't believe you... } @@ -1598,9 +1598,9 @@ public class TaskStack extends WindowContainer<Task> implements } try { - mService.mActivityTaskManager.notifyPinnedStackAnimationEnded(); + mWmService.mActivityTaskManager.notifyPinnedStackAnimationEnded(); if (moveToFullscreen) { - mService.mActivityTaskManager.moveTasksToFullscreenStack(mStackId, + mWmService.mActivityTaskManager.moveTasksToFullscreenStack(mStackId, true /* onTop */); } } catch (RemoteException e) { @@ -1614,7 +1614,7 @@ public class TaskStack extends WindowContainer<Task> implements @Override public boolean isAttached() { - synchronized (mService.mGlobalLock) { + synchronized (mWmService.mGlobalLock) { return mDisplayContent != null; } } @@ -1623,19 +1623,19 @@ public class TaskStack extends WindowContainer<Task> implements * Called immediately prior to resizing the tasks at the end of the pinned stack animation. */ public void onPipAnimationEndResize() { - synchronized (mService.mGlobalLock) { + synchronized (mWmService.mGlobalLock) { mBoundsAnimating = false; for (int i = 0; i < mChildren.size(); i++) { final Task t = mChildren.get(i); t.clearPreserveNonFloatingState(); } - mService.requestTraversal(); + mWmService.requestTraversal(); } } @Override public boolean shouldDeferStartOnMoveToFullscreen() { - synchronized (mService.mGlobalLock) { + synchronized (mWmService.mGlobalLock) { if (!isAttached()) { // Unnecessary to pause the animation because the stack is detached. return false; diff --git a/services/core/java/com/android/server/wm/VrController.java b/services/core/java/com/android/server/wm/VrController.java index abe40a731a43..3e136d355f9d 100644 --- a/services/core/java/com/android/server/wm/VrController.java +++ b/services/core/java/com/android/server/wm/VrController.java @@ -187,7 +187,7 @@ final class VrController { synchronized (mGlobalAmLock) { vrMode = record.requestedVrComponent != null; requestedPackage = record.requestedVrComponent; - userId = record.userId; + userId = record.mUserId; callingPackage = record.info.getComponentName(); // Tell the VrController that a VR mode change is requested. diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 7e4c62935e05..1275ca057e65 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -115,7 +115,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< */ protected final Transaction mPendingTransaction; protected final SurfaceAnimator mSurfaceAnimator; - protected final WindowManagerService mService; + protected final WindowManagerService mWmService; private final Point mTmpPos = new Point(); protected final Point mLastSurfacePosition = new Point(); @@ -129,10 +129,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< */ private boolean mCommittedReparentToAnimationLeash; - WindowContainer(WindowManagerService service) { - mService = service; - mPendingTransaction = service.mTransactionFactory.make(); - mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, service); + WindowContainer(WindowManagerService wms) { + mWmService = wms; + mPendingTransaction = wms.mTransactionFactory.make(); + mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms); } @Override diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index b91afcd18e45..52b24b3e7307 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -66,6 +66,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY; import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID; import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN; import static com.android.server.LockGuard.INDEX_WINDOW; @@ -208,6 +209,7 @@ import android.view.IWindowSessionCallback; import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEventReceiver; +import android.view.InsetsState; import android.view.KeyEvent; import android.view.MagnificationSpec; import android.view.MotionEvent; @@ -218,7 +220,6 @@ 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; @@ -3591,6 +3592,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); @@ -5397,7 +5409,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); @@ -5629,22 +5641,12 @@ public class WindowManagerService extends IWindowManager.Stub } } - // TODO: Make the callers use getNavBarPosition(int) only. - /** - * Used by SystemUI and shared SystemUI libraries. - * @see DisplayPolicy#getNavBarPosition() - */ - @Override - @WindowManagerPolicy.NavigationBarPosition - public int getNavBarPosition() { - return getNavBarPosition(Display.DEFAULT_DISPLAY); - } - /** * Used by ActivityManager to determine where to position an app with aspect ratio shorter then * the screen is. * @see DisplayPolicy#getNavBarPosition() */ + @Override @WindowManagerPolicy.NavigationBarPosition public int getNavBarPosition(int displayId) { synchronized (mGlobalLock) { @@ -5654,7 +5656,7 @@ public class WindowManagerService extends IWindowManager.Stub if (displayContent == null) { Slog.w(TAG, "getNavBarPosition with invalid displayId=" + displayId + " callers=" + Debug.getCallers(3)); - return -1; + return NAV_BAR_INVALID; } displayContent.performLayout(false /* initial */, false /* updateInputWindows */); @@ -7446,4 +7448,29 @@ public class WindowManagerService extends IWindowManager.Stub } } } + + @Override + public void reparentDisplayContent(int displayId, IBinder surfaceControlHandle) { + final Display display = mDisplayManager.getDisplay(displayId); + if (display == null) { + throw new IllegalArgumentException( + "Can't reparent display for non-existent displayId: " + displayId); + } + + final int callingUid = Binder.getCallingUid(); + final int displayOwnerUid = display.getOwnerUid(); + if (callingUid != displayOwnerUid) { + throw new SecurityException("Only owner of the display can reparent surfaces to it."); + } + + synchronized (mGlobalLock) { + long token = Binder.clearCallingIdentity(); + try { + DisplayContent displayContent = getDisplayContentOrCreate(displayId, null); + displayContent.reparentDisplayContent(surfaceControlHandle); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } } 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 578af2eebe88..2dec4dd13cbe 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -443,7 +443,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio for (int i = 0; i < activities.size(); i++) { final ActivityRecord r = activities.get(i); if (!r.finishing && r.isInStackLocked()) { - r.getStack().finishActivityLocked(r, Activity.RESULT_CANCELED, + r.getActivityStack().finishActivityLocked(r, Activity.RESULT_CANCELED, null, "finish-heavy", true); } } @@ -513,7 +513,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } ActivityRecord hist = mActivities.get(0); intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP, hist.packageName); - intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, hist.getTask().taskId); + intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, hist.getTaskRecord().taskId); } } @@ -524,7 +524,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio // Don't kill process(es) that has an activity not stopped. return false; } - final TaskRecord otherTask = activity.getTask(); + final TaskRecord otherTask = activity.getTaskRecord(); if (tr.taskId != otherTask.taskId && otherTask.inRecents) { // Don't kill process(es) that has an activity in a different task that is // also in recents. @@ -557,7 +557,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio continue; } - final TaskRecord task = r.getTask(); + final TaskRecord task = r.getTaskRecord(); if (task != null) { if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + task + " from " + r); @@ -600,7 +600,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } if (r.visible) { callback.onVisibleActivity(); - final TaskRecord task = r.getTask(); + final TaskRecord task = r.getTaskRecord(); if (task != null && minTaskLayer > 0) { final int layer = task.mLayerRank; if (layer >= 0 && minTaskLayer > layer) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index cfd1f86cdeaa..cdf911881395 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -195,7 +195,6 @@ 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; @@ -598,7 +597,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (mForceSeamlesslyRotate || requested) { mPendingSeamlessRotate = new SeamlessRotator(oldRotation, rotation, getDisplayInfo()); mPendingSeamlessRotate.unrotate(transaction, this); - mService.markForSeamlessRotation(this, true); + mWmService.markForSeamlessRotation(this, true); } } @@ -607,7 +606,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mPendingSeamlessRotate.finish(this, timeout); mFinishSeamlessRotateFrameNumber = getFrameNumber(); mPendingSeamlessRotate = null; - mService.markForSeamlessRotation(this, false); + mWmService.markForSeamlessRotation(this, false); } } @@ -651,8 +650,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mAttrs.copyFrom(a); mLastSurfaceInsets.set(mAttrs.surfaceInsets); mViewVisibility = viewVisibility; - mPolicy = mService.mPolicy; - mContext = mService.mContext; + mPolicy = mWmService.mPolicy; + mContext = mWmService.mContext; DeathRecipient deathRecipient = new DeathRecipient(); mSeq = seq; mEnforceSizeCompat = (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0; @@ -846,7 +845,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWindowFrames.mContainingFrame.bottom = mWindowFrames.mContainingFrame.top + frozen.height(); } - final WindowState imeWin = mService.mRoot.getCurrentInputMethodWindow(); + final WindowState imeWin = mWmService.mRoot.getCurrentInputMethodWindow(); // IME is up and obscuring this window. Adjust the window position so it is visible. if (imeWin != null && imeWin.isVisibleNow() && isInputMethodTarget()) { if (inFreeformWindowingMode() && mWindowFrames.mContainingFrame.bottom @@ -1204,7 +1203,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } updateLastInsetValues(); - mService.makeWindowFreezingScreenIfNeededLocked(this); + mWmService.makeWindowFreezingScreenIfNeededLocked(this); // If the orientation is changing, or we're starting or ending a drag resizing action, // then we need to hold off on unfreezing the display until this window has been @@ -1221,9 +1220,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mAppToken.clearAllDrawn(); } } - if (!mService.mResizingWindows.contains(this)) { + if (!mWmService.mResizingWindows.contains(this)) { if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG_WM, "Resizing window " + this); - mService.mResizingWindows.add(this); + mWmService.mResizingWindows.add(this); } } else if (getOrientationChanging()) { if (isDrawnLw()) { @@ -1231,7 +1230,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + this + ", surfaceController " + winAnimator.mSurfaceController); setOrientationChanging(false); mLastFreezeDuration = (int)(SystemClock.elapsedRealtime() - - mService.mDisplayFreezeTime); + - mWmService.mDisplayFreezeTime); } } } @@ -1604,7 +1603,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // 3. WS is currently visible if (!runningAppAnimation && isVisibleNow) { final AccessibilityController accessibilityController = - mService.mAccessibilityController; + mWmService.mAccessibilityController; final int winTransit = TRANSIT_EXIT; mWinAnimator.applyAnimationLocked(winTransit, false /* isEntrance */); //TODO (multidisplay): Magnification is supported only for the default @@ -1626,8 +1625,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (isVisibleNow()) { mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false); //TODO (multidisplay): Magnification is supported only for the default - if (mService.mAccessibilityController != null && isDefaultDisplay()) { - mService.mAccessibilityController.onWindowTransitionLocked(this, TRANSIT_EXIT); + if (mWmService.mAccessibilityController != null && isDefaultDisplay()) { + mWmService.mAccessibilityController.onWindowTransitionLocked(this, TRANSIT_EXIT); } changed = true; if (displayContent != null) { @@ -1645,7 +1644,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override void onResize() { - final ArrayList<WindowState> resizingWindows = mService.mResizingWindows; + final ArrayList<WindowState> resizingWindows = mWmService.mResizingWindows; if (mHasSurface && !isGoneForLayoutLw() && !resizingWindows.contains(this)) { if (DEBUG_RESIZE) Slog.d(TAG, "onResize: Resizing " + this); resizingWindows.add(this); @@ -1669,8 +1668,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mLayoutNeeded = true; setDisplayLayoutNeeded(); - if (!mService.mResizingWindows.contains(this)) { - mService.mResizingWindows.add(this); + if (!mWmService.mResizingWindows.contains(this)) { + mWmService.mResizingWindows.add(this); } } @@ -1702,9 +1701,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } //TODO (multidisplay): Accessibility supported only for the default display. - if (mService.mAccessibilityController != null + if (mWmService.mAccessibilityController != null && getDisplayContent().getDisplayId() == DEFAULT_DISPLAY) { - mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); + mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); } try { @@ -1818,7 +1817,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // we are doing this as part of processing a death note.) } - mService.postWindowRemoveCleanupLocked(this); + mWmService.postWindowRemoveCleanupLocked(this); } @Override @@ -1859,7 +1858,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + " mWillReplaceWindow=" + mWillReplaceWindow + " inPendingTransaction=" + (mAppToken != null ? mAppToken.inPendingTransaction : false) - + " mDisplayFrozen=" + mService.mDisplayFrozen + + " mDisplayFrozen=" + mWmService.mDisplayFrozen + " callers=" + Debug.getCallers(6)); // Visibility of the removed window. Will be used later to update orientation later on. @@ -1894,7 +1893,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mAppDied = true; setDisplayLayoutNeeded(); - mService.mWindowPlacerLocked.performSurfacePlacement(); + mWmService.mWindowPlacerLocked.performSurfacePlacement(); // Set up a replacement input channel since the app is now dead. // We need to catch tapping on the dead window to restart the app. @@ -1913,11 +1912,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // mAnimatingExit affects canAffectSystemUiFlags(). Run layout such that // any change from that is performed immediately. setDisplayLayoutNeeded(); - mService.requestTraversal(); + mWmService.requestTraversal(); } //TODO (multidisplay): Magnification is supported only for the default display. - if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) { - mService.mAccessibilityController.onWindowTransitionLocked(this, transit); + if (mWmService.mAccessibilityController != null + && displayId == DEFAULT_DISPLAY) { + mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit); } } final boolean isAnimating = isAnimating() @@ -1951,7 +1951,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP displayContent.sendNewConfiguration(); } } - mService.updateFocusedWindowLocked(isFocused() + mWmService.updateFocusedWindowLocked(isFocused() ? UPDATE_FOCUS_REMOVING_FOCUS : UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/); @@ -1965,9 +1965,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP setDisplayLayoutNeeded(); // Request a focus update as this window's input channel is already gone. Otherwise // we could have no focused window in input manager. - final boolean focusChanged = mService.updateFocusedWindowLocked( + final boolean focusChanged = mWmService.updateFocusedWindowLocked( UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/); - mService.mWindowPlacerLocked.performSurfacePlacement(); + mWmService.mWindowPlacerLocked.performSurfacePlacement(); if (focusChanged) { getDisplayContent().getInputMonitor().updateInputWindowsLw(false /*force*/); } @@ -2020,7 +2020,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP private final class DeadWindowEventReceiver extends InputEventReceiver { DeadWindowEventReceiver(InputChannel inputChannel) { - super(inputChannel, mService.mH.getLooper()); + super(inputChannel, mWmService.mH.getLooper()); } @Override public void onInputEvent(InputEvent event) { @@ -2051,7 +2051,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // Create dummy event receiver that simply reports all events as handled. mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel); } - mService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder()); + mWmService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder()); } void disposeInputChannel() { @@ -2062,7 +2062,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // unregister server channel first otherwise it complains about broken channel if (mInputChannel != null) { - mService.mInputManager.unregisterInputChannel(mInputChannel); + mWmService.mInputManager.unregisterInputChannel(mInputChannel); mInputChannel.dispose(); mInputChannel = null; @@ -2194,13 +2194,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (isFocused()) { if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, "setAnimationLocked: setting mFocusMayChange true"); - mService.mFocusMayChange = true; + mWmService.mFocusMayChange = true; } setDisplayLayoutNeeded(); // Window is no longer visible -- make sure if we were waiting // for it to be displayed before enabling the display, that // we allow the display to be enabled now. - mService.enableScreenIfNeededLocked(); + mWmService.enableScreenIfNeededLocked(); } } } @@ -2217,8 +2217,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // We need to turn on screen regardless of visibility. boolean hasTurnScreenOnFlag = (mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0; boolean allowTheaterMode = - mService.mAllowTheaterModeWakeFromLayout || Settings.Global.getInt( - mService.mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0) + mWmService.mAllowTheaterModeWakeFromLayout || Settings.Global.getInt( + mWmService.mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0) == 0; boolean canTurnScreenOn = mAppToken == null || mAppToken.canTurnScreenOn(); @@ -2268,8 +2268,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // For child windows we want to use the pid for the parent window in case the the child // window was added from another process. final int pid = getParentWindow() != null ? getParentWindow().mSession.mPid : mSession.mPid; - mTempConfiguration.setTo(mService.mProcessConfigurations.get( - pid, mService.mRoot.getConfiguration())); + mTempConfiguration.setTo(mWmService.mProcessConfigurations.get( + pid, mWmService.mRoot.getConfiguration())); return mTempConfiguration; } @@ -2325,13 +2325,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP public void binderDied() { try { boolean resetSplitScreenResizing = false; - synchronized (mService.mGlobalLock) { - final WindowState win = mService.windowForClientLocked(mSession, mClient, false); + synchronized (mWmService.mGlobalLock) { + final WindowState win = mWmService + .windowForClientLocked(mSession, mClient, false); Slog.i(TAG, "WIN DEATH: " + win); if (win != null) { final DisplayContent dc = getDisplayContent(); if (win.mAppToken != null && win.mAppToken.findMainWindow() == win) { - mService.mTaskSnapshotController.onAppDied(win.mAppToken); + mWmService.mTaskSnapshotController.onAppDied(win.mAppToken); } win.removeIfPossible(shouldKeepVisibleDeadAppWindow()); if (win.mAttrs.type == TYPE_DOCK_DIVIDER) { @@ -2355,7 +2356,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP try { // Note: this calls into ActivityManager, so we must *not* hold the window // manager lock while calling this. - mService.mActivityTaskManager.setSplitScreenResizing(false); + mWmService.mActivityTaskManager.setSplitScreenResizing(false); } catch (RemoteException e) { // Local call, shouldn't return RemoteException. throw e.rethrowAsRuntimeException(); @@ -2462,10 +2463,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWinAnimator.applyAnimationLocked(TRANSIT_ENTER, true); } if (requestAnim) { - mService.scheduleAnimationLocked(); + mWmService.scheduleAnimationLocked(); } if ((mAttrs.flags & FLAG_NOT_FOCUSABLE) == 0) { - mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */); + mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */); } return true; } @@ -2500,18 +2501,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // Window is no longer visible -- make sure if we were waiting // for it to be displayed before enabling the display, that // we allow the display to be enabled now. - mService.enableScreenIfNeededLocked(); + mWmService.enableScreenIfNeededLocked(); if (isFocused) { if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, "WindowState.hideLw: setting mFocusMayChange true"); - mService.mFocusMayChange = true; + mWmService.mFocusMayChange = true; } } if (requestAnim) { - mService.scheduleAnimationLocked(); + mWmService.scheduleAnimationLocked(); } if (isFocused) { - mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */); + mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */); } return true; } @@ -2573,7 +2574,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // and add the window only if the permission was granted. Therefore, if // the mode is MODE_DEFAULT we want the op to succeed as the window is // shown. - final int mode = mService.mAppOps.startOpNoThrow(mAppOp, + final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp, getOwningUid(), getOwningPackage(), true); if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) { setAppOpVisibilityLw(false); @@ -2582,7 +2583,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP void resetAppOpsState() { if (mAppOp != OP_NONE && mAppOpVisibility) { - mService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage()); + mWmService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage()); } } @@ -2595,13 +2596,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (mAppOpVisibility) { // There is a race between the check and the finish calls but this is fine // as this would mean we will get another change callback and will reconcile. - int mode = mService.mAppOps.checkOpNoThrow(mAppOp, uid, packageName); + int mode = mWmService.mAppOps.checkOpNoThrow(mAppOp, uid, packageName); if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) { - mService.mAppOps.finishOp(mAppOp, uid, packageName); + mWmService.mAppOps.finishOp(mAppOp, uid, packageName); setAppOpVisibilityLw(false); } } else { - final int mode = mService.mAppOps.startOpNoThrow(mAppOp, uid, packageName, true); + final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp, uid, packageName, true); if (mode == MODE_ALLOWED || mode == MODE_DEFAULT) { setAppOpVisibilityLw(true); } @@ -2622,7 +2623,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // in wake lock statistics. So in particular, we don't want to include the // window's hash code as in toString(). final CharSequence tag = getWindowTag(); - mDrawLock = mService.mPowerManager.newWakeLock(DRAW_WAKE_LOCK, "Window:" + tag); + mDrawLock = mWmService.mPowerManager.newWakeLock(DRAW_WAKE_LOCK, "Window:" + tag); mDrawLock.setReferenceCounted(false); mDrawLock.setWorkSource(new WorkSource(mOwnerUid, mAttrs.packageName)); } @@ -2707,10 +2708,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mAppFreezing = false; if (mHasSurface && !getOrientationChanging() - && mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) { + && mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "set mOrientationChanging of " + this); setOrientationChanging(true); - mService.mRoot.mOrientationChangeComplete = false; + mWmService.mRoot.mOrientationChangeComplete = false; } mLastFreezeDuration = 0; setDisplayLayoutNeeded(); @@ -2800,7 +2801,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } return win.mShowToOwnerOnly - && !mService.isCurrentProfileLocked(UserHandle.getUserId(win.mOwnerUid)); + && !mWmService.isCurrentProfileLocked(UserHandle.getUserId(win.mOwnerUid)); } private static void applyInsets(Region outRegion, Rect frame, Rect inset) { @@ -2899,7 +2900,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this + ": " + mWindowFrames.mCompatFrame); final MergedConfiguration mergedConfiguration = - new MergedConfiguration(mService.mRoot.getConfiguration(), + new MergedConfiguration(mWmService.mRoot.getConfiguration(), getMergedOverrideConfiguration()); setLastReportedMergedConfiguration(mergedConfiguration); @@ -2920,7 +2921,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (mAttrs.type != WindowManager.LayoutParams.TYPE_APPLICATION_STARTING && mClient instanceof IWindow.Stub) { // To prevent deadlock simulate one-way call if win.mClient is a local object. - mService.mH.post(new Runnable() { + mWmService.mH.post(new Runnable() { @Override public void run() { try { @@ -2939,8 +2940,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } //TODO (multidisplay): Accessibility supported only for the default display. - if (mService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) { - mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); + if (mWmService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) { + mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); } mWindowFrames.resetInsetsChanged(); @@ -2949,12 +2950,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } catch (RemoteException e) { setOrientationChanging(false); mLastFreezeDuration = (int)(SystemClock.elapsedRealtime() - - mService.mDisplayFreezeTime); + - mWmService.mDisplayFreezeTime); // We are assuming the hosting process is dead or in a zombie state. Slog.w(TAG, "Failed to report 'resized' to the client of " + this + ", removing this window."); - mService.mPendingRemove.add(this); - mService.mWindowPlacerLocked.requestTraversal(); + mWmService.mPendingRemove.add(this); + mWmService.mWindowPlacerLocked.requestTraversal(); } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @@ -3015,7 +3016,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } public void registerFocusObserver(IWindowFocusObserver observer) { - synchronized (mService.mGlobalLock) { + synchronized (mWmService.mGlobalLock) { if (mFocusCallbacks == null) { mFocusCallbacks = new RemoteCallbackList<IWindowFocusObserver>(); } @@ -3024,7 +3025,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } public void unregisterFocusObserver(IWindowFocusObserver observer) { - synchronized (mService.mGlobalLock) { + synchronized (mWmService.mGlobalLock) { if (mFocusCallbacks != null) { mFocusCallbacks.unregister(observer); } @@ -3097,7 +3098,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override void setWaitingForDrawnIfResizingChanged() { if (isDragResizeChanged()) { - mService.mWaitingForDrawn.add(this); + mWmService.mWaitingForDrawn.add(this); } super.setWaitingForDrawnIfResizingChanged(); } @@ -3586,7 +3587,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (dc != null && (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) { dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; dc.setLayoutNeeded(); - mService.mWindowPlacerLocked.requestTraversal(); + mWmService.mWindowPlacerLocked.requestTraversal(); } for (int i = mChildren.size() - 1; i >= 0; i--) { @@ -3684,7 +3685,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP logPerformShow("Showing "); - mService.enableScreenIfNeededLocked(); + mWmService.enableScreenIfNeededLocked(); mWinAnimator.applyEnterAnimationLocked(); // Force the show in the next prepareSurfaceLocked() call. @@ -3692,7 +3693,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (DEBUG_ANIM) Slog.v(TAG, "performShowLocked: mDrawState=HAS_DRAWN in " + this); mWinAnimator.mDrawState = HAS_DRAWN; - mService.scheduleAnimationLocked(); + mWmService.scheduleAnimationLocked(); if (mHidden) { mHidden = false; @@ -3971,7 +3972,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (mWinAnimator.mEnteringAnimation) { mWinAnimator.mEnteringAnimation = false; - mService.requestTraversal(); + mWmService.requestTraversal(); // System windows don't have an activity and an app token as a result, but need a way // to be informed about their entrance animation end. if (mAppToken == null) { @@ -3987,8 +3988,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } //TODO (multidisplay): Accessibility is supported only for the default display. - if (mService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) { - mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); + if (mWmService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) { + mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); } if (!isSelfOrAncestorWindowAnimatingExit()) { @@ -4014,10 +4015,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mAppToken.destroySurfaces(); } else { if (hasSurface) { - mService.mDestroySurface.add(this); + mWmService.mDestroySurface.add(this); } if (mRemoveOnExit) { - mService.mPendingRemove.add(this); + mWmService.mPendingRemove.add(this); mRemoveOnExit = false; } } @@ -4048,7 +4049,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } if (mDestroying) { mDestroying = false; - mService.mDestroySurface.remove(this); + mWmService.mDestroySurface.remove(this); didSomething = true; } } @@ -4077,7 +4078,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (displayContent != null) { displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; if (DEBUG_LAYOUT_REPEATS) { - mService.mWindowPlacerLocked.debugLayoutRepeats("hideWallpaperWindow " + this, + mWmService.mWindowPlacerLocked.debugLayoutRepeats("hideWallpaperWindow " + this, displayContent.pendingLayoutChanges); } } @@ -4292,7 +4293,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } if (mDestroying) { mDestroying = false; - mService.mDestroySurface.remove(this); + mWmService.mDestroySurface.remove(this); } if (oldVisibility == View.GONE) { mWinAnimator.mEnterAnimationPending = true; @@ -4360,10 +4361,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(), displayInfo.appWidth, displayInfo.appHeight); anim.restrictDuration(MAX_ANIMATION_DURATION); - anim.scaleCurrentDuration(mService.getWindowAnimationScaleLocked()); + anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked()); final AnimationAdapter adapter = new LocalAnimationAdapter( new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */), - mService.mSurfaceAnimationRunner); + mWmService.mSurfaceAnimationRunner); startAnimation(mPendingTransaction, adapter); commitPendingTransaction(); } @@ -4377,7 +4378,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP transformFrameToSurfacePosition(left, top, newPosition); final AnimationAdapter adapter = new LocalAnimationAdapter( new MoveAnimationSpec(oldPosition.x, oldPosition.y, newPosition.x, newPosition.y), - mService.mSurfaceAnimationRunner); + mWmService.mSurfaceAnimationRunner); startAnimation(getPendingTransaction(), adapter); } @@ -4471,7 +4472,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP public boolean isFocused() { final WindowState outer = mOuter.get(); if (outer != null) { - synchronized (outer.mService.mGlobalLock) { + synchronized (outer.mWmService.mGlobalLock) { return outer.isFocused(); } } @@ -4811,7 +4812,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final Animation anim = AnimationUtils.loadAnimation(mContext, com.android.internal.R.anim.window_move_from_decor); mDuration = (long) - (anim.computeDurationHint() * mService.getWindowAnimationScaleLocked()); + (anim.computeDurationHint() * mWmService.getWindowAnimationScaleLocked()); mInterpolator = anim.getInterpolator(); mFrom.set(fromX, fromY); mTo.set(toX, toY); diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 78a3fe5ac4ca..8bc6db7fb9a7 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -234,7 +234,7 @@ class WindowStateAnimator { private final Point mTmpPos = new Point(); WindowStateAnimator(final WindowState win) { - final WindowManagerService service = win.mService; + final WindowManagerService service = win.mWmService; mService = service; mAnimator = service.mAnimator; diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index d8242f8a6daa..9c1378211e6d 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -172,8 +172,8 @@ class WindowToken extends WindowContainer<WindowState> { setHidden(true); if (changed) { - mService.mWindowPlacerLocked.performSurfacePlacement(); - mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /*updateInputWindows*/); + mWmService.mWindowPlacerLocked.performSurfacePlacement(); + mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /*updateInputWindows*/); } if (delayed) { @@ -202,7 +202,7 @@ class WindowToken extends WindowContainer<WindowState> { if (!mChildren.contains(win)) { if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + this); addChild(win, mWindowComparator); - mService.mWindowsChanged = true; + mWmService.mWindowsChanged = true; // TODO: Should we also be setting layout needed here and other places? } } @@ -326,9 +326,9 @@ class WindowToken extends WindowContainer<WindowState> { * system bars, or in other words extend outside of the "Decor Frame" */ boolean canLayerAboveSystemBars() { - int layer = mService.mPolicy.getWindowLayerFromTypeLw(windowType, + int layer = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType, mOwnerCanManageAppTokens); - int navLayer = mService.mPolicy.getWindowLayerFromTypeLw(TYPE_NAVIGATION_BAR, + int navLayer = mWmService.mPolicy.getWindowLayerFromTypeLw(TYPE_NAVIGATION_BAR, mOwnerCanManageAppTokens); return mOwnerCanManageAppTokens && (layer > navLayer); } 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..43d2dcf7e0d1 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 = reinterpret_cast<jclass>(env->NewGlobalRef(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/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/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java index c7409d7f8335..89c7b71e2cc1 100644 --- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java +++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java @@ -20,7 +20,6 @@ import static com.android.server.am.MemoryStatUtil.BYTES_IN_KILOBYTE; import static com.android.server.am.MemoryStatUtil.JIFFY_NANOS; import static com.android.server.am.MemoryStatUtil.MemoryStat; import static com.android.server.am.MemoryStatUtil.PAGE_SIZE; -import static com.android.server.am.MemoryStatUtil.parseMemoryMaxUsageFromMemCg; import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromMemcg; import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromProcfs; import static com.android.server.am.MemoryStatUtil.parseVmHWMFromProcfs; @@ -197,23 +196,6 @@ public class MemoryStatUtilTest { } @Test - public void testParseMemoryMaxUsageFromMemCg_parsesCorrectValue() { - assertEquals(1234, parseMemoryMaxUsageFromMemCg("1234")); - } - - @Test - public void testParseMemoryMaxUsageFromMemCg_emptyContents() { - assertEquals(0, parseMemoryMaxUsageFromMemCg("")); - - assertEquals(0, parseMemoryMaxUsageFromMemCg(null)); - } - - @Test - public void testParseMemoryMaxUsageFromMemCg_incorrectValue() { - assertEquals(0, parseMemoryMaxUsageFromMemCg("memory")); - } - - @Test public void testParseMemoryStatFromProcfs_parsesCorrectValues() { MemoryStat stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS); assertEquals(1, stat.pgfault); 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 92efc3cc2b3c..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,9 +141,8 @@ 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; @@ -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(); @@ -1782,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); @@ -1880,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)); } @@ -1914,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. @@ -1935,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); } } @@ -1998,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; } @@ -2036,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); } } @@ -2049,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/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java deleted file mode 100644 index 2e47c353d8c2..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java +++ /dev/null @@ -1,322 +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 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, 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 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/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/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index 0e30037bde6c..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,16 +72,13 @@ 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. @@ -81,13 +89,45 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { 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/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java index 78a67d212f24..f7b5d26ac87e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java @@ -18,15 +18,8 @@ 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; @@ -36,9 +29,6 @@ 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; @@ -53,12 +43,7 @@ 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; @@ -66,8 +51,6 @@ import androidx.test.filters.MediumTest; import org.junit.Before; import org.junit.Test; -import java.util.ArrayList; - /** * Tests for the {@link ActivityStackSupervisor} class. * @@ -127,7 +110,7 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { assertThat(mSupervisor.mWaitingActivityLaunched).isEmpty(); assertEquals(deliverToTopWait.result, START_DELIVERED_TO_TOP); - assertEquals(deliverToTopWait.who, firstActivity.realActivity); + assertEquals(deliverToTopWait.who, firstActivity.mActivityComponent); } } } 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 0da0b247b60c..048384e9536e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -233,7 +233,7 @@ public class ActivityStackTests extends ActivityTestsBase { .setStack(mStack) .setUid(0) .build(); - final TaskRecord task = r.getTask(); + final TaskRecord task = r.getTaskRecord(); // Overlay must be for a different user to prevent recognizing a matching top activity final ActivityRecord taskOverlay = new ActivityBuilder(mService).setTask(task) .setUid(UserHandle.PER_USER_RANGE * 2).build(); 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 d78fbcd4571a..5c918b4c8bfe 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -113,6 +113,7 @@ public class ActivityStarterTests extends ActivityTestsBase { 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 { @@ -243,7 +244,7 @@ public class ActivityStarterTests extends ActivityTestsBase { final ActivityRecord source = builder.build(); if (!containsConditions(preconditions, PRECONDITION_NO_INTENT_COMPONENT)) { - intent.setComponent(source.realActivity); + intent.setComponent(source.mActivityComponent); } if (containsConditions(preconditions, PRECONDITION_DISALLOW_APP_SWITCHING)) { @@ -262,11 +263,11 @@ public class ActivityStarterTests extends ActivityTestsBase { PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION)) { doAnswer((inv) -> { throw new RemoteException(); - }).when(packageManager).activitySupportsIntent(eq(source.realActivity), eq(intent), - any()); + }).when(packageManager).activitySupportsIntent( + eq(source.mActivityComponent), eq(intent), any()); } else { doReturn(!containsConditions(preconditions, PRECONDITION_NO_VOICE_SESSION_SUPPORT)) - .when(packageManager).activitySupportsIntent(eq(source.realActivity), + .when(packageManager).activitySupportsIntent(eq(source.mActivityComponent), eq(intent), any()); } } catch (RemoteException e) { @@ -430,7 +431,7 @@ public class ActivityStarterTests extends ActivityTestsBase { .setCreateTask(true) .build(); - focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + focusActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); final ActivityRecord reusableActivity = new ActivityBuilder(mService) .setCreateTask(true) @@ -438,10 +439,10 @@ public class ActivityStarterTests extends ActivityTestsBase { // Create reusable activity after entering split-screen so that it is the top secondary // stack. - reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + reusableActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); // Set focus back to primary. - final ActivityStack focusStack = focusActivity.getStack(); + final ActivityStack focusStack = focusActivity.getActivityStack(); focusStack.moveToFront("testSplitScreenDeliverToTop"); doReturn(reusableActivity).when(mRootActivityContainer).findTask(any(), anyInt()); @@ -467,14 +468,14 @@ public class ActivityStarterTests extends ActivityTestsBase { .setCreateTask(true) .build(); - reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + reusableActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); final ActivityRecord focusActivity = new ActivityBuilder(mService) .setCreateTask(true) .build(); // Enter split-screen. Primary stack should have focus. - focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + focusActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); doReturn(reusableActivity).when(mRootActivityContainer).findTask(any(), anyInt()); @@ -561,8 +562,10 @@ public class ActivityStarterTests extends ActivityTestsBase { public void testBackgroundActivityStartsAllowed_noStartsAborted() { doReturn(true).when(mService).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest("allowed_noStartsAborted", - false, UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, false, false); + runAndVerifyBackgroundActivityStartsSubtest("allowed_noStartsAborted", false, + UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, + UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1, + false, false); } /** @@ -573,8 +576,11 @@ public class ActivityStarterTests extends ActivityTestsBase { public void testBackgroundActivityStartsDisallowed_unsupportedStartsAborted() { doReturn(false).when(mService).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest("disallowed_unsupportedUsecase_aborted", - true, UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, false, false); + runAndVerifyBackgroundActivityStartsSubtest( + "disallowed_unsupportedUsecase_aborted", true, + UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, + UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1, + false, false); } /** @@ -586,44 +592,77 @@ public class ActivityStarterTests extends ActivityTestsBase { public void testBackgroundActivityStartsDisallowed_supportedStartsNotAborted() { doReturn(false).when(mService).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest("disallowed_rootUid_notAborted", - false, Process.ROOT_UID, false, PROCESS_STATE_TOP + 1, false, false); - runAndVerifyBackgroundActivityStartsSubtest("disallowed_systemUid_notAborted", - false, Process.SYSTEM_UID, false, PROCESS_STATE_TOP + 1, false, false); - runAndVerifyBackgroundActivityStartsSubtest("disallowed_hasVisibleWindow_notAborted", - false, UNIMPORTANT_UID, true, PROCESS_STATE_TOP + 1, false, false); - runAndVerifyBackgroundActivityStartsSubtest("disallowed_processStateTop_notAborted", - false, UNIMPORTANT_UID, false, PROCESS_STATE_TOP, false, false); - runAndVerifyBackgroundActivityStartsSubtest("disallowed_hasForegroundActivities_notAborted", - false, UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, true, false); - runAndVerifyBackgroundActivityStartsSubtest("disallowed_callerIsRecents_notAborted", - false, UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, false, true); + 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 testCallingUid, boolean hasVisibleWindow, int procState, + int callingUid, boolean callingUidHasVisibleWindow, int callingUidProcState, + int realCallingUid, boolean realCallingUidHasVisibleWindow, int realCallingUidProcState, boolean hasForegroundActivities, boolean callerIsRecents) { // window visibility - doReturn(hasVisibleWindow).when(mService.mWindowManager).isAnyWindowVisibleForUid( - testCallingUid); + doReturn(callingUidHasVisibleWindow).when(mService.mWindowManager).isAnyWindowVisibleForUid( + callingUid); + doReturn(realCallingUidHasVisibleWindow).when(mService.mWindowManager) + .isAnyWindowVisibleForUid(realCallingUid); // process importance - doReturn(procState).when(mService).getUidStateLocked(testCallingUid); + 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 = testCallingUid; + ai.uid = callingUid; final WindowProcessController callerApp = - new WindowProcessController(mService, ai, null, testCallingUid, -1, null, null); + 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(testCallingUid); + doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid); final ActivityOptions options = spy(ActivityOptions.makeBasic()); - ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK).setCaller(caller) - .setCallingUid(testCallingUid).setActivityOptions(new SafeActivityOptions(options)); + 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(); 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 ead9731782e8..3a56419f67ae 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -34,7 +34,6 @@ 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.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; @@ -403,11 +402,10 @@ class ActivityTestsBase { doReturn(true).when(this).isBackgroundActivityStartsEnabled(); } - void setActivityManagerService(IntentFirewall intentFirewall, - PendingIntentController intentController, ActivityManagerInternal amInternal, - WindowManagerService wm) { + void setup(IntentFirewall intentFirewall, PendingIntentController intentController, + ActivityManagerInternal amInternal, WindowManagerService wm, Looper looper) { mAmInternal = amInternal; - setActivityManagerService(intentFirewall, intentController); + initialize(intentFirewall, intentController, looper); initRootActivityContainerMocks(wm); setWindowManager(wm); } @@ -517,8 +515,8 @@ class ActivityTestsBase { mWindowManager = prepareMockWindowManager(); mUgmInternal = mock(UriGrantsManagerInternal.class); - atm.setActivityManagerService(mIntentFirewall, mPendingIntentController, - new LocalService(), mWindowManager); + atm.setup(mIntentFirewall, mPendingIntentController, new LocalService(), mWindowManager, + testInjector.mHandlerThread.getLooper()); mActivityTaskManager = atm; mAtmInternal = atm.mInternal; 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 19d18cab931e..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,13 +24,14 @@ 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; 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 99abbf7b1f3a..0ee532d0b075 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -34,14 +34,15 @@ 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; @@ -118,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(); } @@ -156,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); @@ -164,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(); } @@ -185,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(); } @@ -221,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 @@ -247,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 @@ -331,7 +335,7 @@ public class AppWindowTokenTests extends WindowTestsBase { public void testTransferStartingWindowWhileCreating() { final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken(); final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken(); - ((TestWindowManagerPolicy) token1.mService.mPolicy).setRunnableWhenAddingSplashScreen( + ((TestWindowManagerPolicy) token1.mWmService.mPolicy).setRunnableWhenAddingSplashScreen( () -> { // Surprise, ...! Transfer window in the middle of the creation flow. token2.addStartingWindow(mPackageName, 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 a6415d1f2b1f..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; 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 ad293d963f3e..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; diff --git a/services/tests/servicestests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java index 241b987e58ba..c11e606386e6 100644 --- a/services/tests/servicestests/src/com/android/server/wm/InsetsSourceProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java @@ -18,6 +18,7 @@ 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; @@ -25,14 +26,11 @@ import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.view.InsetsSource; -import org.junit.Test; -import org.junit.runner.RunWith; - import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; -@RunWith(AndroidJUnit4.class) +import org.junit.Test; + @SmallTest @FlakyTest(detail = "Promote once confirmed non-flaky") @Presubmit diff --git a/services/tests/servicestests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index 7505db103bbf..331622ce22a5 100644 --- a/services/tests/servicestests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -20,20 +20,18 @@ 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 org.junit.Test; -import org.junit.runner.RunWith; - import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; -@RunWith(AndroidJUnit4.class) +import org.junit.Test; + @SmallTest @FlakyTest(detail = "Promote once confirmed non-flaky") @Presubmit diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java index fa4898b16040..3720c8566e74 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java @@ -89,9 +89,9 @@ public class LaunchParamsControllerTests extends ActivityTestsBase { final WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0); final ActivityOptions options = mock(ActivityOptions.class); - mController.calculate(record.getTask(), layout, record, source, options, + mController.calculate(record.getTaskRecord(), layout, record, source, options, new LaunchParams()); - verify(positioner, times(1)).onCalculate(eq(record.getTask()), eq(layout), eq(record), + verify(positioner, times(1)).onCalculate(eq(record.getTaskRecord()), eq(layout), eq(record), eq(source), eq(options), any(), any()); } @@ -114,7 +114,7 @@ public class LaunchParamsControllerTests extends ActivityTestsBase { mPersister.putLaunchParams(userId, name, expected); - mController.calculate(activity.getTask(), null /*layout*/, activity, null /*source*/, + mController.calculate(activity.getTaskRecord(), null /*layout*/, activity, null /*source*/, null /*options*/, new LaunchParams()); verify(positioner, times(1)).onCalculate(any(), any(), any(), any(), any(), eq(expected), any()); @@ -228,7 +228,7 @@ public class LaunchParamsControllerTests extends ActivityTestsBase { final LaunchParams result = new LaunchParams(); final ActivityRecord vrActivity = new ActivityBuilder(mService).build(); - vrActivity.requestedVrComponent = vrActivity.realActivity; + vrActivity.requestedVrComponent = vrActivity.mActivityComponent; // VR activities should always land on default display. mController.calculate(null /*task*/, null /*layout*/, vrActivity /*activity*/, @@ -412,8 +412,9 @@ public class LaunchParamsControllerTests extends ActivityTestsBase { @Override void getLaunchParams(TaskRecord task, ActivityRecord activity, LaunchParams params) { - final int userId = task != null ? task.userId : activity.userId; - final ComponentName name = task != null ? task.realActivity : activity.realActivity; + final int userId = task != null ? task.userId : activity.mUserId; + final ComponentName name = task != null + ? task.realActivity : activity.mActivityComponent; params.reset(); final Map<ComponentName, LaunchParams> map = mMap.get(userId); 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 2c3c66be6248..f3a125bf79e4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java @@ -272,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/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/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/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 index 631de99dc3ad..9b18388b5305 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -16,8 +16,6 @@ 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; @@ -51,7 +49,6 @@ 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; @@ -101,11 +98,11 @@ public class RootActivityContainerTests extends ActivityTestsBase { public void testReplacingTaskInPinnedStack() { final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true) .setStack(mFullscreenStack).build(); - final TaskRecord firstTask = firstActivity.getTask(); + final TaskRecord firstTask = firstActivity.getTaskRecord(); final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true) .setStack(mFullscreenStack).build(); - final TaskRecord secondTask = secondActivity.getTask(); + final TaskRecord secondTask = secondActivity.getTaskRecord(); mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack"); @@ -257,7 +254,7 @@ public class RootActivityContainerTests extends ActivityTestsBase { assertTrue(pinnedActivity.isFocusable()); // Without the overridding activity, stack should not be focusable. - pinnedStack.removeTask(pinnedActivity.getTask(), "testFocusability", + pinnedStack.removeTask(pinnedActivity.getTaskRecord(), "testFocusability", REMOVE_TASK_MODE_DESTROYING); assertFalse(pinnedStack.isFocusable()); } 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 e638a6accf7c..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; 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 bd8cd1f37f5e..9569c0d5affa 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -175,7 +175,7 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase { WINDOWING_MODE_FREEFORM); ActivityRecord source = createSourceActivity(freeformDisplay); - assertEquals(RESULT_CONTINUE, mTarget.onCalculate(source.getTask(), null /* layout */, + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(source.getTaskRecord(), null /* layout */, null /* activity */, null /* source */, null /* options */, mCurrent, mResult)); assertEquals(freeformDisplay.mDisplayId, mResult.mPreferredDisplayId); 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 792e8a6f7582..792e8a6f7582 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java 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 bbf508dd1630..bbf508dd1630 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java 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 432af0d7a469..432af0d7a469 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java 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..d4a32cfc4a1d 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; @@ -782,7 +782,7 @@ public class WindowContainerTests extends WindowTestsBase { } TestWindowContainer addChildWindow() { - return addChildWindow(new TestWindowContainerBuilder(mService).setLayer(1)); + return addChildWindow(new TestWindowContainerBuilder(mWmService).setLayer(1)); } @Override 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..a1b0411afcde 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.mWmService.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.mWmService, 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.mWmService.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.mWmService, 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/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index d97f0c5f7684..143b323892f3 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -304,4 +304,10 @@ interface ITelecomService { * @see TelecomServiceImpl#handleCallIntent */ void handleCallIntent(in Intent intent); + + void setTestDefaultCallScreeningApp(String packageName); + + void addOrRemoveTestCallCompanionApp(String packageName, boolean isAdded); + + void setTestAutoModeApp(String packageName); } diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java index da3acc2eedfe..448207424d59 100644 --- a/telephony/java/android/telephony/RadioAccessFamily.java +++ b/telephony/java/android/telephony/RadioAccessFamily.java @@ -460,6 +460,9 @@ public class RadioAccessFamily implements Parcelable { if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA)) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA; } + if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_NR)) != 0) { + networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_NR; + } return (networkTypeRaf == 0) ? TelephonyManager.NETWORK_TYPE_UNKNOWN : networkTypeRaf; } diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 777d219449a2..13fbeaaa02b7 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -223,9 +223,15 @@ public class ServiceState implements Parcelable { public static final int RIL_RADIO_TECHNOLOGY_LTE_CA = 19; /** - * Number of radio technologies for GSM, UMTS and CDMA. + * NR(New Radio) 5G. + * @hide + */ + public static final int RIL_RADIO_TECHNOLOGY_NR = 20; + + /** + * The number of the radio technologies. */ - private static final int NEXT_RIL_RADIO_TECHNOLOGY = 20; + private static final int NEXT_RIL_RADIO_TECHNOLOGY = 21; /** @hide */ public static final int RIL_RADIO_CDMA_TECHNOLOGY_BITMASK = 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/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 79ed93e009dc..d54da0905e18 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -2243,9 +2243,11 @@ public class TelephonyManager { /** Current network is LTE_CA {@hide} */ @UnsupportedAppUsage public static final int NETWORK_TYPE_LTE_CA = TelephonyProtoEnums.NETWORK_TYPE_LTE_CA; // = 19. + /** Current network is NR(New Radio) 5G. */ + public static final int NETWORK_TYPE_NR = TelephonyProtoEnums.NETWORK_TYPE_NR; // 20. /** Max network type number. Update as new types are added. Don't add negative types. {@hide} */ - public static final int MAX_NETWORK_TYPE = NETWORK_TYPE_LTE_CA; + public static final int MAX_NETWORK_TYPE = NETWORK_TYPE_NR; /** @hide */ @IntDef({ @@ -2269,6 +2271,7 @@ public class TelephonyManager { NETWORK_TYPE_TD_SCDMA, NETWORK_TYPE_IWLAN, NETWORK_TYPE_LTE_CA, + NETWORK_TYPE_NR, }) @Retention(RetentionPolicy.SOURCE) public @interface NetworkType{} @@ -4214,7 +4217,8 @@ public class TelephonyManager { } /** - * Returns the voice mail count for a subscription. Return 0 if unavailable. + * Returns the voice mail count for a subscription. Return 0 if unavailable or the caller does + * not have the READ_PHONE_STATE permission. * @param subId whose voice message count is returned * @hide */ @@ -4225,7 +4229,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony == null) return 0; - return telephony.getVoiceMessageCountForSubscriber(subId); + return telephony.getVoiceMessageCountForSubscriber(subId, getOpPackageName()); } catch (RemoteException ex) { return 0; } catch (NullPointerException ex) { @@ -8579,7 +8583,8 @@ public class TelephonyManager { /** * Return the application ID for the uicc application type like {@link #APPTYPE_CSIM}. - * All uicc applications are uniquely identified by application ID. See ETSI 102.221 and 101.220 + * All uicc applications are uniquely identified by application ID, represented by the hex + * string. e.g, A00000015141434C00. See ETSI 102.221 and 101.220 * <p>Requires Permission: * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} * @@ -9216,6 +9221,7 @@ public class TelephonyManager { NETWORK_TYPE_BITMASK_TD_SCDMA, NETWORK_TYPE_BITMASK_LTE, NETWORK_TYPE_BITMASK_LTE_CA, + NETWORK_TYPE_BITMASK_NR, }) public @interface NetworkTypeBitMask {} @@ -9332,6 +9338,13 @@ public class TelephonyManager { public static final int NETWORK_TYPE_BITMASK_LTE_CA = (1 << NETWORK_TYPE_LTE_CA); /** + * network type bitmask indicating the support of radio tech NR(New Radio) 5G. + * @hide + */ + @SystemApi + public static final int NETWORK_TYPE_BITMASK_NR = (1 << NETWORK_TYPE_NR); + + /** * @return Modem supported radio access family bitmask * * <p>Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java index 2d46ec26a755..41f7bd7ade63 100644 --- a/telephony/java/android/telephony/emergency/EmergencyNumber.java +++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java @@ -17,6 +17,7 @@ package android.telephony.emergency; import android.annotation.IntDef; +import android.annotation.NonNull; import android.hardware.radio.V1_4.EmergencyNumberSource; import android.hardware.radio.V1_4.EmergencyServiceCategory; import android.os.Parcel; @@ -196,7 +197,7 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu private final int mEmergencyNumberSourceBitmask; /** @hide */ - public EmergencyNumber(String number, String countryIso, + public EmergencyNumber(@NonNull String number, @NonNull String countryIso, int emergencyServiceCategories, int emergencyNumberSources) { this.mNumber = number; @@ -403,7 +404,7 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu * 0 if both have equal display priority. */ @Override - public int compareTo(EmergencyNumber emergencyNumber) { + public int compareTo(@NonNull EmergencyNumber emergencyNumber) { if (this.getDisplayPriorityScore() > emergencyNumber.getDisplayPriorityScore()) { return -1; diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index f73036e88a95..a6c24bf4e18a 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -23,6 +23,8 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.telecom.VideoProfile; +import android.telephony.emergency.EmergencyNumber; +import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories; import android.util.Log; import com.android.internal.telephony.PhoneConstants; @@ -295,6 +297,28 @@ public final class ImsCallProfile implements Parcelable { public @CallRestrictCause int mRestrictCause = CALL_RESTRICT_CAUSE_NONE; /** + * The emergency service categories, only valid if {@link #getServiceType} returns + * {@link #SERVICE_TYPE_EMERGENCY} + * + * If valid, the value is the bitwise-OR combination of the following constants: + * <ol> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li> + * </ol> + * + * Reference: 3gpp 23.167, Section 6 - Functional description; + * 3gpp 22.101, Section 10 - Emergency Calls. + */ + private @EmergencyServiceCategories int mEmergencyServiceCategories = + EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED; + + /** * Extras associated with this {@link ImsCallProfile}. * <p> * Valid data types include: @@ -495,6 +519,7 @@ public final class ImsCallProfile implements Parcelable { out.writeInt(mCallType); out.writeBundle(filteredExtras); out.writeParcelable(mMediaProfile, 0); + out.writeInt(mEmergencyServiceCategories); } private void readFromParcel(Parcel in) { @@ -502,6 +527,7 @@ public final class ImsCallProfile implements Parcelable { mCallType = in.readInt(); mCallExtras = in.readBundle(); mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader()); + mEmergencyServiceCategories = in.readInt(); } public static final Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() { @@ -710,4 +736,53 @@ public final class ImsCallProfile implements Parcelable { private static boolean isVideoStateSet(int videoState, int videoStateToCheck) { return (videoState & videoStateToCheck) == videoStateToCheck; } + + /** + * Set the emergency service categories. The set value is valid only if + * {@link #getServiceType} returns {@link #SERVICE_TYPE_EMERGENCY} + * + * If valid, the value is the bitwise-OR combination of the following constants: + * <ol> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li> + * </ol> + * + * Reference: 3gpp 23.167, Section 6 - Functional description; + * 3gpp 22.101, Section 10 - Emergency Calls. + */ + public void setEmergencyServiceCategories( + @EmergencyServiceCategories int emergencyServiceCategories) { + mEmergencyServiceCategories = emergencyServiceCategories; + } + + /** + * Get the emergency service categories, only valid if {@link #getServiceType} returns + * {@link #SERVICE_TYPE_EMERGENCY} + * + * @return the emergency service categories, + * + * If valid, the value is the bitwise-OR combination of the following constants: + * <ol> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li> + * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li> + * </ol> + * + * Reference: 3gpp 23.167, Section 6 - Functional description; + * 3gpp 22.101, Section 10 - Emergency Calls. + */ + public @EmergencyServiceCategories int getEmergencyServiceCategories() { + return mEmergencyServiceCategories; + } } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index fc42de58319e..32e939a0c925 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -393,16 +393,11 @@ interface ITelephony { int getDataActivationState(int subId, String callingPackage); /** - * Returns the unread count of voicemails - */ - int getVoiceMessageCount(); - - /** * Returns the unread count of voicemails for a subId. * @param subId user preferred subId. * Returns the unread count of voicemails */ - int getVoiceMessageCountForSubscriber(int subId); + int getVoiceMessageCountForSubscriber(int subId, String callingPackage); /** * Returns true if current state supports both voice and data 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/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py index fdc800bcc177..2f1e53ca5065 100755 --- a/tools/hiddenapi/generate_hiddenapi_lists.py +++ b/tools/hiddenapi/generate_hiddenapi_lists.py @@ -15,23 +15,54 @@ # limitations under the License. """ Generate API lists for non-SDK API enforcement. - -usage: generate-hiddenapi-lists.py [-h] - --input-public INPUT_PUBLIC - --input-private INPUT_PRIVATE - [--input-whitelists [INPUT_WHITELISTS [INPUT_WHITELISTS ...]]] - [--input-greylists [INPUT_GREYLISTS [INPUT_GREYLISTS ...]]] - [--input-blacklists [INPUT_BLACKLISTS [INPUT_BLACKLISTS ...]]] - --output-whitelist OUTPUT_WHITELIST - --output-light-greylist OUTPUT_LIGHT_GREYLIST - --output-dark-greylist OUTPUT_DARK_GREYLIST - --output-blacklist OUTPUT_BLACKLIST """ import argparse import os import sys import re +# Names of flags recognized by the `hiddenapi` tool. +FLAG_WHITELIST = "whitelist" +FLAG_GREYLIST = "greylist" +FLAG_BLACKLIST = "blacklist" +FLAG_GREYLIST_MAX_O = "greylist-max-o" + +# List of all known flags. +FLAGS = [ + FLAG_WHITELIST, + FLAG_GREYLIST, + FLAG_BLACKLIST, + FLAG_GREYLIST_MAX_O, +] +FLAGS_SET = set(FLAGS) + +# Suffix used in command line args to express that only known and +# otherwise unassigned entries should be assign the given flag. +# For example, the P dark greylist is checked in as it was in P, +# but signatures have changes since then. The flag instructs this +# script to skip any entries which do not exist any more. +FLAG_IGNORE_CONFLICTS_SUFFIX = "-ignore-conflicts" + +# Regex patterns of fields/methods used in serialization. These are +# considered public API despite being hidden. +SERIALIZATION_PATTERNS = [ + r'readObject\(Ljava/io/ObjectInputStream;\)V', + r'readObjectNoData\(\)V', + r'readResolve\(\)Ljava/lang/Object;', + r'serialVersionUID:J', + r'serialPersistentFields:\[Ljava/io/ObjectStreamField;', + r'writeObject\(Ljava/io/ObjectOutputStream;\)V', + r'writeReplace\(\)Ljava/lang/Object;', +] + +# Single regex used to match serialization API. It combines all the +# SERIALIZATION_PATTERNS into a single regular expression. +SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$') + +# Predicates to be used with filter_apis. +IS_UNASSIGNED = lambda api, flags: not flags +IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api) + def get_args(): """Parses command line arguments. @@ -39,21 +70,21 @@ def get_args(): Namespace: dictionary of parsed arguments """ parser = argparse.ArgumentParser() - parser.add_argument('--input-public', required=True, help='List of all public members') - parser.add_argument('--input-private', required=True, help='List of all private members') - parser.add_argument( - '--input-whitelists', nargs='*', - help='Lists of members to force on whitelist') - parser.add_argument( - '--input-greylists', nargs='*', - help='Lists of members to force on light greylist') - parser.add_argument( - '--input-blacklists', nargs='*', - help='Lists of members to force on blacklist') - parser.add_argument('--output-whitelist', required=True) - parser.add_argument('--output-light-greylist', required=True) - parser.add_argument('--output-dark-greylist', required=True) - parser.add_argument('--output-blacklist', required=True) + parser.add_argument('--output', required=True) + parser.add_argument('--public', required=True, help='list of all public entries') + parser.add_argument('--private', required=True, help='list of all private entries') + parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE', + help='CSV files to be merged into output') + + for flag in FLAGS: + ignore_conflicts_flag = flag + FLAG_IGNORE_CONFLICTS_SUFFIX + parser.add_argument('--' + flag, dest=flag, nargs='*', default=[], metavar='TXT_FILE', + help='lists of entries with flag "' + flag + '"') + parser.add_argument('--' + ignore_conflicts_flag, dest=ignore_conflicts_flag, nargs='*', + default=[], metavar='TXT_FILE', + help='lists of entries with flag "' + flag + + '". skip entry if missing or flag conflict.') + return parser.parse_args() def read_lines(filename): @@ -65,10 +96,13 @@ def read_lines(filename): filename (string): Path to the file to read from. Returns: - list: Lines of the loaded file as a list of strings. + Lines of the file as a list of string. """ with open(filename, 'r') as f: - return filter(lambda line: not line.startswith('#'), f.readlines()) + lines = f.readlines(); + lines = filter(lambda line: not line.startswith('#'), lines) + lines = map(lambda line: line.strip(), lines) + return set(lines) def write_lines(filename, lines): """Writes list of lines into a file, overwriting the file it it exists. @@ -77,167 +111,168 @@ def write_lines(filename, lines): filename (string): Path to the file to be writting into. lines (list): List of strings to write into the file. """ + lines = map(lambda line: line + '\n', lines) with open(filename, 'w') as f: f.writelines(lines) -def move_between_sets(subset, src, dst, source = "<unknown>"): - """Removes a subset of elements from one set and add it to another. - - Args: - subset (set): The subset of `src` to be moved from `src` to `dst`. - src (set): Source set. Must be a superset of `subset`. - dst (set): Destination set. Must be disjoint with `subset`. - """ - assert src.issuperset(subset), ( - "Error processing: {}\n" - "The following entries were not found:\n" - "{}" - "Please visit go/hiddenapi for more information.").format( - source, "".join(map(lambda x: " " + str(x), subset.difference(src)))) - assert dst.isdisjoint(subset) - # Order matters if `src` and `subset` are the same object. - dst.update(subset) - src.difference_update(subset) - -def get_package_name(signature): - """Returns the package name prefix of a class member signature. - - Example: "Ljava/lang/String;->hashCode()J" --> "Ljava/lang/" - - Args: - signature (string): Member signature - - Returns - string: Package name of the given member - """ - class_name_end = signature.find("->") - assert class_name_end != -1, "Invalid signature: {}".format(signature) - package_name_end = signature.rfind("/", 0, class_name_end) - assert package_name_end != -1, "Invalid signature: {}".format(signature) - return signature[:package_name_end + 1] - -def all_package_names(*args): - """Returns a set of packages names in given lists of member signatures. - - Example: args = [ set([ "Lpkg1/ClassA;->foo()V", "Lpkg2/ClassB;->bar()J" ]), - set([ "Lpkg1/ClassC;->baz()Z" ]) ] - return value = set([ "Lpkg1/", "Lpkg2" ]) - - Args: - *args (list): List of sets to iterate over and extract the package names - of its elements (member signatures) - - Returns: - set: All package names extracted from the given lists of signatures. - """ - packages = set() - for arg in args: - packages = packages.union(map(get_package_name, arg)) - return packages - -def move_all(src, dst): - """Moves all elements of one set to another. - - Args: - src (set): Source set. Will become empty. - dst (set): Destination set. Will contain all elements of `src`. - """ - move_between_sets(src, src, dst) - -def move_from_files(filenames, src, dst): - """Loads member signatures from a list of files and moves them to a given set. - - Opens files in `filenames`, reads all their lines and moves those from `src` - set to `dst` set. - - Args: - filenames (list): List of paths to files to be loaded. - src (set): Set that loaded lines should be moved from. - dst (set): Set that loaded lines should be moved to. - """ - if filenames: - for filename in filenames: - move_between_sets(set(read_lines(filename)), src, dst, filename) - -def move_serialization(src, dst): - """Moves all members matching serialization API signatures between given sets. - - Args: - src (set): Set that will be searched for serialization API and that API - will be removed from it. - dst (set): Set that serialization API will be moved to. - """ - serialization_patterns = [ - r'readObject\(Ljava/io/ObjectInputStream;\)V', - r'readObjectNoData\(\)V', - r'readResolve\(\)Ljava/lang/Object;', - r'serialVersionUID:J', - r'serialPersistentFields:\[Ljava/io/ObjectStreamField;', - r'writeObject\(Ljava/io/ObjectOutputStream;\)V', - r'writeReplace\(\)Ljava/lang/Object;', - ] - regex = re.compile(r'.*->(' + '|'.join(serialization_patterns) + r')$') - move_between_sets(filter(lambda api: regex.match(api), src), src, dst) - -def move_from_packages(packages, src, dst): - """Moves all members of given package names from one set to another. - - Args: - packages (list): List of string package names. - src (set): Set that will be searched for API matching one of the given - package names. Surch API will be removed from the set. - dst (set): Set that matching API will be moved to. - """ - move_between_sets(filter(lambda api: get_package_name(api) in packages, src), src, dst) +class FlagsDict: + def __init__(self, public_api, private_api): + # Bootstrap the entries dictionary. + + # Check that the two sets do not overlap. + public_api_set = set(public_api) + private_api_set = set(private_api) + assert public_api_set.isdisjoint(private_api_set), ( + "Lists of public and private API overlap. " + + "This suggests an issue with the `hiddenapi` build tool.") + + # Compute the whole key set + self._dict_keyset = public_api_set.union(private_api_set) + + # Create a dict that creates entries for both public and private API, + # and assigns public API to the whitelist. + self._dict = {} + for api in public_api: + self._dict[api] = set([ FLAG_WHITELIST ]) + for api in private_api: + self._dict[api] = set() + + def _check_entries_set(self, keys_subset, source): + assert isinstance(keys_subset, set) + assert keys_subset.issubset(self._dict_keyset), ( + "Error processing: {}\n" + "The following entries were unexpected:\n" + "{}" + "Please visit go/hiddenapi for more information.").format( + source, "".join(map(lambda x: " " + str(x), keys_subset - self._dict_keyset))) + + def _check_flags_set(self, flags_subset, source): + assert isinstance(flags_subset, set) + assert flags_subset.issubset(FLAGS_SET), ( + "Error processing: {}\n" + "The following flags were not recognized: \n" + "{}\n" + "Please visit go/hiddenapi for more information.").format( + source, "\n".join(flags_subset - FLAGS_SET)) + + def filter_apis(self, filter_fn): + """Returns APIs which match a given predicate. + + This is a helper function which allows to filter on both signatures (keys) and + flags (values). The built-in filter() invokes the lambda only with dict's keys. + + Args: + filter_fn : Function which takes two arguments (signature/flags) and returns a boolean. + + Returns: + A set of APIs which match the predicate. + """ + return set(filter(lambda x: filter_fn(x, self._dict[x]), self._dict_keyset)) + + def get_valid_subset_of_unassigned_apis(self, api_subset): + """Sanitizes a key set input to only include keys which exist in the dictionary + and have not been assigned any flags. + + Args: + entries_subset (set/list): Key set to be sanitized. + + Returns: + Sanitized key set. + """ + assert isinstance(api_subset, set) + return api_subset.intersection(self.filter_apis(IS_UNASSIGNED)) + + def generate_csv(self): + """Constructs CSV entries from a dictionary. + + Returns: + List of lines comprising a CSV file. See "parse_and_merge_csv" for format description. + """ + return sorted(map(lambda api: ",".join([api] + sorted(self._dict[api])), self._dict)) + + def parse_and_merge_csv(self, csv_lines, source = "<unknown>"): + """Parses CSV entries and merges them into a given dictionary. + + The expected CSV format is: + <api signature>,<flag1>,<flag2>,...,<flagN> + + Args: + csv_lines (list of strings): Lines read from a CSV file. + source (string): Origin of `csv_lines`. Will be printed in error messages. + + Throws: + AssertionError if parsed API signatures of flags are invalid. + """ + # Split CSV lines into arrays of values. + csv_values = [ line.split(',') for line in csv_lines ] + + # Check that all entries exist in the dict. + csv_keys = set([ csv[0] for csv in csv_values ]) + self._check_entries_set(csv_keys, source) + + # Check that all flags are known. + csv_flags = set(reduce(lambda x, y: set(x).union(y), [ csv[1:] for csv in csv_values ], [])) + self._check_flags_set(csv_flags, source) + + # Iterate over all CSV lines, find entry in dict and append flags to it. + for csv in csv_values: + self._dict[csv[0]].update(csv[1:]) + + def assign_flag(self, flag, apis, source="<unknown>"): + """Assigns a flag to given subset of entries. + + Args: + flag (string): One of FLAGS. + apis (set): Subset of APIs to recieve the flag. + source (string): Origin of `entries_subset`. Will be printed in error messages. + + Throws: + AssertionError if parsed API signatures of flags are invalid. + """ + # Check that all APIs exist in the dict. + self._check_entries_set(apis, source) + + # Check that the flag is known. + self._check_flags_set(set([ flag ]), source) + + # Iterate over the API subset, find each entry in dict and assign the flag to it. + for api in apis: + self._dict[api].add(flag) def main(argv): - args = get_args() - - # Initialize API sets by loading lists of public and private API. Public API - # are all members resolvable from SDK API stubs, other members are private. - # As an optimization, skip the step of moving public API from a full set of - # members and start with a populated whitelist. - whitelist = set(read_lines(args.input_public)) - uncategorized = set(read_lines(args.input_private)) - light_greylist = set() - dark_greylist = set() - blacklist = set() - - # Assert that there is no overlap between public and private API. - assert whitelist.isdisjoint(uncategorized) - num_all_api = len(whitelist) + len(uncategorized) - - # Read all files which manually assign members to specific lists. - move_from_files(args.input_whitelists, uncategorized, whitelist) - move_from_files(args.input_greylists, uncategorized, light_greylist) - move_from_files(args.input_blacklists, uncategorized, blacklist) - - # Iterate over all uncategorized members and move serialization API to whitelist. - move_serialization(uncategorized, whitelist) - - # Extract package names of members from whitelist and light greylist, which - # are assumed to have been finalized at this point. Assign all uncategorized - # members from the same packages to the dark greylist. - dark_greylist_packages = all_package_names(whitelist, light_greylist) - move_from_packages(dark_greylist_packages, uncategorized, dark_greylist) - - # Assign all uncategorized members to the blacklist. - move_all(uncategorized, blacklist) - - # Assert we have not missed anything. - assert whitelist.isdisjoint(light_greylist) - assert whitelist.isdisjoint(dark_greylist) - assert whitelist.isdisjoint(blacklist) - assert light_greylist.isdisjoint(dark_greylist) - assert light_greylist.isdisjoint(blacklist) - assert dark_greylist.isdisjoint(blacklist) - assert num_all_api == len(whitelist) + len(light_greylist) + len(dark_greylist) + len(blacklist) - - # Write final lists to disk. - write_lines(args.output_whitelist, whitelist) - write_lines(args.output_light_greylist, light_greylist) - write_lines(args.output_dark_greylist, dark_greylist) - write_lines(args.output_blacklist, blacklist) + # Parse arguments. + args = vars(get_args()) + + flags = FlagsDict(read_lines(args["public"]), read_lines(args["private"])) + + # Combine inputs which do not require any particular order. + # (1) Assign serialization API to whitelist. + flags.assign_flag(FLAG_WHITELIST, flags.filter_apis(IS_SERIALIZATION)) + + # (2) Merge input CSV files into the dictionary. + for filename in args["csv"]: + flags.parse_and_merge_csv(read_lines(filename), filename) + + # (3) Merge text files with a known flag into the dictionary. + for flag in FLAGS: + for filename in args[flag]: + flags.assign_flag(flag, read_lines(filename), filename) + + # Merge text files where conflicts should be ignored. + # This will only assign the given flag if: + # (a) the entry exists, and + # (b) it has not been assigned any other flag. + # Because of (b), this must run after all strict assignments have been performed. + for flag in FLAGS: + for filename in args[flag + FLAG_IGNORE_CONFLICTS_SUFFIX]: + valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(filename)) + flags.assign_flag(flag, valid_entries, filename) + + # Assign all remaining entries to the blacklist. + flags.assign_flag(FLAG_BLACKLIST, flags.filter_apis(IS_UNASSIGNED)) + + # Write output. + write_lines(args["output"], flags.generate_csv()) if __name__ == "__main__": main(sys.argv) diff --git a/tools/hiddenapi/generate_hiddenapi_lists_test.py b/tools/hiddenapi/generate_hiddenapi_lists_test.py index 4716241940b5..249f37db5a82 100755 --- a/tools/hiddenapi/generate_hiddenapi_lists_test.py +++ b/tools/hiddenapi/generate_hiddenapi_lists_test.py @@ -2,14 +2,14 @@ # # Copyright (C) 2018 The Android Open Source Project # -# Licensed under the Apache License, Version 2.0 (the "License"); +# Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, +# distributed under the License is distributed on an 'AS IS' BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. @@ -18,90 +18,90 @@ import unittest from generate_hiddenapi_lists import * class TestHiddenapiListGeneration(unittest.TestCase): + def test_init(self): + # Check empty lists + flags = FlagsDict([], []) + self.assertEquals(flags.generate_csv(), []) - def test_move_between_sets(self): - A = set([1, 2, 3, 4]) - B = set([5, 6, 7, 8]) - move_between_sets(set([2, 4]), A, B) - self.assertEqual(A, set([1, 3])) - self.assertEqual(B, set([2, 4, 5, 6, 7, 8])) - - def test_move_between_sets_fail_not_superset(self): - A = set([1, 2, 3, 4]) - B = set([5, 6, 7, 8]) - with self.assertRaises(AssertionError) as ar: - move_between_sets(set([0, 2]), A, B) - - def test_move_between_sets_fail_not_disjoint(self): - A = set([1, 2, 3, 4]) - B = set([4, 5, 6, 7, 8]) - with self.assertRaises(AssertionError) as ar: - move_between_sets(set([1, 4]), A, B) - - def test_get_package_name(self): - self.assertEqual(get_package_name("Ljava/lang/String;->clone()V"), "Ljava/lang/") - - def test_get_package_name_fail_no_arrow(self): - with self.assertRaises(AssertionError) as ar: - get_package_name("Ljava/lang/String;-clone()V") - with self.assertRaises(AssertionError) as ar: - get_package_name("Ljava/lang/String;>clone()V") - with self.assertRaises(AssertionError) as ar: - get_package_name("Ljava/lang/String;__clone()V") - - def test_get_package_name_fail_no_package(self): - with self.assertRaises(AssertionError) as ar: - get_package_name("LString;->clone()V") - - def test_all_package_names(self): - self.assertEqual(all_package_names(), set()) - self.assertEqual(all_package_names(set(["Lfoo/Bar;->baz()V"])), set(["Lfoo/"])) - self.assertEqual( - all_package_names(set(["Lfoo/Bar;->baz()V", "Lfoo/BarX;->bazx()I"])), - set(["Lfoo/"])) - self.assertEqual( - all_package_names( - set(["Lfoo/Bar;->baz()V"]), - set(["Lfoo/BarX;->bazx()I", "Labc/xyz/Mno;->ijk()J"])), - set(["Lfoo/", "Labc/xyz/"])) - - def test_move_all(self): - src = set([ "abc", "xyz" ]) - dst = set([ "def" ]) - move_all(src, dst) - self.assertEqual(src, set()) - self.assertEqual(dst, set([ "abc", "def", "xyz" ])) - - def test_move_from_packages(self): - src = set([ "Lfoo/bar/ClassA;->abc()J", # will be moved - "Lfoo/bar/ClassA;->def()J", # will be moved - "Lcom/pkg/example/ClassD;->ijk:J", # not moved: different package - "Lfoo/bar/xyz/ClassC;->xyz()Z" ]) # not moved: subpackage - dst = set() - packages = set([ "Lfoo/bar/" ]) - move_from_packages(packages, src, dst) - self.assertEqual( - src, set([ "Lfoo/bar/xyz/ClassC;->xyz()Z", "Lcom/pkg/example/ClassD;->ijk:J" ])) + # Check valid input - two public and two private API signatures. + flags = FlagsDict(['A', 'B'], ['C', 'D']) + self.assertEquals(flags.generate_csv(), + [ 'A,' + FLAG_WHITELIST, 'B,' + FLAG_WHITELIST, 'C', 'D' ]) + + # Check invalid input - overlapping public/private API signatures. + with self.assertRaises(AssertionError): + flags = FlagsDict(['A', 'B'], ['B', 'C', 'D']) + + def test_filter_apis(self): + # Initialize flags so that A and B are put on the whitelist and + # C, D, E are left unassigned. Try filtering for the unassigned ones. + flags = FlagsDict(['A', 'B'], ['C', 'D', 'E']) + filter_set = flags.filter_apis(lambda api, flags: not flags) + self.assertTrue(isinstance(filter_set, set)) + self.assertEqual(filter_set, set([ 'C', 'D', 'E' ])) + + def test_get_valid_subset_of_unassigned_keys(self): + # Create flags where only A is unassigned. + flags = FlagsDict(['A'], ['B', 'C']) + flags.assign_flag(FLAG_GREYLIST, set(['C'])) + self.assertEquals(flags.generate_csv(), + [ 'A,' + FLAG_WHITELIST, 'B', 'C,' + FLAG_GREYLIST ]) + + # Check three things: + # (1) B is selected as valid unassigned + # (2) A is not selected because it is assigned 'whitelist' + # (3) D is not selected because it is not a valid key self.assertEqual( - dst, set([ "Lfoo/bar/ClassA;->abc()J", "Lfoo/bar/ClassA;->def()J" ])) - - def test_move_serialization(self): - # All the entries should be moved apart from the last one - src = set([ "Lfoo/bar/ClassA;->readObject(Ljava/io/ObjectInputStream;)V", - "Lfoo/bar/ClassA;->readObjectNoData()V", - "Lfoo/bar/ClassA;->readResolve()Ljava/lang/Object;", - "Lfoo/bar/ClassA;->serialVersionUID:J", - "Lfoo/bar/ClassA;->serialPersistentFields:[Ljava/io/ObjectStreamField;", - "Lfoo/bar/ClassA;->writeObject(Ljava/io/ObjectOutputStream;)V", - "Lfoo/bar/ClassA;->writeReplace()Ljava/lang/Object;", - # Should not be moved as signature does not match - "Lfoo/bar/ClassA;->readObject(Ljava/io/ObjectInputStream;)I"]) - expectedToMove = len(src) - 1 - dst = set() - packages = set([ "Lfoo/bar/" ]) - move_serialization(src, dst) - self.assertEqual(len(src), 1) - self.assertEqual(len(dst), expectedToMove) + flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])), set([ 'B' ])) + + def test_parse_and_merge_csv(self): + flags = FlagsDict(['A'], ['B']) + self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ]) + + # Test empty CSV entry. + flags.parse_and_merge_csv(['B']) + self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ]) + + # Test assigning an already assigned flag. + flags.parse_and_merge_csv(['A,' + FLAG_WHITELIST]) + self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ]) + + # Test new additions. + flags.parse_and_merge_csv([ + 'A,' + FLAG_GREYLIST, + 'B,' + FLAG_BLACKLIST + ',' + FLAG_GREYLIST_MAX_O ]) + self.assertEqual(flags.generate_csv(), + [ 'A,' + FLAG_GREYLIST + "," + FLAG_WHITELIST, + 'B,' + FLAG_BLACKLIST + "," + FLAG_GREYLIST_MAX_O ]) + + # Test unknown API signature. + with self.assertRaises(AssertionError): + flags.parse_and_merge_csv([ 'C' ]) + + # Test unknown flag. + with self.assertRaises(AssertionError): + flags.parse_and_merge_csv([ 'A,foo' ]) + + def test_assign_flag(self): + flags = FlagsDict(['A'], ['B']) + self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ]) + + # Test assigning an already assigned flag. + flags.assign_flag(FLAG_WHITELIST, set([ 'A' ])) + self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ]) + + # Test new additions. + flags.assign_flag(FLAG_GREYLIST, set([ 'A', 'B' ])) + self.assertEquals(flags.generate_csv(), + [ 'A,' + FLAG_GREYLIST + "," + FLAG_WHITELIST, 'B,' + FLAG_GREYLIST ]) + + # Test invalid API signature. + with self.assertRaises(AssertionError): + flags.assign_flag(FLAG_WHITELIST, set([ 'C' ])) + + # Test invalid flag. + with self.assertRaises(AssertionError): + flags.assign_flag('foo', set([ 'A' ])) if __name__ == '__main__': unittest.main() diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp index 3f42275b4e1b..faa354788c4e 100644 --- a/tools/incident_section_gen/main.cpp +++ b/tools/incident_section_gen/main.cpp @@ -262,8 +262,9 @@ static bool isDefaultMessageImpl(const Descriptor* descriptor, const Destination return false; case FieldDescriptor::TYPE_STRING: if (getPrivacyFlags(field).patterns_size() != 0) return false; + break; default: - continue; + break; } } parents->erase(descriptor->full_name()); 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..c6acd026bd39 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); @@ -190,8 +190,8 @@ interface IWifiManager void unregisterNetworkRequestMatchCallback(int callbackIdentifier); - boolean addNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName); + int addNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName); - boolean removeNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName); + int removeNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName); } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 7aff03c00dd3..9789319ed6b9 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -25,6 +25,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.UnsupportedAppUsage; +import android.app.ActivityManager; import android.content.Context; import android.content.pm.ParceledListSlice; import android.net.ConnectivityManager; @@ -137,6 +138,55 @@ public class WifiManager { public static final int ERROR_AUTH_FAILURE_EAP_FAILURE = 3; /** + * Maximum number of active network suggestions allowed per app. + * @hide + */ + public static final int NETWORK_SUGGESTIONS_MAX_PER_APP = + ActivityManager.isLowRamDeviceStatic() ? 256 : 1024; + + /** + * Reason code if all of the network suggestions were successfully added or removed. + */ + public static final int STATUS_NETWORK_SUGGESTIONS_SUCCESS = 0; + + /** + * Reason code if there was an internal error in the platform while processing the addition or + * removal of suggestions. + */ + public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1; + + /** + * Reason code if one or more of the network suggestions added already exists in platform's + * database. + * @see WifiNetworkSuggestion#equals(Object) + */ + public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 2; + + /** + * Reason code if the number of network suggestions provided by the app crosses the max + * threshold set per app. + * @see #getMaxNumberOfNetworkSuggestionsPerApp() + */ + public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 3; + + /** + * Reason code if one or more of the network suggestions removed does not exist in platform's + * database. + */ + public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 4; + + @IntDef(prefix = { "STATUS_NETWORK_SUGGESTIONS_" }, value = { + STATUS_NETWORK_SUGGESTIONS_SUCCESS, + STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL, + STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE, + STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP, + STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID, + }) + + @Retention(RetentionPolicy.SOURCE) + public @interface NetworkSuggestionsStatusCode {} + + /** * Broadcast intent action indicating whether Wi-Fi scanning is allowed currently * @hide */ @@ -1067,7 +1117,7 @@ public class WifiManager { public List<WifiConfiguration> getConfiguredNetworks() { try { ParceledListSlice<WifiConfiguration> parceledList = - mService.getConfiguredNetworks(); + mService.getConfiguredNetworks(mContext.getOpPackageName()); if (parceledList == null) { return Collections.emptyList(); } @@ -1126,7 +1176,6 @@ public class WifiManager { * @throws UnsupportedOperationException if Passpoint is not enabled on the device. * @hide */ - @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public List<OsuProvider> getMatchingOsuProviders(List<ScanResult> scanResults) { try { @@ -1497,12 +1546,13 @@ public class WifiManager { * suggestion back using this API.</li> * * @param networkSuggestions List of network suggestions provided by the app. - * @return true on success, false if any of the suggestions match (See + * @return Status code corresponding to the values in {@link NetworkSuggestionsStatusCode}. * {@link WifiNetworkSuggestion#equals(Object)} any previously provided suggestions by the app. * @throws {@link SecurityException} if the caller is missing required permissions. */ @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) - public boolean addNetworkSuggestions(@NonNull List<WifiNetworkSuggestion> networkSuggestions) { + public @NetworkSuggestionsStatusCode int addNetworkSuggestions( + @NonNull List<WifiNetworkSuggestion> networkSuggestions) { try { return mService.addNetworkSuggestions(networkSuggestions, mContext.getOpPackageName()); } catch (RemoteException e) { @@ -1510,21 +1560,20 @@ public class WifiManager { } } - /** - * Remove a subset of or all of networks from previously provided suggestions by the app to the - * device. + * Remove some or all of the network suggestions that were previously provided by the app. * See {@link WifiNetworkSuggestion} for a detailed explanation of the parameters. * See {@link WifiNetworkSuggestion#equals(Object)} for the equivalence evaluation used. * * @param networkSuggestions List of network suggestions to be removed. Pass an empty list * to remove all the previous suggestions provided by the app. - * @return true on success, false if any of the suggestions do not match any suggestions - * previously provided by the app. Any matching suggestions are removed from the device and - * will not be considered for any further connection attempts. + * @return Status code corresponding to the values in + * {@link NetworkSuggestionsStatusCode}. + * Any matching suggestions are removed from the device and will not be considered for any + * further connection attempts. */ @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) - public boolean removeNetworkSuggestions( + public @NetworkSuggestionsStatusCode int removeNetworkSuggestions( @NonNull List<WifiNetworkSuggestion> networkSuggestions) { try { return mService.removeNetworkSuggestions( @@ -1535,6 +1584,15 @@ public class WifiManager { } /** + * Returns the max number of network suggestions that are allowed per app on the device. + * @see #addNetworkSuggestions(List) + * @see #removeNetworkSuggestions(List) + */ + public int getMaxNumberOfNetworkSuggestionsPerApp() { + return NETWORK_SUGGESTIONS_MAX_PER_APP; + } + + /** * Add or update a Passpoint configuration. The configuration provides a credential * for connecting to Passpoint networks that are operated by the Passpoint * service provider specified in the configuration. @@ -1761,8 +1819,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 +1843,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 +1867,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 +2187,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 +3461,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 +3486,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 +3517,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 +3541,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 +3561,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 +3578,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/WifiNetworkConfigBuilder.java b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java index 87706b936f03..f73b9e5e2a00 100644 --- a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java +++ b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java @@ -42,8 +42,10 @@ import java.util.List; public class WifiNetworkConfigBuilder { private static final String MATCH_ALL_SSID_PATTERN_PATH = ".*"; private static final String MATCH_EMPTY_SSID_PATTERN_PATH = ""; - private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN = + private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN1 = new Pair(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS); + private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN2 = + new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.BROADCAST_ADDRESS); private static final Pair<MacAddress, MacAddress> MATCH_ALL_BSSID_PATTERN = new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS); private static final MacAddress MATCH_EXACT_BSSID_PATTERN_MASK = @@ -189,7 +191,13 @@ public class WifiNetworkConfigBuilder { * Set the BSSID to use for filtering networks from scan results. Will only match network whose * BSSID is identical to the specified value. * <p> - * <li>Only allowed for creating network specifier, i.e {@link #buildNetworkSpecifier()}. </li> + * <li>For network requests ({@link NetworkSpecifier}), built using + * {@link #buildNetworkSpecifier}, sets the BSSID to use for filtering networks from scan + * results. Will only match networks whose BSSID is identical to specified value.</li> + * <li>For network suggestions ({@link WifiNetworkSuggestion}), built using + * {@link #buildNetworkSuggestion()}, sets a specific BSSID for the network suggestion. + * If set, only the specified BSSID with the specified SSID will be considered for connection. + * If not set, all BSSIDs with the specified SSID will be considered for connection.</li> * <li>Overrides any previous value set using {@link #setBssid(MacAddress)} or * {@link #setBssidPattern(MacAddress, MacAddress)}.</li> * @@ -432,6 +440,9 @@ public class WifiNetworkConfigBuilder { if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) { wifiConfiguration.SSID = "\"" + mSsidPatternMatcher.getPath() + "\""; } + if (mBssidPatternMatcher.second == MATCH_EXACT_BSSID_PATTERN_MASK) { + wifiConfiguration.BSSID = mBssidPatternMatcher.first.toString(); + } setSecurityParamsInWifiConfiguration(wifiConfiguration); wifiConfiguration.hiddenSSID = mIsHiddenSSID; wifiConfiguration.priority = mPriority; @@ -460,7 +471,10 @@ public class WifiNetworkConfigBuilder { && mSsidPatternMatcher.getPath().equals(MATCH_EMPTY_SSID_PATTERN_PATH)) { return true; } - if (mBssidPatternMatcher.equals(MATCH_NO_BSSID_PATTERN)) { + if (mBssidPatternMatcher.equals(MATCH_NO_BSSID_PATTERN1)) { + return true; + } + if (mBssidPatternMatcher.equals(MATCH_NO_BSSID_PATTERN2)) { return true; } return false; @@ -474,6 +488,16 @@ public class WifiNetworkConfigBuilder { return false; } + private boolean hasSetMatchExactPattern() { + // exact ssid match with either match-all bssid or match-exact bssid. + if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL + && (mBssidPatternMatcher.equals(MATCH_ALL_BSSID_PATTERN) + || mBssidPatternMatcher.second.equals(MATCH_EXACT_BSSID_PATTERN_MASK))) { + return true; + } + return false; + } + private void validateSecurityParams() { int numSecurityTypes = 0; numSecurityTypes += mIsEnhancedOpen ? 1 : 0; @@ -566,9 +590,42 @@ public class WifiNetworkConfigBuilder { } /** - * Create a network suggestion object use in - * {@link WifiManager#addNetworkSuggestions(List)}. + * Create a network suggestion object use in {@link WifiManager#addNetworkSuggestions(List)}. * See {@link WifiNetworkSuggestion}. + *<p> + * Note: Apps can set a combination of SSID using {@link #setSsid(String)} and BSSID + * using {@link #setBssid(MacAddress)} to provide more fine grained network suggestions to the + * platform. + * </p> + * + * For example: + * To provide credentials for one open, one WPA2 and one WPA3 network with their + * corresponding SSID's: + * {@code + * final WifiNetworkSuggestion suggestion1 = + * new WifiNetworkConfigBuilder() + * .setSsid("test111111") + * .buildNetworkSuggestion() + * final WifiNetworkSuggestion suggestion2 = + * new WifiNetworkConfigBuilder() + * .setSsid("test222222") + * .setWpa2Passphrase("test123456") + * .buildNetworkSuggestion() + * final WifiNetworkSuggestion suggestion3 = + * new WifiNetworkConfigBuilder() + * .setSsid("test333333") + * .setWpa3Passphrase("test6789") + * .buildNetworkSuggestion() + * final List<WifiNetworkSuggestion> suggestionsList = new ArrayList<WifiNetworkSuggestion> {{ + * add(suggestion1); + * add(suggestion2); + * add(suggestion3); + * }}; + * final WifiManager wifiManager = + * context.getSystemService(Context.WIFI_SERVICE); + * wifiManager.addNetworkSuggestions(suggestionsList); + * ... + * } * * @return Instance of {@link WifiNetworkSuggestion}. * @throws IllegalStateException on invalid params set. @@ -577,11 +634,14 @@ public class WifiNetworkConfigBuilder { if (mSsidPatternMatcher == null) { throw new IllegalStateException("setSsid should be invoked for suggestion"); } - if (mSsidPatternMatcher.getType() != PatternMatcher.PATTERN_LITERAL - || mBssidPatternMatcher != null) { - throw new IllegalStateException("none of setSsidPattern/setBssidPattern/setBssid are" + setMatchAnyPatternIfUnset(); + if (!hasSetMatchExactPattern()) { + throw new IllegalStateException("none of setSsidPattern/setBssidPattern are" + " allowed for suggestion"); } + if (hasSetMatchNonePattern()) { + throw new IllegalStateException("cannot set match-none for suggestion"); + } validateSecurityParams(); return new WifiNetworkSuggestion( diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java index 25121e2dc8c7..760f1e6bc5e2 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -104,8 +104,8 @@ public final class WifiNetworkSuggestion implements Parcelable { @Override public int hashCode() { - return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.allowedKeyManagement, - suggestorUid); + return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.BSSID, + wifiConfiguration.allowedKeyManagement, suggestorUid); } /** @@ -121,6 +121,7 @@ public final class WifiNetworkSuggestion implements Parcelable { } WifiNetworkSuggestion lhs = (WifiNetworkSuggestion) obj; return Objects.equals(this.wifiConfiguration.SSID, lhs.wifiConfiguration.SSID) + && Objects.equals(this.wifiConfiguration.BSSID, lhs.wifiConfiguration.BSSID) && Objects.equals(this.wifiConfiguration.allowedKeyManagement, lhs.wifiConfiguration.allowedKeyManagement) && suggestorUid == lhs.suggestorUid; diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index 529548f1d2b8..fc5caf0a47d7 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} */ @@ -250,6 +253,14 @@ public class WifiScanner { */ @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public int type = TYPE_LOW_LATENCY; + /** + * This scan request may ignore location settings while receiving scans. This should only + * be used in emergency situations. + * {@hide} + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) + public boolean ignoreLocationSettings; /** Implement the Parcelable interface {@hide} */ public int describeContents() { @@ -267,6 +278,7 @@ public class WifiScanner { dest.writeInt(stepCount); dest.writeInt(isPnoScan ? 1 : 0); dest.writeInt(type); + dest.writeInt(ignoreLocationSettings ? 1 : 0); if (channels != null) { dest.writeInt(channels.length); for (int i = 0; i < channels.length; i++) { @@ -301,6 +313,7 @@ public class WifiScanner { settings.stepCount = in.readInt(); settings.isPnoScan = in.readInt() == 1; settings.type = in.readInt(); + settings.ignoreLocationSettings = in.readInt() == 1; int num_channels = in.readInt(); settings.channels = new ChannelSpec[num_channels]; for (int i = 0; i < num_channels; i++) { @@ -798,6 +811,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 +826,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 +838,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 +876,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 +891,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 +902,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 d0efbcffd0ae..b0ed11034810 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java @@ -1216,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 diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/AbstractWifiService.java index 04bc55710dfd..0f4e3a8ba20f 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(); } @@ -442,13 +442,13 @@ public abstract class AbstractWifiService extends IWifiManager.Stub { } @Override - public boolean addNetworkSuggestions( + public int addNetworkSuggestions( List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName) { throw new UnsupportedOperationException(); } @Override - public boolean removeNetworkSuggestions( + public int removeNetworkSuggestions( List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName) { throw new UnsupportedOperationException(); } diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index 8fe5af998dcf..13c8c9ea7ead 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -1298,13 +1298,26 @@ i * Verify that a call to cancel WPS immediately returns a failure. */ @Test public void addRemoveNetworkSuggestions() throws Exception { - when(mWifiService.addNetworkSuggestions(any(List.class), anyString())).thenReturn(true); - when(mWifiService.removeNetworkSuggestions(any(List.class), anyString())).thenReturn(true); + when(mWifiService.addNetworkSuggestions(any(List.class), anyString())) + .thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS); + when(mWifiService.removeNetworkSuggestions(any(List.class), anyString())) + .thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS); - assertTrue(mWifiManager.addNetworkSuggestions(new ArrayList<>())); + assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, + mWifiManager.addNetworkSuggestions(new ArrayList<>())); verify(mWifiService).addNetworkSuggestions(anyList(), eq(TEST_PACKAGE_NAME)); - assertTrue(mWifiManager.removeNetworkSuggestions(new ArrayList<>())); + assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, + mWifiManager.removeNetworkSuggestions(new ArrayList<>())); verify(mWifiService).removeNetworkSuggestions(anyList(), eq(TEST_PACKAGE_NAME)); } + + /** + * Verify call to {@link WifiManager#getMaxNumberOfNetworkSuggestionsPerApp()}. + */ + @Test + public void getMaxNumberOfNetworkSuggestionsPerApp() { + assertEquals(WifiManager.NETWORK_SUGGESTIONS_MAX_PER_APP, + mWifiManager.getMaxNumberOfNetworkSuggestionsPerApp()); + } } diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java index c455c6f0836d..2505499c85d7 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java @@ -244,7 +244,7 @@ public class WifiNetworkConfigBuilderTest { * when match-none SSID pattern is set. */ @Test(expected = IllegalStateException.class) - public void testWifiNetworkSpecifierBuilderWithMatchNoneSsidPattern() { + public void testWifiNetworkSpecifierBuilderWithMatchNoneSsidPattern1() { new WifiNetworkConfigBuilder() .setSsidPattern(new PatternMatcher("", PatternMatcher.PATTERN_LITERAL)) .buildNetworkSpecifier(); @@ -252,10 +252,21 @@ public class WifiNetworkConfigBuilderTest { /** * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when match-none SSID pattern is set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithMatchNoneSsidPattern2() { + new WifiNetworkConfigBuilder() + .setSsid("") + .buildNetworkSpecifier(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception * when match-none BSSID pattern is set. */ @Test(expected = IllegalStateException.class) - public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern() { + public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern1() { new WifiNetworkConfigBuilder() .setBssidPattern(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS) .buildNetworkSpecifier(); @@ -263,6 +274,28 @@ public class WifiNetworkConfigBuilderTest { /** * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when match-none BSSID pattern is set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern2() { + new WifiNetworkConfigBuilder() + .setBssid(MacAddress.BROADCAST_ADDRESS) + .buildNetworkSpecifier(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when match-none BSSID pattern is set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern3() { + new WifiNetworkConfigBuilder() + .setBssid(MacAddress.ALL_ZEROS_ADDRESS) + .buildNetworkSpecifier(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception * when SSID pattern is set for hidden network. */ @Test(expected = IllegalStateException.class) @@ -429,13 +462,15 @@ public class WifiNetworkConfigBuilderTest { * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for OWE network. */ @Test - public void testWifiNetworkSuggestionBuilderForEnhancedOpenNetwork() { + public void testWifiNetworkSuggestionBuilderForEnhancedOpenNetworkWithBssid() { WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder() .setSsid(TEST_SSID) + .setBssid(MacAddress.fromString(TEST_BSSID)) .setIsEnhancedOpen() .buildNetworkSuggestion(); assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID); + assertEquals(TEST_BSSID, suggestion.wifiConfiguration.BSSID); assertTrue(suggestion.wifiConfiguration.allowedKeyManagement .get(WifiConfiguration.KeyMgmt.OWE)); assertNull(suggestion.wifiConfiguration.preSharedKey); @@ -505,7 +540,7 @@ public class WifiNetworkConfigBuilderTest { /** * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception - * when {@link WifiNetworkConfigBuilder#setBssid(MacAddress)} is set. + * when {@link WifiNetworkConfigBuilder#setBssidPattern(MacAddress, MacAddress)} is set. */ @Test(expected = IllegalStateException.class) public void testWifiNetworkSuggestionBuilderWithBssidPattern() { @@ -518,23 +553,46 @@ public class WifiNetworkConfigBuilderTest { /** * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception - * when {@link WifiNetworkConfigBuilder#setBssidPattern(MacAddress, MacAddress)} is set. + * when {@link WifiNetworkConfigBuilder#setSsid(String)} is not set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSuggestionBuilderWithNoSsid() { + new WifiNetworkConfigBuilder() + .buildNetworkSuggestion(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception + * when {@link WifiNetworkConfigBuilder#setSsid(String)} is invoked with an invalid value. */ @Test(expected = IllegalStateException.class) - public void testWifiNetworkSuggestionBuilderWithBssid() { + public void testWifiNetworkSuggestionBuilderWithInvalidSsid() { + new WifiNetworkConfigBuilder() + .setSsid("") + .buildNetworkSuggestion(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception + * when {@link WifiNetworkConfigBuilder#setBssid(MacAddress)} is invoked with an invalid value. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSuggestionBuilderWithInvalidBroadcastBssid() { new WifiNetworkConfigBuilder() .setSsid(TEST_SSID) - .setBssid(MacAddress.fromString(TEST_BSSID)) + .setBssid(MacAddress.BROADCAST_ADDRESS) .buildNetworkSuggestion(); } /** * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception - * when {@link WifiNetworkConfigBuilder#setSsid(String)} is not set. + * when {@link WifiNetworkConfigBuilder#setBssid(MacAddress)} is invoked with an invalid value. */ @Test(expected = IllegalStateException.class) - public void testWifiNetworkSuggestionBuilderWithNoSsid() { + public void testWifiNetworkSuggestionBuilderWithInvalidAllZeroBssid() { new WifiNetworkConfigBuilder() + .setSsid(TEST_SSID) + .setBssid(MacAddress.ALL_ZEROS_ADDRESS) .buildNetworkSuggestion(); } diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java index 6bab60dd480b..5cc821717462 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java @@ -29,6 +29,7 @@ import org.junit.Test; @SmallTest public class WifiNetworkSuggestionTest { private static final String TEST_SSID = "\"Test123\""; + private static final String TEST_BSSID = "12:12:12:12:12:12"; private static final String TEST_SSID_1 = "\"Test1234\""; /** @@ -38,6 +39,7 @@ public class WifiNetworkSuggestionTest { public void testWifiNetworkSuggestionParcel() { WifiConfiguration configuration = new WifiConfiguration(); configuration.SSID = TEST_SSID; + configuration.BSSID = TEST_BSSID; configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion(configuration, false, true, 0); @@ -65,18 +67,20 @@ public class WifiNetworkSuggestionTest { /** * Check NetworkSuggestion equals returns {@code true} for 2 network suggestions with the same - * SSID, key mgmt and UID. + * SSID, BSSID, key mgmt and UID. */ @Test public void testWifiNetworkSuggestionEqualsSame() { WifiConfiguration configuration = new WifiConfiguration(); configuration.SSID = TEST_SSID; + configuration.BSSID = TEST_BSSID; configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion(configuration, true, false, 0); WifiConfiguration configuration1 = new WifiConfiguration(); configuration1.SSID = TEST_SSID; + configuration1.BSSID = TEST_BSSID; configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); WifiNetworkSuggestion suggestion1 = new WifiNetworkSuggestion(configuration1, false, true, 0); @@ -86,7 +90,7 @@ public class WifiNetworkSuggestionTest { /** * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same - * key mgmt and UID, but different SSID. + * BSSID, key mgmt and UID, but different SSID. */ @Test public void testWifiNetworkSuggestionEqualsFailsWhenSsidIsDifferent() { @@ -107,7 +111,29 @@ public class WifiNetworkSuggestionTest { /** * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same - * SSID and UID, but different key mgmt. + * SSID, key mgmt and UID, but different BSSID. + */ + @Test + public void testWifiNetworkSuggestionEqualsFailsWhenBssidIsDifferent() { + WifiConfiguration configuration = new WifiConfiguration(); + configuration.SSID = TEST_SSID; + configuration.BSSID = TEST_BSSID; + configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + WifiNetworkSuggestion suggestion = + new WifiNetworkSuggestion(configuration, false, false, 0); + + WifiConfiguration configuration1 = new WifiConfiguration(); + configuration1.SSID = TEST_SSID; + configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + WifiNetworkSuggestion suggestion1 = + new WifiNetworkSuggestion(configuration1, false, false, 0); + + assertNotEquals(suggestion, suggestion1); + } + + /** + * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same + * SSID, BSSID and UID, but different key mgmt. */ @Test public void testWifiNetworkSuggestionEqualsFailsWhenKeyMgmtIsDifferent() { @@ -128,7 +154,7 @@ public class WifiNetworkSuggestionTest { /** * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same - * SSID and key mgmt, but different UID. + * SSID, BSSID and key mgmt, but different UID. */ @Test public void testWifiNetworkSuggestionEqualsFailsWhenUidIsDifferent() { |