diff options
281 files changed, 6245 insertions, 2487 deletions
diff --git a/.gitignore b/.gitignore index 45884c46ffea..d7aebc6d7ee6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /.idea *.iml +*.sw* diff --git a/Android.bp b/Android.bp index 183121b3cf67..995fe3e2465e 100644 --- a/Android.bp +++ b/Android.bp @@ -773,7 +773,7 @@ java_defaults { "android.hardware.vibrator-V1.3-java", "android.hardware.wifi-V1.0-java-constants", "networkstack-aidl-interfaces-java", - "netd_aidl_interface-java", + "netd_aidl_parcelables-java", "devicepolicyprotosnano", ], @@ -1187,6 +1187,15 @@ packages_to_document = [ "org/apache/http/params", ] +// Make the api/current.txt file available for use by modules in other +// directories. +filegroup { + name: "frameworks-base-api-current.txt", + srcs: [ + "api/current.txt", + ], +} + framework_docs_only_args = " -android -manifest $(location core/res/AndroidManifest.xml) " + "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " + "-overview $(location core/java/overview.html) " + diff --git a/Android.mk b/Android.mk index 9a91dd1c491a..c58f7af1d7d5 100644 --- a/Android.mk +++ b/Android.mk @@ -77,8 +77,6 @@ docs offline-sdk-docs: $(OUT_DOCS)/offline-sdk-timestamp # Run this for checkbuild checkbuild: doc-comment-check-docs -# Check comment when you are updating the API -update-api: doc-comment-check-docs # ==== hiddenapi lists ======================================= ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true) diff --git a/api/current.txt b/api/current.txt index a0e14c24c029..15c4fdfed178 100644 --- a/api/current.txt +++ b/api/current.txt @@ -14096,6 +14096,34 @@ package android.graphics { ctor @Deprecated public EmbossMaskFilter(float[], float, float, float); } + public class HardwareRenderer { + ctor public HardwareRenderer(); + method public void clearContent(); + method public android.graphics.HardwareRenderer.FrameRenderRequest createRenderRequest(); + method public void destroy(); + method public boolean isOpaque(); + method public void notifyFramePending(); + method public void setContentRoot(@Nullable android.graphics.RenderNode); + method public void setLightSourceAlpha(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float); + method public void setLightSourceGeometry(float, float, float, float); + method public void setName(String); + method public void setOpaque(boolean); + method public void setStopped(boolean); + method public void setSurface(@Nullable android.view.Surface); + field public static final int SYNC_CONTEXT_IS_STOPPED = 4; // 0x4 + field public static final int SYNC_FRAME_DROPPED = 8; // 0x8 + field public static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 2; // 0x2 + field public static final int SYNC_OK = 0; // 0x0 + field public static final int SYNC_REDRAW_REQUESTED = 1; // 0x1 + } + + public final class HardwareRenderer.FrameRenderRequest { + method public android.graphics.HardwareRenderer.FrameRenderRequest setFrameCommitCallback(@NonNull java.util.concurrent.Executor, @NonNull Runnable); + method public android.graphics.HardwareRenderer.FrameRenderRequest setVsyncTime(long); + method public android.graphics.HardwareRenderer.FrameRenderRequest setWaitForPresent(boolean); + method public int syncAndDraw(); + } + public final class ImageDecoder implements java.lang.AutoCloseable { method public void close(); method @AnyThread @NonNull public static android.graphics.ImageDecoder.Source createSource(@NonNull android.content.res.Resources, int); @@ -27445,6 +27473,7 @@ package android.media.session { method public void seekTo(long); method public void sendCustomAction(@NonNull android.media.session.PlaybackState.CustomAction, @Nullable android.os.Bundle); method public void sendCustomAction(@NonNull String, @Nullable android.os.Bundle); + method public void setPlaybackSpeed(float); method public void setRating(android.media.Rating); method public void skipToNext(); method public void skipToPrevious(); @@ -27495,6 +27524,7 @@ package android.media.session { method public void onPrepareFromUri(android.net.Uri, android.os.Bundle); method public void onRewind(); method public void onSeekTo(long); + method public void onSetPlaybackSpeed(float); method public void onSetRating(@NonNull android.media.Rating); method public void onSkipToNext(); method public void onSkipToPrevious(); @@ -35050,7 +35080,7 @@ package android.os { method public android.os.PowerManager.WakeLock newWakeLock(int, String); method public void reboot(String); method public void registerThermalStatusCallback(@NonNull android.os.PowerManager.ThermalStatusCallback, @NonNull java.util.concurrent.Executor); - method public void unregisterThermalStatusCallback(android.os.PowerManager.ThermalStatusCallback); + method public void unregisterThermalStatusCallback(@NonNull android.os.PowerManager.ThermalStatusCallback); field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000 field public static final String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED"; field public static final String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED"; @@ -41953,7 +41983,6 @@ package android.service.voice { public class VoiceInteractionService extends android.app.Service { ctor public VoiceInteractionService(); - method public final void clearTranscription(boolean); method public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback); method public int getDisabledShowContext(); method public static boolean isActiveService(android.content.Context, android.content.ComponentName); @@ -41963,8 +41992,7 @@ package android.service.voice { method public void onReady(); method public void onShutdown(); method public void setDisabledShowContext(int); - method public final void setTranscription(@NonNull String); - method public final void setVoiceState(int); + method public final void setUiHints(@NonNull android.os.Bundle); method public void showSession(android.os.Bundle, int); field public static final String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService"; field public static final String SERVICE_META_DATA = "android.voice_interaction"; @@ -44395,6 +44423,7 @@ package android.telephony { method public String getMccString(); method public String getMncString(); method @Nullable public String getMobileNetworkOperator(); + method public int getUarfcn(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityTdscdma> CREATOR; } @@ -45069,12 +45098,12 @@ package android.telephony { method public boolean canChangeDtmfToneLength(); method @Nullable public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle); method public android.telephony.TelephonyManager createForSubscriptionId(int); - method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo(); + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo(); method public int getCallState(); method public int getCardIdForDefaultEuicc(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @WorkerThread public android.os.PersistableBundle getCarrierConfig(); method public int getCarrierIdFromSimMccMnc(); - method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.CellLocation getCellLocation(); + method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public android.telephony.CellLocation getCellLocation(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList(int); method public int getDataActivity(); @@ -45104,7 +45133,7 @@ package android.telephony { method public int getPhoneCount(); method public int getPhoneType(); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription(); - method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.ServiceState getServiceState(); + method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState(); method @Nullable public android.telephony.SignalStrength getSignalStrength(); method public int getSimCarrierId(); method @Nullable public CharSequence getSimCarrierIdName(); @@ -45146,8 +45175,8 @@ package android.telephony { method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle); method public boolean isWorldPhone(); method public void listen(android.telephony.PhoneStateListener, int); - method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback); + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback); + method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback); method public void sendDialerSpecialCode(String); method public String sendEnvelopeWithStatus(String); method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler); @@ -48998,6 +49027,7 @@ package android.util { } public final class StatsLog { + method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public static boolean logBinaryPushStateChanged(@NonNull String, long, int, int, @NonNull long[]); method public static boolean logEvent(int); method public static boolean logStart(int); method public static boolean logStop(int); @@ -50805,7 +50835,7 @@ package android.view { method @android.view.ViewDebug.ExportedProperty(category="drawing") public float getAlpha(); method public android.view.animation.Animation getAnimation(); method public android.os.IBinder getApplicationWindowToken(); - method @NonNull public java.util.List<java.lang.Integer> getAttributeResolutionStack(); + method @NonNull public java.util.List<java.lang.Integer> getAttributeResolutionStack(@AttrRes int); method @NonNull public java.util.Map<java.lang.Integer,java.lang.Integer> getAttributeSourceResourceMap(); method @android.view.ViewDebug.ExportedProperty @Nullable public String[] getAutofillHints(); method public final android.view.autofill.AutofillId getAutofillId(); @@ -53472,6 +53502,7 @@ package android.view.contentcapture { method public void close(); method @NonNull public final android.view.contentcapture.ContentCaptureSession createContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext); method public final void destroy(); + method @Nullable public final android.view.contentcapture.ContentCaptureContext getContentCaptureContext(); method public final android.view.contentcapture.ContentCaptureSessionId getContentCaptureSessionId(); method @NonNull public android.view.autofill.AutofillId newAutofillId(@NonNull android.view.autofill.AutofillId, long); method @NonNull public final android.view.ViewStructure newVirtualViewStructure(@NonNull android.view.autofill.AutofillId, long); @@ -53479,6 +53510,7 @@ package android.view.contentcapture { method public final void notifyViewDisappeared(@NonNull android.view.autofill.AutofillId); method public final void notifyViewTextChanged(@NonNull android.view.autofill.AutofillId, @Nullable CharSequence); method public final void notifyViewsDisappeared(@NonNull android.view.autofill.AutofillId, @NonNull long[]); + method public final void setContentCaptureContext(@Nullable android.view.contentcapture.ContentCaptureContext); } public final class ContentCaptureSessionId implements android.os.Parcelable { @@ -55739,6 +55771,7 @@ package android.widget { method public int getDropDownVerticalOffset(); method public int getDropDownWidth(); method protected android.widget.Filter getFilter(); + method public int getInputMethodMode(); method @Deprecated public android.widget.AdapterView.OnItemClickListener getItemClickListener(); method @Deprecated public android.widget.AdapterView.OnItemSelectedListener getItemSelectedListener(); method public int getListSelection(); @@ -55763,6 +55796,7 @@ package android.widget { method public void setDropDownHorizontalOffset(int); method public void setDropDownVerticalOffset(int); method public void setDropDownWidth(int); + method public void setInputMethodMode(int); method public void setListSelection(int); method public void setOnDismissListener(android.widget.AutoCompleteTextView.OnDismissListener); method public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener); diff --git a/api/removed.txt b/api/removed.txt index f5bd434c3cc0..c4ed871d0661 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -558,7 +558,7 @@ package android.telephony { public class TelephonyManager { method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo(); - method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback); + method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback); } } diff --git a/api/system-current.txt b/api/system-current.txt index 48a450a441a9..357f6a4a3607 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -285,9 +285,11 @@ package android.app { method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String); method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public static int getCurrentUser(); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String); + method @NonNull public java.util.Collection<java.util.Locale> getSupportedLocales(); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int); method @RequiresPermission(android.Manifest.permission.KILL_UID) public void killUid(int, String); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener); + method public void setDeviceLocales(@NonNull android.os.LocaleList); method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public static void setPersistentVrThread(int); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull android.os.UserHandle); } @@ -312,6 +314,7 @@ package android.app { method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(String, int, String, int); method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(String, int, int); field public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover"; + field public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility"; field public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications"; field public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn"; field public static final String OPSTR_ASSIST_SCREENSHOT = "android:assist_screenshot"; @@ -545,15 +548,17 @@ package android.app { } public class StatusBarManager { - method public android.util.Pair<java.lang.Integer,java.lang.Integer> getDisableFlags(); + method public android.app.StatusBarManager.DisableInfo getDisableInfo(); method public void setDisabledForSetup(boolean); - field public static final int DISABLE2_NONE = 0; // 0x0 - field public static final int DISABLE_EXPAND = 65536; // 0x10000 - field public static final int DISABLE_HOME = 2097152; // 0x200000 - field public static final int DISABLE_NONE = 0; // 0x0 - field public static final int DISABLE_NOTIFICATION_ALERTS = 262144; // 0x40000 - field public static final int DISABLE_RECENT = 16777216; // 0x1000000 - field public static final int DISABLE_SEARCH = 33554432; // 0x2000000 + } + + public static final class StatusBarManager.DisableInfo { + method public boolean areNoComponentsDisabled(); + method public boolean isNavigateToHomeDisabled(); + method public boolean isNotificationPeekingDisabled(); + method public boolean isRecentsDisabled(); + method public boolean isSearchDisabled(); + method public boolean isStatusBarExpansionDisabled(); } public final class Vr2dDisplayProperties implements android.os.Parcelable { @@ -1366,6 +1371,7 @@ package android.content { field public static final String ACTION_INSTALL_INSTANT_APP_PACKAGE = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE"; field public static final String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS"; field public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION"; + field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_MANAGE_APP_PERMISSION = "android.intent.action.MANAGE_APP_PERMISSION"; field public static final String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS"; field @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public static final String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP"; field public static final String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS"; @@ -1376,6 +1382,7 @@ package android.content { field public static final String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED"; field public static final String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART"; field public static final String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE"; + field public static final String ACTION_REVIEW_ACCESSIBILITY_SERVICES = "android.intent.action.REVIEW_ACCESSIBILITY_SERVICES"; field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_APP_PERMISSION_USAGE = "android.intent.action.REVIEW_APP_PERMISSION_USAGE"; field public static final String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS"; field public static final String ACTION_REVIEW_PERMISSION_USAGE = "android.intent.action.REVIEW_PERMISSION_USAGE"; @@ -1580,7 +1587,7 @@ package android.content.pm { method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); method public void sendDeviceCustomizationReadyBroadcast(); method @RequiresPermission(allOf={android.Manifest.permission.SET_PREFERRED_APPLICATIONS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public abstract boolean setDefaultBrowserPackageNameAsUser(String, int); - method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setDistractingPackageRestrictions(@NonNull String[], @android.content.pm.PackageManager.DistractionRestriction int); + method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setDistractingPackageRestrictions(@NonNull String[], int); method @RequiresPermission(android.Manifest.permission.SET_HARMFUL_APP_WARNINGS) public void setHarmfulAppWarning(@NonNull String, @Nullable CharSequence); method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable String); method @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo); @@ -1658,9 +1665,6 @@ package android.content.pm { method public abstract void onDexModuleRegistered(String, boolean, String); } - @IntDef(flag=true, prefix={"RESTRICTION_"}, value={android.content.pm.PackageManager.RESTRICTION_NONE, android.content.pm.PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, android.content.pm.PackageManager.RESTRICTION_HIDE_NOTIFICATIONS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.DistractionRestriction { - } - public static interface PackageManager.OnPermissionsChangedListener { method public void onPermissionsChanged(int); } @@ -5305,6 +5309,10 @@ package android.os { field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR; } + public final class LocaleList implements android.os.Parcelable { + method public static boolean isPseudoLocale(@Nullable android.icu.util.ULocale); + } + public final class NativeHandle implements java.io.Closeable { ctor public NativeHandle(); ctor public NativeHandle(@NonNull java.io.FileDescriptor, boolean); @@ -5783,8 +5791,10 @@ package android.provider { } public static interface DeviceConfig.Rollback { + field public static final String BOOT_NAMESPACE = "rollback_boot"; field public static final String ENABLE_ROLLBACK_TIMEOUT = "enable_rollback_timeout"; field public static final String NAMESPACE = "rollback"; + field public static final String ROLLBACK_LIFETIME_IN_MILLIS = "rollback_lifetime_in_millis"; } public static interface DeviceConfig.Runtime { @@ -5946,6 +5956,7 @@ package android.provider { public final class Settings { field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS"; field public static final String ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS = "android.settings.LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS"; + field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE"; field public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS"; } @@ -6301,20 +6312,11 @@ package android.service.autofill.augmented { } public abstract class PresentationParams { - method public int getFlags(); - method @Nullable public android.service.autofill.augmented.PresentationParams.Area getFullArea(); method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSuggestionArea(); - field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2 - field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4 - field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8 - field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1 - field public static final int FLAG_HOST_IME = 16; // 0x10 - field public static final int FLAG_HOST_SYSTEM = 32; // 0x20 } public abstract static class PresentationParams.Area { method @NonNull public android.graphics.Rect getBounds(); - method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSubArea(@NonNull android.graphics.Rect); } } @@ -6974,6 +6976,7 @@ package android.telecom { field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT"; field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT"; field public static final String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE"; + field public static final String EXTRA_IS_USER_INTENT_EMERGENCY_CALL = "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL"; field public static final int TTY_MODE_FULL = 1; // 0x1 field public static final int TTY_MODE_HCO = 2; // 0x2 field public static final int TTY_MODE_OFF = 0; // 0x0 @@ -7860,7 +7863,7 @@ package android.telephony { method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle); method public boolean needsOtaServiceProvisioning(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio(); - method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback); + method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig(); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>); @@ -8248,6 +8251,7 @@ package android.telephony.ims { method public int getServiceType(); method public static int getVideoStateFromCallType(int); method public static int getVideoStateFromImsCallProfile(android.telephony.ims.ImsCallProfile); + method public boolean hasKnownUserIntentEmergency(); method public boolean isEmergencyCallTesting(); method public boolean isVideoCall(); method public boolean isVideoPaused(); @@ -8260,6 +8264,7 @@ package android.telephony.ims { method public void setEmergencyCallTesting(boolean); method public void setEmergencyServiceCategories(int); method public void setEmergencyUrns(java.util.List<java.lang.String>); + method public void setHasKnownUserIntentEmergency(boolean); method public void updateCallExtras(android.telephony.ims.ImsCallProfile); method public void updateCallType(android.telephony.ims.ImsCallProfile); method public void updateMediaProfile(android.telephony.ims.ImsCallProfile); @@ -9324,6 +9329,7 @@ package android.view.contentcapture { public final class ContentCaptureEvent implements android.os.Parcelable { method public int describeContents(); + method @Nullable public android.view.contentcapture.ContentCaptureContext getContentCaptureContext(); method public long getEventTime(); method @Nullable public android.view.autofill.AutofillId getId(); method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds(); @@ -9332,6 +9338,7 @@ package android.view.contentcapture { method @Nullable public android.view.contentcapture.ViewNode getViewNode(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR; + field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6 field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5 field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4 field public static final int TYPE_VIEW_APPEARED = 1; // 0x1 diff --git a/api/test-current.txt b/api/test-current.txt index 1a7e4cb83c52..47d38a7d2f3a 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -996,6 +996,7 @@ package android.net { method public int[] getCapabilities(); method public int[] getTransportTypes(); method public boolean satisfiedByNetworkCapabilities(android.net.NetworkCapabilities); + field public static final int TRANSPORT_TEST = 7; // 0x7 } public class NetworkStack { @@ -1815,6 +1816,7 @@ package android.provider { public final class Settings { field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS"; + field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE"; field public static final int RESET_MODE_PACKAGE_DEFAULTS = 1; // 0x1 } @@ -2073,20 +2075,11 @@ package android.service.autofill.augmented { } public abstract class PresentationParams { - method public int getFlags(); - method @Nullable public android.service.autofill.augmented.PresentationParams.Area getFullArea(); method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSuggestionArea(); - field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2 - field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4 - field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8 - field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1 - field public static final int FLAG_HOST_IME = 16; // 0x10 - field public static final int FLAG_HOST_SYSTEM = 32; // 0x20 } public abstract static class PresentationParams.Area { method @NonNull public android.graphics.Rect getBounds(); - method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSubArea(@NonNull android.graphics.Rect); } } @@ -2725,6 +2718,7 @@ package android.view.contentcapture { public final class ContentCaptureEvent implements android.os.Parcelable { method public int describeContents(); + method @Nullable public android.view.contentcapture.ContentCaptureContext getContentCaptureContext(); method public long getEventTime(); method @Nullable public android.view.autofill.AutofillId getId(); method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds(); @@ -2733,6 +2727,7 @@ package android.view.contentcapture { method @Nullable public android.view.contentcapture.ViewNode getViewNode(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR; + field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6 field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5 field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4 field public static final int TYPE_VIEW_APPEARED = 1; // 0x1 diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index faf2053835fa..f4086557870d 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -76,6 +76,7 @@ cc_defaults { "src/external/SubsystemSleepStatePuller.cpp", "src/external/PowerStatsPuller.cpp", "src/external/ResourceHealthManagerPuller.cpp", + "src/external/TrainInfoPuller.cpp", "src/external/StatsPullerManager.cpp", "src/external/puller_util.cpp", "src/logd/LogEvent.cpp", @@ -238,6 +239,7 @@ cc_test { "tests/guardrail/StatsdStats_test.cpp", "tests/metrics/metrics_test_helper.cpp", "tests/statsd_test_util.cpp", + "tests/storage/StorageManager_test.cpp", "tests/e2e/WakelockDuration_e2e_test.cpp", "tests/e2e/MetricActivation_e2e_test.cpp", "tests/e2e/MetricConditionLink_e2e_test.cpp", diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index b478fed49a54..fb603b9ce163 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -31,6 +31,7 @@ #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/PermissionController.h> +#include <cutils/multiuser.h> #include <dirent.h> #include <frameworks/base/cmds/statsd/src/statsd_config.pb.h> #include <private/android_filesystem_config.h> @@ -47,6 +48,7 @@ using namespace android; using android::base::StringPrintf; using android::util::FIELD_COUNT_REPEATED; +using android::util::FIELD_TYPE_INT64; using android::util::FIELD_TYPE_MESSAGE; namespace android { @@ -62,6 +64,8 @@ constexpr const char* kOpUsage = "android:get_usage_stats"; // for StatsDataDumpProto const int FIELD_ID_REPORTS_LIST = 1; +// for TrainInfo experiment id serialization +const int FIELD_ID_EXPERIMENT_ID = 1; static binder::Status ok() { return binder::Status::ok(); @@ -1167,6 +1171,56 @@ Status StatsService::unregisterPullerCallback(int32_t atomTag, const String16& p return Status::ok(); } +Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& trainName, + int64_t trainVersionCode, int options, + int32_t state, + const std::vector<int64_t>& experimentIds) { + uid_t uid = IPCThreadState::self()->getCallingUid(); + // For testing + if (uid == AID_ROOT || uid == AID_SYSTEM || uid == AID_SHELL) { + return ok(); + } + + // Caller must be granted these permissions + if (!checkCallingPermission(String16(kPermissionDump))) { + return exception(binder::Status::EX_SECURITY, + StringPrintf("UID %d lacks permission %s", uid, kPermissionDump)); + } + if (!checkCallingPermission(String16(kPermissionUsage))) { + return exception(binder::Status::EX_SECURITY, + StringPrintf("UID %d lacks permission %s", uid, kPermissionUsage)); + } + // TODO: add verifier permission + + userid_t userId = multiuser_get_user_id(uid); + + bool requiresStaging = options | IStatsManager::FLAG_REQUIRE_STAGING; + bool rollbackEnabled = options | IStatsManager::FLAG_ROLLBACK_ENABLED; + bool requiresLowLatencyMonitor = options | IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR; + + ProtoOutputStream proto; + for (const auto& expId : experimentIds) { + proto.write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_EXPERIMENT_ID, + (long long)expId); + } + + vector<uint8_t> buffer; + buffer.resize(proto.size()); + size_t pos = 0; + auto iter = proto.data(); + while (iter.readBuffer() != NULL) { + size_t toRead = iter.currentToRead(); + std::memcpy(&(buffer[pos]), iter.readBuffer(), toRead); + pos += toRead; + iter.rp()->move(toRead); + } + LogEvent event(std::string(String8(trainName).string()), trainVersionCode, requiresStaging, + rollbackEnabled, requiresLowLatencyMonitor, state, buffer, userId); + mProcessor->OnLogEvent(&event); + StorageManager::writeTrainInfo(trainVersionCode, buffer); + return Status::ok(); +} + hardware::Return<void> StatsService::reportSpeakerImpedance( const SpeakerImpedance& speakerImpedance) { LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), speakerImpedance); diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 7f10d74ec7d6..d24565a63054 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -31,6 +31,7 @@ #include <android/frameworks/stats/1.0/types.h> #include <android/os/BnStatsManager.h> #include <android/os/IStatsCompanionService.h> +#include <android/os/IStatsManager.h> #include <binder/IResultReceiver.h> #include <utils/Looper.h> @@ -186,6 +187,13 @@ public: virtual Status unregisterPullerCallback(int32_t atomTag, const String16& packageName) override; /** + * Binder call to log BinaryPushStateChanged atom. + */ + virtual Status sendBinaryPushStateChangedAtom( + const android::String16& trainName, int64_t trainVersionCode, int options, + int32_t state, const std::vector<int64_t>& experimentIds) override; + + /** * Binder call to get SpeakerImpedance atom. */ virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override; diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 29f67c7e6f9b..873b772d06ea 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -3156,6 +3156,13 @@ message FlagFlipUpdateOccurred { optional int64 order_id = 2; } +/** + * Potential experiment ids that goes with a train install. + */ +message TrainExperimentIds { + repeated int64 experiment_id = 1; +} + /* * Logs when a binary push state changes. * Logged by the installer via public api. @@ -3182,8 +3189,14 @@ message BinaryPushStateChanged { INSTALL_FAILURE = 6; INSTALL_CANCELLED = 7; INSTALLER_ROLLBACK_REQUESTED = 8; + INSTALLER_ROLLBACK_SUCCESS = 9; + INSTALLER_ROLLBACK_FAILURE = 10; } optional State state = 6; + // Possible experiment ids for monitoring this push. + optional TrainExperimentIds experiment_ids = 7 [(log_mode) = MODE_BYTES]; + // user id + optional int32 user_id = 8; } /** Represents USB port overheat event. */ @@ -5511,13 +5524,6 @@ message DeviceIdentifierAccessDenied { } /** - * Potential experiment ids that goes with a train install. - */ -message TrainExperimentIds { - repeated int64 experiment_id = 1; -} - -/** * Pulls the ongoing mainline install train version code. * Pulled from StatsCompanionService */ diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index ed72b2914a34..98f810fd9a2c 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -29,10 +29,11 @@ #include "../statscompanion_util.h" #include "PowerStatsPuller.h" #include "ResourceHealthManagerPuller.h" -#include "StatsCompanionServicePuller.h" #include "StatsCallbackPuller.h" +#include "StatsCompanionServicePuller.h" #include "StatsPullerManager.h" #include "SubsystemSleepStatePuller.h" +#include "TrainInfoPuller.h" #include "statslog.h" #include <iostream> @@ -152,7 +153,7 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}}, // temperature {android::util::TEMPERATURE, - {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}}, + {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}}, // binder_calls {android::util::BINDER_CALLS, {.additiveFields = {4, 5, 6, 8, 12}, @@ -231,6 +232,8 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { // PermissionState. {android::util::DANGEROUS_PERMISSION_STATE, {.puller = new StatsCompanionServicePuller(android::util::DANGEROUS_PERMISSION_STATE)}}, + // TrainInfo. + {android::util::TRAIN_INFO, {.puller = new TrainInfoPuller()}}, }; StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) { diff --git a/cmds/statsd/src/external/TrainInfoPuller.cpp b/cmds/statsd/src/external/TrainInfoPuller.cpp new file mode 100644 index 000000000000..9d0924297912 --- /dev/null +++ b/cmds/statsd/src/external/TrainInfoPuller.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define DEBUG false // STOPSHIP if true +#include "Log.h" + +#include "external/StatsPuller.h" + +#include "TrainInfoPuller.h" +#include "logd/LogEvent.h" +#include "stats_log_util.h" +#include "statslog.h" +#include "storage/StorageManager.h" + +using std::make_shared; +using std::shared_ptr; + +namespace android { +namespace os { +namespace statsd { + +TrainInfoPuller::TrainInfoPuller() : + StatsPuller(android::util::TRAIN_INFO) { +} + +bool TrainInfoPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { + InstallTrainInfo trainInfo; + bool ret = StorageManager::readTrainInfo(trainInfo); + if (!ret) { + ALOGW("Failed to read train info."); + return false; + } + auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo); + data->push_back(event); + return true; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/TrainInfoPuller.h b/cmds/statsd/src/external/TrainInfoPuller.h new file mode 100644 index 000000000000..615d02351fd3 --- /dev/null +++ b/cmds/statsd/src/external/TrainInfoPuller.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "StatsPuller.h" + +namespace android { +namespace os { +namespace statsd { + +/** + * Reads train info from disk. + */ +class TrainInfoPuller : public StatsPuller { + public: + TrainInfoPuller(); + + private: + bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override; +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index b433c41518cc..d661ee8651be 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -478,6 +478,11 @@ void StatsdStats::noteBucketDropped(int64_t metricId) { getAtomMetricStats(metricId).bucketDropped++; } +void StatsdStats::noteBucketUnknownCondition(int64_t metricId) { + lock_guard<std::mutex> lock(mLock); + getAtomMetricStats(metricId).bucketUnknownCondition++; +} + void StatsdStats::noteConditionChangeInNextBucket(int64_t metricId) { lock_guard<std::mutex> lock(mLock); getAtomMetricStats(metricId).conditionChangeInNextBucket++; @@ -497,7 +502,7 @@ void StatsdStats::noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayN std::min(pullStats.minBucketBoundaryDelayNs, timeDelayNs); } -StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int metricId) { +StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int64_t metricId) { auto atomMetricStatsIter = mAtomMetricStats.find(metricId); if (atomMetricStatsIter != mAtomMetricStats.end()) { return atomMetricStatsIter->second; diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 5275c8f34fd0..e039be2b4395 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -409,6 +409,11 @@ public: void noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs); /** + * Number of buckets with unknown condition. + */ + void noteBucketUnknownCondition(int64_t metricId); + + /** * Reset the historical stats. Including all stats in icebox, and the tracked stats about * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue * to collect stats after reset() has been called. @@ -458,6 +463,7 @@ public: long bucketDropped = 0; int64_t minBucketBoundaryDelayNs = 0; int64_t maxBucketBoundaryDelayNs = 0; + long bucketUnknownCondition = 0; } AtomMetricStats; private: @@ -488,7 +494,7 @@ private: std::map<int, PulledAtomStats> mPulledAtomStats; // Maps metric ID to its stats. The size is capped by the number of metrics. - std::map<int, AtomMetricStats> mAtomMetricStats; + std::map<int64_t, AtomMetricStats> mAtomMetricStats; struct LogLossStats { LogLossStats(int32_t sec, int32_t count, int32_t error) @@ -532,7 +538,7 @@ private: * Get a reference to AtomMetricStats for a metric. If none exists, create it. The reference * will live as long as `this`. */ - StatsdStats::AtomMetricStats& getAtomMetricStats(int metricId); + StatsdStats::AtomMetricStats& getAtomMetricStats(int64_t metricId); FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd); FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd); diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 40a4070d2974..dec36b54a1ce 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -20,6 +20,8 @@ #include "stats_log_util.h" #include "statslog.h" +#include <binder/IPCThreadState.h> + namespace android { namespace os { namespace statsd { @@ -180,6 +182,25 @@ LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedT } } +LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requiresStaging, + bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state, + const std::vector<uint8_t>& experimentIds, int32_t userId) { + mLogdTimestampNs = getWallClockNs(); + mElapsedTimestampNs = getElapsedRealtimeNs(); + mTagId = android::util::BINARY_PUSH_STATE_CHANGED; + mLogUid = android::IPCThreadState::self()->getCallingUid(); + + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value((int)requiresStaging))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value((int)rollbackEnabled))); + mValues.push_back( + FieldValue(Field(mTagId, getSimpleField(5)), Value((int)requiresLowLatencyMonitor))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(6)), Value(state))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(7)), Value(experimentIds))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(8)), Value(userId))); +} + LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const SpeakerImpedance& speakerImpedance) { mLogdTimestampNs = wallClockTimestampNs; @@ -340,6 +361,16 @@ LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, } } +LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, + const InstallTrainInfo& trainInfo) { + mLogdTimestampNs = wallClockTimestampNs; + mElapsedTimestampNs = elapsedTimestampNs; + mTagId = android::util::TRAIN_INFO; + mValues.push_back( + FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainInfo.experimentIds))); +} + LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) : LogEvent(tagId, timestampNs, 0) {} LogEvent::LogEvent(int32_t tagId, int64_t timestampNs, int32_t uid) { diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 784376a1580c..111a619760df 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -55,6 +55,11 @@ struct AttributionNodeInternal { int32_t mUid; std::string mTag; }; + +struct InstallTrainInfo { + int64_t trainVersionCode; + std::vector<uint8_t> experimentIds; +}; /** * Wrapper for the log_msg structure. */ @@ -97,6 +102,11 @@ public: const std::map<int32_t, std::string>& string_map, const std::map<int32_t, float>& float_map); + // Constructs a BinaryPushStateChanged LogEvent from API call. + explicit LogEvent(const std::string& trainName, int64_t trainVersionCode, bool requiresStaging, + bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state, + const std::vector<uint8_t>& experimentIds, int32_t userId); + explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const SpeakerImpedance& speakerImpedance); @@ -127,6 +137,9 @@ public: explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const VendorAtom& vendorAtom); + explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, + const InstallTrainInfo& installTrainInfo); + ~LogEvent(); /** diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 350745b2c326..5707de544648 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -250,7 +250,7 @@ void CountMetricProducer::dropDataLocked(const int64_t dropTimeNs) { void CountMetricProducer::onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) { VLOG("Metric %lld onConditionChanged", (long long)mMetricId); - mCondition = conditionMet; + mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; } bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 6c1c47bbc093..da6b97cc4e59 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -433,7 +433,7 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondit void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) { VLOG("Metric %lld onConditionChanged", (long long)mMetricId); - mCondition = conditionMet; + mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; flushIfNeededLocked(eventTime); for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { for (auto& pair : whatIt.second) { @@ -767,12 +767,13 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, !mSameConditionDimensionsInTracker, !mHasLinksToAllConditionDimensionsInTracker, &dimensionKeysInCondition); - condition = (conditionState == ConditionState::kTrue); + condition = conditionState == ConditionState::kTrue; if (mDimensionsInCondition.empty() && condition) { dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY); } } else { - condition = mCondition; + // TODO: The unknown condition state is not handled here, we should fix it. + condition = mCondition == ConditionState::kTrue; if (condition) { dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY); } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 1b830a3f69f5..ec561b527025 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -137,6 +137,7 @@ private: FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition); FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition); + FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState); FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade); FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket); FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade); diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index 7e695a69988f..3b4af6533e34 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -132,7 +132,7 @@ void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, void EventMetricProducer::onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) { VLOG("Metric %lld onConditionChanged", (long long)mMetricId); - mCondition = conditionMet; + mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; } void EventMetricProducer::onMatchedLogEventInternalLocked( diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index d56a355f15d6..837d532f7e09 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -320,15 +320,15 @@ void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) { // When the metric wants to do random sampling and there is already one gauge atom for the // current bucket, do not do it again. case GaugeMetric::RANDOM_ONE_SAMPLE: { - triggerPuller = mCondition && mCurrentSlicedBucket->empty(); + triggerPuller = mCondition == ConditionState::kTrue && mCurrentSlicedBucket->empty(); break; } case GaugeMetric::CONDITION_CHANGE_TO_TRUE: { - triggerPuller = mCondition; + triggerPuller = mCondition == ConditionState::kTrue; break; } case GaugeMetric::FIRST_N_SAMPLES: { - triggerPuller = mCondition; + triggerPuller = mCondition == ConditionState::kTrue; break; } default: @@ -364,7 +364,7 @@ void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet, const int64_t eventTimeNs) { VLOG("GaugeMetric %lld onConditionChanged", (long long)mMetricId); flushIfNeededLocked(eventTimeNs); - mCondition = conditionMet; + mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; if (mIsPulled && mTriggerAtomId == -1) { pullAndMatchEventsLocked(eventTimeNs); } // else: Push mode. No need to proactively pull the gauge data. @@ -377,7 +377,7 @@ void GaugeMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition flushIfNeededLocked(eventTimeNs); // If the condition is sliced, mCondition is true if any of the dimensions is true. And we will // pull for every dimension. - mCondition = overallCondition; + mCondition = overallCondition ? ConditionState::kTrue : ConditionState::kFalse; if (mIsPulled && mTriggerAtomId == -1) { pullAndMatchEventsLocked(eventTimeNs); } // else: Push mode. No need to proactively pull the gauge data. diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 11075685b7fa..4cf5333947d3 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -45,7 +45,8 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo &dimensionKeysInCondition); condition = (conditionState == ConditionState::kTrue); } else { - condition = mCondition; + // TODO: The unknown condition state is not handled here, we should fix it. + condition = mCondition == ConditionState::kTrue; } if (mDimensionsInCondition.empty() && condition) { @@ -107,7 +108,8 @@ void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedT if (it == mEventActivationMap.end()) { return; } - if (mActivationType == MetricActivation::ACTIVATE_ON_BOOT) { + if (mActivationType == MetricActivation::ACTIVATE_ON_BOOT && + it->second.state == ActivationState::kNotActive) { it->second.state = ActivationState::kActiveOnBoot; return; } diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 849cb76ec392..8ab3b0680276 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -59,7 +59,7 @@ public: mTimeBaseNs(timeBaseNs), mCurrentBucketStartTimeNs(timeBaseNs), mCurrentBucketNum(0), - mCondition(conditionIndex >= 0 ? false : true), + mCondition(conditionIndex >= 0 ? ConditionState::kUnknown : ConditionState::kTrue), mConditionSliced(false), mWizard(wizard), mConditionTrackerIndex(conditionIndex), @@ -315,7 +315,7 @@ protected: int64_t mBucketSizeNs; - bool mCondition; + ConditionState mCondition; bool mConditionSliced; diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 851ae9962d09..bc7c8727284f 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -154,7 +154,7 @@ ValueMetricProducer::ValueMetricProducer( // Adjust start for partial bucket mCurrentBucketStartTimeNs = startTimeNs; // Kicks off the puller immediately if condition is true and diff based. - if (mIsPulled && mCondition && mUseDiff) { + if (mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) { pullAndMatchEventsLocked(startTimeNs); } VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), @@ -341,17 +341,21 @@ void ValueMetricProducer::onConditionChangedLocked(const bool condition, flushIfNeededLocked(eventTimeNs); // Pull on condition changes. - if (mIsPulled && (mCondition != condition)) { + bool conditionChanged = mCondition != condition; + bool unknownToFalse = mCondition == ConditionState::kUnknown + && condition == ConditionState::kFalse; + // We do not need to pull when we go from unknown to false. + if (mIsPulled && conditionChanged && !unknownToFalse) { pullAndMatchEventsLocked(eventTimeNs); } // when condition change from true to false, clear diff base but don't // reset other counters as we may accumulate more value in the bucket. - if (mUseDiff && mCondition && !condition) { + if (mUseDiff && mCondition == ConditionState::kTrue && condition == ConditionState::kFalse) { resetBase(); } - mCondition = condition; + mCondition = condition ? ConditionState::kTrue : ConditionState::kFalse; } void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) { @@ -372,7 +376,7 @@ int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTime void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData, bool pullSuccess, int64_t originalPullTimeNs) { std::lock_guard<std::mutex> lock(mMutex); - if (mCondition) { + if (mCondition == ConditionState::kTrue) { if (!pullSuccess) { // If the pull failed, we won't be able to compute a diff. invalidateCurrentBucket(); @@ -725,6 +729,10 @@ void ValueMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) { } void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs) { + if (mCondition == ConditionState::kUnknown) { + StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId); + } + VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs, (int)mCurrentSlicedBucket.size()); int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs(); diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index f7428a50b3da..09b8fed60dcc 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -425,6 +425,7 @@ message StatsdStatsReport { optional int64 bucket_dropped = 8; optional int64 min_bucket_boundary_delay_ns = 9; optional int64 max_bucket_boundary_delay_ns = 10; + optional int64 bucket_unknown_condition = 11; } repeated AtomMetricStats atom_metric_stats = 17; diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp index 01043a2143f0..ca645e186614 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -84,6 +84,7 @@ const int FIELD_ID_INVALIDATED_BUCKET = 7; const int FIELD_ID_BUCKET_DROPPED = 8; const int FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS = 9; const int FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS = 10; +const int FIELD_ID_BUCKET_UNKNOWN_CONDITION = 11; namespace { @@ -489,11 +490,11 @@ void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats> protoOutput->end(token); } -void writeAtomMetricStatsToStream(const std::pair<int, StatsdStats::AtomMetricStats> &pair, +void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair, util::ProtoOutputStream *protoOutput) { uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_METRIC_STATS | FIELD_COUNT_REPEATED); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (int32_t)pair.first); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (long long)pair.first); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_HARD_DIMENSION_LIMIT_REACHED, (long long)pair.second.hardDimensionLimitReached); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_LATE_LOG_EVENT_SKIPPED, @@ -512,6 +513,8 @@ void writeAtomMetricStatsToStream(const std::pair<int, StatsdStats::AtomMetricSt (long long)pair.second.minBucketBoundaryDelayNs); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS, (long long)pair.second.maxBucketBoundaryDelayNs); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_UNKNOWN_CONDITION, + (long long)pair.second.bucketUnknownCondition); protoOutput->end(token); } diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h index dcea0e653e22..59d4865f8977 100644 --- a/cmds/statsd/src/stats_log_util.h +++ b/cmds/statsd/src/stats_log_util.h @@ -74,7 +74,7 @@ void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats> util::ProtoOutputStream* protoOutput); // Helper function to write AtomMetricStats to ProtoOutputStream -void writeAtomMetricStatsToStream(const std::pair<int, StatsdStats::AtomMetricStats> &pair, +void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair, util::ProtoOutputStream *protoOutput); template<class T> diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp index 90f641a34b85..165e57c73743 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -38,10 +38,13 @@ using std::map; #define STATS_DATA_DIR "/data/misc/stats-data" #define STATS_SERVICE_DIR "/data/misc/stats-service" +#define TRAIN_INFO_DIR "/data/misc/train-info" // for ConfigMetricsReportList const int FIELD_ID_REPORTS = 2; +std::mutex StorageManager::sTrainInfoMutex; + using android::base::StringPrintf; using std::unique_ptr; @@ -92,6 +95,71 @@ void StorageManager::writeFile(const char* file, const void* buffer, int numByte close(fd); } +bool StorageManager::writeTrainInfo(int64_t trainVersionCode, + const std::vector<uint8_t>& experimentIds) { + std::lock_guard<std::mutex> lock(sTrainInfoMutex); + + deleteAllFiles(TRAIN_INFO_DIR); + + string file_name = StringPrintf("%s/%lld", TRAIN_INFO_DIR, (long long)trainVersionCode); + + int fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); + if (fd == -1) { + VLOG("Attempt to access %s but failed", file_name.c_str()); + return false; + } + + size_t result = write(fd, experimentIds.data(), experimentIds.size()); + if (result == experimentIds.size()) { + VLOG("Successfully wrote %s", file_name.c_str()); + } else { + VLOG("Failed to write %s", file_name.c_str()); + return false; + } + + result = fchown(fd, AID_STATSD, AID_STATSD); + if (result) { + VLOG("Failed to chown %s to statsd", file_name.c_str()); + return false; + } + + close(fd); + return true; +} + +bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) { + std::lock_guard<std::mutex> lock(sTrainInfoMutex); + + unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir); + + if (dir == NULL) { + VLOG("Directory does not exist: %s", TRAIN_INFO_DIR); + return false; + } + + dirent* de; + while ((de = readdir(dir.get()))) { + char* name = de->d_name; + if (name[0] == '.') { + continue; + } + trainInfo.trainVersionCode = StrToInt64(name); + string fullPath = StringPrintf("%s/%s", TRAIN_INFO_DIR, name); + int fd = open(fullPath.c_str(), O_RDONLY | O_CLOEXEC); + if (fd != -1) { + string str; + if (android::base::ReadFdToString(fd, &str)) { + close(fd); + std::copy(str.begin(), str.end(), std::back_inserter(trainInfo.experimentIds)); + VLOG("Read train info file successful: %s", fullPath.c_str()); + return true; + } + } + close(fd); + } + return false; +} + void StorageManager::deleteFile(const char* file) { if (remove(file) != 0) { VLOG("Attempt to delete %s but is not found", file); diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h index dcf3bb607380..d6df8674e59b 100644 --- a/cmds/statsd/src/storage/StorageManager.h +++ b/cmds/statsd/src/storage/StorageManager.h @@ -29,6 +29,11 @@ namespace statsd { using android::util::ProtoOutputStream; +struct TrainInfo { + int64_t trainVersionCode; + std::vector<uint8_t> experimentIds; +}; + class StorageManager : public virtual RefBase { public: /** @@ -37,6 +42,16 @@ public: static void writeFile(const char* file, const void* buffer, int numBytes); /** + * Writes train info. + */ + static bool writeTrainInfo(int64_t trainVersionCode, const std::vector<uint8_t>& experimentIds); + + /** + * Reads train info. + */ + static bool readTrainInfo(InstallTrainInfo& trainInfo); + + /** * Reads the file content to the buffer. */ static bool readFileToString(const char* file, string* content); @@ -109,6 +124,8 @@ private: * Prints disk usage statistics about a directory related to statsd. */ static void printDirStats(int out, const char* path); + + static std::mutex sTrainInfoMutex; }; } // namespace statsd diff --git a/cmds/statsd/statsd.rc b/cmds/statsd/statsd.rc index e0cbd5d25716..a98ecd586b42 100644 --- a/cmds/statsd/statsd.rc +++ b/cmds/statsd/statsd.rc @@ -27,3 +27,4 @@ on post-fs-data mkdir /data/misc/stats-data/ 0770 statsd system mkdir /data/misc/stats-service/ 0770 statsd system mkdir /data/misc/stats-active-metric/ 0770 statsd system + mkdir /data/misc/train-info/ 0770 statsd system diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index 5f3aae3ab93a..4579ca6008ef 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -297,7 +297,8 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { // Setup a simple config, no activation StatsdConfig config1; - config1.set_id(12341); + int64_t cfgId1 = 12341; + config1.set_id(cfgId1); config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); *config1.add_atom_matcher() = wakelockAcquireMatcher; @@ -314,14 +315,12 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { countMetric2->set_what(wakelockAcquireMatcher.id()); countMetric2->set_bucket(FIVE_MINUTES); - ConfigKey cfgKey1(uid, 12341); - long timeBase1 = 1; - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); + ConfigKey cfgKey1(uid, cfgId1); // Add another config, with two metrics, one with activation StatsdConfig config2; - config2.set_id(12342); + int64_t cfgId2 = 12342; + config2.set_id(cfgId2); config2.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. *config2.add_atom_matcher() = wakelockAcquireMatcher; @@ -344,11 +343,12 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); metric3ActivationTrigger->set_ttl_seconds(100); - ConfigKey cfgKey2(uid, 12342); + ConfigKey cfgKey2(uid, cfgId2); // Add another config, with two metrics, both with activations StatsdConfig config3; - config3.set_id(12342); + int64_t cfgId3 = 12343; + config3.set_id(cfgId3); config3.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. *config3.add_atom_matcher() = wakelockAcquireMatcher; @@ -376,14 +376,37 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); metric6ActivationTrigger->set_ttl_seconds(200); - ConfigKey cfgKey3(uid, 12343); + ConfigKey cfgKey3(uid, cfgId3); - processor->OnConfigUpdated(2, cfgKey2, config2); - processor->OnConfigUpdated(3, cfgKey3, config3); + sp<UidMap> m = new UidMap(); + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> subscriberAlarmMonitor; + vector<int64_t> activeConfigsBroadcast; - EXPECT_EQ(3, processor->mMetricsManagers.size()); - auto it = processor->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor->mMetricsManagers.end()); + long timeBase1 = 1; + int broadcastCount = 0; + StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, + timeBase1, [](const ConfigKey& key) { return true; }, + [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, + const vector<int64_t>& activeConfigs) { + broadcastCount++; + EXPECT_EQ(broadcastUid, uid); + activeConfigsBroadcast.clear(); + activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), + activeConfigs.begin(), activeConfigs.end()); + return true; + }); + + processor.OnConfigUpdated(1, cfgKey1, config1); + processor.OnConfigUpdated(2, cfgKey2, config2); + processor.OnConfigUpdated(3, cfgKey3, config3); + + EXPECT_EQ(3, processor.mMetricsManagers.size()); + + // Expect the first config and both metrics in it to be active. + auto it = processor.mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor.mMetricsManagers.end()); auto& metricsManager1 = it->second; EXPECT_TRUE(metricsManager1->isActive()); @@ -407,8 +430,9 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto& metricProducer2 = *metricIt; EXPECT_TRUE(metricProducer2->isActive()); - it = processor->mMetricsManagers.find(cfgKey2); - EXPECT_TRUE(it != processor->mMetricsManagers.end()); + // Expect config 2 to be active. Metric 3 shouldn't be active, metric 4 should be active. + it = processor.mMetricsManagers.find(cfgKey2); + EXPECT_TRUE(it != processor.mMetricsManagers.end()); auto& metricsManager2 = it->second; EXPECT_TRUE(metricsManager2->isActive()); @@ -432,8 +456,9 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto& metricProducer4 = *metricIt; EXPECT_TRUE(metricProducer4->isActive()); - it = processor->mMetricsManagers.find(cfgKey3); - EXPECT_TRUE(it != processor->mMetricsManagers.end()); + // Expect the third config and both metrics in it to be inactive. + it = processor.mMetricsManagers.find(cfgKey3); + EXPECT_TRUE(it != processor.mMetricsManagers.end()); auto& metricsManager3 = it->second; EXPECT_FALSE(metricsManager3->isActive()); @@ -457,10 +482,30 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto& metricProducer6 = *metricIt; EXPECT_FALSE(metricProducer6->isActive()); + // No broadcast for active configs should have happened yet. + EXPECT_EQ(broadcastCount, 0); + + // Activate all 3 metrics that were not active. std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + // Assert that all 3 configs are active. + EXPECT_TRUE(metricsManager1->isActive()); + EXPECT_TRUE(metricsManager2->isActive()); + EXPECT_TRUE(metricsManager3->isActive()); + + // A broadcast should have happened, and all 3 configs should be active in the broadcast. + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 3); + EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId1) + != activeConfigsBroadcast.end()); + EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId2) + != activeConfigsBroadcast.end()); + EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId3) + != activeConfigsBroadcast.end()); + + // When we shut down, metrics 3 & 5 have 100ns remaining, metric 6 has 100s + 100ns. int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; EXPECT_TRUE(metricProducer3->isActive()); int64_t ttl3 = metricProducer3->getRemainingTtlNs(shutDownTime); @@ -472,8 +517,9 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { int64_t ttl6 = metricProducer6->getRemainingTtlNs(shutDownTime); EXPECT_EQ(100 + 100 * NS_PER_SEC, ttl6); - processor->WriteMetricsActivationToDisk(timeBase1 + 100 * NS_PER_SEC); + processor.WriteMetricsActivationToDisk(shutDownTime); + // Create a second StatsLogProcessor and push the same 3 configs. long timeBase2 = 1000; sp<StatsLogProcessor> processor2 = CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); @@ -481,6 +527,8 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { processor2->OnConfigUpdated(timeBase2, cfgKey3, config3); EXPECT_EQ(3, processor2->mMetricsManagers.size()); + + // First config and both metrics are active. it = processor2->mMetricsManagers.find(cfgKey1); EXPECT_TRUE(it != processor2->mMetricsManagers.end()); auto& metricsManager1001 = it->second; @@ -506,6 +554,7 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto& metricProducer1002 = *metricIt; EXPECT_TRUE(metricProducer1002->isActive()); + // Second config is active. Metric 3 is inactive, metric 4 is active. it = processor2->mMetricsManagers.find(cfgKey2); EXPECT_TRUE(it != processor2->mMetricsManagers.end()); auto& metricsManager1002 = it->second; @@ -531,6 +580,7 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto& metricProducer1004 = *metricIt; EXPECT_TRUE(metricProducer1004->isActive()); + // Config 3 is inactive. both metrics are inactive. it = processor2->mMetricsManagers.find(cfgKey3); EXPECT_TRUE(it != processor2->mMetricsManagers.end()); auto& metricsManager1003 = it->second; @@ -557,6 +607,7 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto& metricProducer1006 = *metricIt; EXPECT_FALSE(metricProducer1006->isActive()); + // Assert that all 3 metrics with activation are inactive and that the ttls were properly set. EXPECT_FALSE(metricProducer1003->isActive()); const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second; EXPECT_EQ(100 * NS_PER_SEC, activation1003.ttl_ns); @@ -572,12 +623,16 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { processor2->LoadMetricsActivationFromDisk(); + // After loading activations from disk, assert that all 3 metrics are active. EXPECT_TRUE(metricProducer1003->isActive()); EXPECT_EQ(timeBase2 + ttl3 - activation1003.ttl_ns, activation1003.activation_ns); EXPECT_TRUE(metricProducer1005->isActive()); EXPECT_EQ(timeBase2 + ttl5 - activation1005.ttl_ns, activation1005.activation_ns); EXPECT_TRUE(metricProducer1006->isActive()); EXPECT_EQ(timeBase2 + ttl6 - activation1006.ttl_ns, activation1003.activation_ns); + + // Make sure no more broadcasts have happened. + EXPECT_EQ(broadcastCount, 1); } TEST(StatsLogProcessorTest, TestActivationOnBoot) { diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp index 29e86f3f9456..85d8a5613a8b 100644 --- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp @@ -66,17 +66,42 @@ TEST(MetricActivationE2eTest, TestCountMetric) { auto config = CreateStatsdConfig(); int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; - - ConfigKey cfgKey; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - EXPECT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - sp<MetricProducer> metricProducer = - processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + sp<UidMap> m = new UidMap(); + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> subscriberAlarmMonitor; + vector<int64_t> activeConfigsBroadcast; + + long timeBase1 = 1; + int broadcastCount = 0; + StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, + bucketStartTimeNs, [](const ConfigKey& key) { return true; }, + [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, + const vector<int64_t>& activeConfigs) { + broadcastCount++; + EXPECT_EQ(broadcastUid, uid); + activeConfigsBroadcast.clear(); + activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), + activeConfigs.begin(), activeConfigs.end()); + return true; + }); + + processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); + + EXPECT_EQ(processor.mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; auto& eventActivationMap = metricProducer->mEventActivationMap; + EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); // Two activations: one is triggered by battery saver mode (tracker index 0), the other is // triggered by screen on event (tracker index 2). @@ -93,13 +118,19 @@ TEST(MetricActivationE2eTest, TestCountMetric) { std::unique_ptr<LogEvent> event; event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 0); // Activated by battery save mode. event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive); EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC); @@ -109,12 +140,13 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // First processed event. event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); // Activated by screen on event. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 20); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive); EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); @@ -126,7 +158,8 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // 2nd processed event. // The activation by screen_on event expires, but the one by battery save mode is still active. event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive); EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); @@ -134,15 +167,21 @@ TEST(MetricActivationE2eTest, TestCountMetric) { EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive); EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + 20); EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC); + // No new broadcast since the config should still be active. + EXPECT_EQ(broadcastCount, 1); // 3rd processed event. event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); // All activations expired. event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 2); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive); EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC); @@ -153,8 +192,12 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // Re-activate. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive); EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC); @@ -163,11 +206,11 @@ TEST(MetricActivationE2eTest, TestCountMetric) { EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC); event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); ConfigMetricsReportList reports; vector<uint8_t> buffer; - processor->onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, + processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, ADB_DUMP, &buffer); EXPECT_TRUE(buffer.size() > 0); EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index b54096441d3f..b294cad1802c 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -117,6 +117,7 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { DurationMetricProducer durationProducer( kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); + durationProducer.mCondition = ConditionState::kFalse; EXPECT_FALSE(durationProducer.mCondition); EXPECT_FALSE(durationProducer.isConditionSliced()); @@ -140,6 +141,51 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { EXPECT_EQ(1LL, buckets2[0].mDuration); } +TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) { + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_SUM); + + int tagId = 1; + LogEvent event1(tagId, bucketStartTimeNs + 1); + event1.init(); + LogEvent event2(tagId, bucketStartTimeNs + 2); + event2.init(); + LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1); + event3.init(); + LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3); + event4.init(); + + FieldMatcher dimensions; + DurationMetricProducer durationProducer( + kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); + + EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition); + EXPECT_FALSE(durationProducer.isConditionSliced()); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event3); + durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2); + durationProducer.onMatchedLogEvent(2 /* stop index*/, event4); + durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); + EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); + const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets2.size()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs); + EXPECT_EQ(1LL, buckets2[0].mDuration); +} + TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) { /** * The duration starts from the first bucket, through the two partial buckets (10-70sec), diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 572b1991f426..7e7ffeda8053 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -2176,7 +2176,7 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = true; + valueProducer.mCondition = ConditionState::kTrue; vector<shared_ptr<LogEvent>> allData; valueProducer.onDataPulled(allData, /** succeed */ false, bucketStartTimeNs); @@ -2226,7 +2226,7 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = false; + valueProducer.mCondition = ConditionState::kFalse; // Max delay is set to 0 so pull will exceed max delay. valueProducer.onConditionChanged(true, bucketStartTimeNs + 1); @@ -2257,7 +2257,7 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) { eventMatcherWizard, tagId, bucket2StartTimeNs, bucket2StartTimeNs, pullerManager); - valueProducer.mCondition = false; + valueProducer.mCondition = ConditionState::kFalse; // Event should be skipped since it is from previous bucket. // Pull should not be called. @@ -2299,7 +2299,7 @@ TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = false; + valueProducer.mCondition = ConditionState::kFalse; valueProducer.mHasGlobalBase = false; valueProducer.onConditionChanged(true, bucketStartTimeNs + 1); @@ -2351,7 +2351,7 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = true; + valueProducer.mCondition = ConditionState::kTrue; // Bucket start. vector<shared_ptr<LogEvent>> allData; @@ -2429,7 +2429,7 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = false; + valueProducer.mCondition = ConditionState::kFalse; valueProducer.onConditionChanged(true, bucket2StartTimeNs + 2); EXPECT_EQ(true, valueProducer.mCurrentBucketIsInvalid); EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); @@ -2481,7 +2481,7 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = true; + valueProducer.mCondition = ConditionState::kTrue; // Bucket start. vector<shared_ptr<LogEvent>> allData; @@ -2564,7 +2564,7 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = true; + valueProducer.mCondition = ConditionState::kTrue; // Bucket start. vector<shared_ptr<LogEvent>> allData; diff --git a/cmds/statsd/tests/storage/StorageManager_test.cpp b/cmds/statsd/tests/storage/StorageManager_test.cpp new file mode 100644 index 000000000000..f66de0518f80 --- /dev/null +++ b/cmds/statsd/tests/storage/StorageManager_test.cpp @@ -0,0 +1,52 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <stdio.h> +#include "src/storage/StorageManager.h" + +#ifdef __ANDROID__ + +namespace android { +namespace os { +namespace statsd { + +using namespace testing; +using std::make_shared; +using std::shared_ptr; +using std::vector; +using testing::Contains; + +TEST(StorageManagerTest, TrainInfoReadWriteTest) { + InstallTrainInfo trainInfo; + trainInfo.trainVersionCode = 12345; + const char* expIds = "test_ids"; + trainInfo.experimentIds.assign(expIds, expIds + strlen(expIds)); + + StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.experimentIds); + + InstallTrainInfo result; + StorageManager::readTrainInfo(result); + EXPECT_EQ(trainInfo.trainVersionCode, result.trainVersionCode); + EXPECT_EQ(trainInfo.experimentIds.size(), result.experimentIds.size()); + EXPECT_EQ(trainInfo.experimentIds, result.experimentIds); +} + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt index b72ce8977128..dc2ed4cc0d69 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-greylist.txt @@ -542,8 +542,6 @@ Landroid/net/IConnectivityManager;->getTetherableWifiRegexs()[Ljava/lang/String; Landroid/net/IConnectivityManager;->getTetheredIfaces()[Ljava/lang/String; Landroid/net/IConnectivityManager;->getTetheringErroredIfaces()[Ljava/lang/String; Landroid/net/IConnectivityManager;->startLegacyVpn(Lcom/android/internal/net/VpnProfile;)V -Landroid/net/INetd$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetd; -Landroid/net/INetd;->interfaceAddAddress(Ljava/lang/String;Ljava/lang/String;I)V Landroid/net/INetworkManagementEventObserver$Stub;-><init>()V Landroid/net/INetworkPolicyListener$Stub;-><init>()V Landroid/net/INetworkPolicyManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkPolicyManager; @@ -2825,7 +2823,6 @@ Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->reset()V Lcom/android/internal/telephony/GsmAlphabet$TextEncodingDetails;-><init>()V Lcom/android/internal/telephony/GsmCdmaCall;->attachFake(Lcom/android/internal/telephony/Connection;Lcom/android/internal/telephony/Call$State;)V Lcom/android/internal/telephony/GsmCdmaCallTracker;->clearDisconnected()V -Lcom/android/internal/telephony/GsmCdmaCallTracker;->dialThreeWay(Ljava/lang/String;)Lcom/android/internal/telephony/Connection; Lcom/android/internal/telephony/GsmCdmaCallTracker;->disableDataCallInEmergencyCall(Ljava/lang/String;)V Lcom/android/internal/telephony/GsmCdmaCallTracker;->fakeHoldForegroundBeforeDial()V Lcom/android/internal/telephony/GsmCdmaCallTracker;->getPhone()Lcom/android/internal/telephony/GsmCdmaPhone; diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index ca3c72627e3b..5d4f988c3630 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -55,6 +55,7 @@ import android.os.Bundle; import android.os.Debug; import android.os.Handler; import android.os.IBinder; +import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; @@ -68,6 +69,7 @@ import android.util.DisplayMetrics; import android.util.Singleton; import android.util.Size; +import com.android.internal.app.LocalePicker; import com.android.internal.app.procstats.ProcessStats; import com.android.internal.os.RoSystemProperties; import com.android.internal.os.TransferPipe; @@ -84,7 +86,9 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.Locale; /** * <p> @@ -3451,6 +3455,35 @@ public class ActivityManager { } /** + * Sets the current locales of the device. Calling app must have the permission + * {@code android.permission.CHANGE_CONFIGURATION} and + * {@code android.permission.WRITE_SETTINGS}. + * + * @hide + */ + @SystemApi + public void setDeviceLocales(@NonNull LocaleList locales) { + LocalePicker.updateLocales(locales); + } + + /** + * Returns a list of supported locales by this system. It includes all locales that are + * selectable by the user, potentially including locales that the framework does not have + * translated resources for. To get locales that the framework has translated resources for, use + * {@code Resources.getSystem().getAssets().getLocales()} instead. + * + * @hide + */ + @SystemApi + public @NonNull Collection<Locale> getSupportedLocales() { + ArrayList<Locale> locales = new ArrayList<>(); + for (String localeTag : LocalePicker.getSupportedLocales(mContext)) { + locales.add(Locale.forLanguageTag(localeTag)); + } + return locales; + } + + /** * Get the device configuration attributes. */ public ConfigurationInfo getDeviceConfigurationInfo() { diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 64b94a946489..f76f7b9a7fc7 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -848,6 +848,7 @@ public class AppOpsManager { /** @hide Has a legacy (non-isolated) view of storage. */ public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage"; /** @hide Interact with accessibility. */ + @SystemApi public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility"; // Warning: If an permission is added here it also has to be added to diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 1a728c12e138..6908ca27480c 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2101,7 +2101,8 @@ class ContextImpl extends Context { } private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName, - int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) { + int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo, + String[] overlayDirs) { final String[] splitResDirs; final ClassLoader classLoader; try { @@ -2113,7 +2114,7 @@ class ContextImpl extends Context { return ResourcesManager.getInstance().getResources(activityToken, pi.getResDir(), splitResDirs, - pi.getOverlayDirs(), + overlayDirs, pi.getApplicationInfo().sharedLibraryFiles, displayId, overrideConfig, @@ -2131,9 +2132,11 @@ class ContextImpl extends Context { new UserHandle(UserHandle.getUserId(application.uid)), flags, null, null); final int displayId = getDisplayId(); - + // overlayDirs is retrieved directly from ApplicationInfo since ActivityThread may have + // a LoadedApk containing Resources with stale overlays for a remote application. + final String[] overlayDirs = application.resourceDirs; c.setResources(createResources(mActivityToken, pi, null, displayId, null, - getDisplayAdjustments(displayId).getCompatibilityInfo())); + getDisplayAdjustments(displayId).getCompatibilityInfo(), overlayDirs)); if (c.mResources != null) { return c; } @@ -2168,7 +2171,7 @@ class ContextImpl extends Context { final int displayId = getDisplayId(); c.setResources(createResources(mActivityToken, pi, null, displayId, null, - getDisplayAdjustments(displayId).getCompatibilityInfo())); + getDisplayAdjustments(displayId).getCompatibilityInfo(), pi.getOverlayDirs())); if (c.mResources != null) { return c; } @@ -2218,7 +2221,8 @@ class ContextImpl extends Context { final int displayId = getDisplayId(); context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId, - overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo())); + overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(), + mPackageInfo.getOverlayDirs())); return context; } @@ -2233,7 +2237,8 @@ class ContextImpl extends Context { final int displayId = display.getDisplayId(); context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId, - null, getDisplayAdjustments(displayId).getCompatibilityInfo())); + null, getDisplayAdjustments(displayId).getCompatibilityInfo(), + mPackageInfo.getOverlayDirs())); context.mDisplay = display; return context; } @@ -2416,7 +2421,7 @@ class ContextImpl extends Context { ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null, null, null, 0, null, null); context.setResources(createResources(null, packageInfo, null, displayId, null, - packageInfo.getCompatibilityInfo())); + packageInfo.getCompatibilityInfo(), packageInfo.getOverlayDirs())); context.updateDisplay(displayId); return context; } diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 1878d8407738..077652cacc2d 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -42,12 +42,10 @@ import java.lang.annotation.RetentionPolicy; public class StatusBarManager { /** @hide */ - @SystemApi public static final int DISABLE_EXPAND = View.STATUS_BAR_DISABLE_EXPAND; /** @hide */ public static final int DISABLE_NOTIFICATION_ICONS = View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS; /** @hide */ - @SystemApi public static final int DISABLE_NOTIFICATION_ALERTS = View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS; @@ -59,17 +57,14 @@ public class StatusBarManager { /** @hide */ public static final int DISABLE_SYSTEM_INFO = View.STATUS_BAR_DISABLE_SYSTEM_INFO; /** @hide */ - @SystemApi public static final int DISABLE_HOME = View.STATUS_BAR_DISABLE_HOME; /** @hide */ - @SystemApi public static final int DISABLE_RECENT = View.STATUS_BAR_DISABLE_RECENT; /** @hide */ public static final int DISABLE_BACK = View.STATUS_BAR_DISABLE_BACK; /** @hide */ public static final int DISABLE_CLOCK = View.STATUS_BAR_DISABLE_CLOCK; /** @hide */ - @SystemApi public static final int DISABLE_SEARCH = View.STATUS_BAR_DISABLE_SEARCH; /** @hide */ @@ -78,7 +73,6 @@ public class StatusBarManager { View.STATUS_BAR_DISABLE_HOME | View.STATUS_BAR_DISABLE_RECENT; /** @hide */ - @SystemApi public static final int DISABLE_NONE = 0x00000000; /** @hide */ @@ -122,7 +116,6 @@ public class StatusBarManager { public static final int DISABLE2_ROTATE_SUGGESTIONS = 1 << 4; /** @hide */ - @SystemApi public static final int DISABLE2_NONE = 0x00000000; /** @hide */ @@ -387,14 +380,14 @@ public class StatusBarManager { } /** - * Get the currently applied StatusBar disable flags + * Get this app's currently requested disabled components * - * @return a pair of Integers in the form of (disable, disable2) + * @return a new DisableInfo * * @hide */ @SystemApi - public Pair<Integer, Integer> getDisableFlags() { + public DisableInfo getDisableInfo() { try { final int userId = Binder.getCallingUserHandle().getIdentifier(); final IStatusBarService svc = getService(); @@ -403,7 +396,7 @@ public class StatusBarManager { flags = svc.getDisableFlags(mToken, userId); } - return new Pair<Integer, Integer>(flags[0], flags[1]); + return new DisableInfo(flags[0], flags[1]); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } @@ -416,4 +409,180 @@ public class StatusBarManager { if (state == WINDOW_STATE_SHOWING) return "WINDOW_STATE_SHOWING"; return "WINDOW_STATE_UNKNOWN"; } + + /** + * DisableInfo describes this app's requested state of the StatusBar with regards to which + * components are enabled/disabled + * + * @hide + */ + @SystemApi + public static final class DisableInfo { + + private boolean mStatusBarExpansion; + private boolean mNavigateHome; + private boolean mNotificationPeeking; + private boolean mRecents; + private boolean mSearch; + + /** @hide */ + public DisableInfo(int flags1, int flags2) { + mStatusBarExpansion = (flags1 & DISABLE_EXPAND) != 0; + mNavigateHome = (flags1 & DISABLE_HOME) != 0; + mNotificationPeeking = (flags1 & DISABLE_NOTIFICATION_ALERTS) != 0; + mRecents = (flags1 & DISABLE_RECENT) != 0; + mSearch = (flags1 & DISABLE_SEARCH) != 0; + } + + /** @hide */ + public DisableInfo() {} + + /** + * @return {@code true} if expanding the notification shade is disabled + * + * @hide + */ + @SystemApi + public boolean isStatusBarExpansionDisabled() { + return mStatusBarExpansion; + } + + /** * @hide */ + public void setStatusBarExpansionDisabled(boolean disabled) { + mStatusBarExpansion = disabled; + } + + /** + * @return {@code true} if navigation home is disabled + * + * @hide + */ + @SystemApi + public boolean isNavigateToHomeDisabled() { + return mNavigateHome; + } + + /** * @hide */ + public void setNagivationHomeDisabled(boolean disabled) { + mNavigateHome = disabled; + } + + /** + * @return {@code true} if notification peeking (heads-up notification) is disabled + * + * @hide + */ + @SystemApi + public boolean isNotificationPeekingDisabled() { + return mNotificationPeeking; + } + + /** @hide */ + public void setNotificationPeekingDisabled(boolean disabled) { + mNotificationPeeking = disabled; + } + + /** + * @return {@code true} if mRecents/overview is disabled + * + * @hide + */ + @SystemApi + public boolean isRecentsDisabled() { + return mRecents; + } + + /** @hide */ + public void setRecentsDisabled(boolean disabled) { + mRecents = disabled; + } + + /** + * @return {@code true} if mSearch is disabled + * + * @hide + */ + @SystemApi + public boolean isSearchDisabled() { + return mSearch; + } + + /** @hide */ + public void setSearchDisabled(boolean disabled) { + mSearch = disabled; + } + + /** + * @return {@code true} if no components are disabled (default state) + * + * @hide + */ + @SystemApi + public boolean areNoComponentsDisabled() { + return !mStatusBarExpansion && !mNavigateHome && !mNotificationPeeking && !mRecents + && !mSearch; + } + + /** @hide */ + public void setEnableAll() { + mStatusBarExpansion = false; + mNavigateHome = false; + mNotificationPeeking = false; + mRecents = false; + mSearch = false; + } + + /** + * @return {@code true} if all status bar components are disabled + * + * @hide + */ + public boolean areAllComponentsDisabled() { + return mStatusBarExpansion && mNavigateHome && mNotificationPeeking + && mRecents && mSearch; + } + + /** @hide */ + public void setDisableAll() { + mStatusBarExpansion = true; + mNavigateHome = true; + mNotificationPeeking = true; + mRecents = true; + mSearch = true; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("DisableInfo: "); + sb.append(" mStatusBarExpansion=").append(mStatusBarExpansion ? "disabled" : "enabled"); + sb.append(" mNavigateHome=").append(mNavigateHome ? "disabled" : "enabled"); + sb.append(" mNotificationPeeking=") + .append(mNotificationPeeking ? "disabled" : "enabled"); + sb.append(" mRecents=").append(mRecents ? "disabled" : "enabled"); + sb.append(" mSearch=").append(mSearch ? "disabled" : "enabled"); + + return sb.toString(); + + } + + /** + * Convert a DisableInfo to equivalent flags + * @return a pair of equivalent disable flags + * + * @hide + */ + public Pair<Integer, Integer> toFlags() { + int disable1 = DISABLE_NONE; + int disable2 = DISABLE2_NONE; + + if (mStatusBarExpansion) disable1 |= DISABLE_EXPAND; + if (mNavigateHome) disable1 |= DISABLE_HOME; + if (mNotificationPeeking) disable1 |= DISABLE_NOTIFICATION_ALERTS; + if (mRecents) disable1 |= DISABLE_RECENT; + if (mSearch) disable1 |= DISABLE_SEARCH; + + return new Pair<Integer, Integer>(disable1, disable2); + } + } } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 807b7f2ac349..cca8bd00b646 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -101,7 +101,6 @@ import android.net.IConnectivityManager; import android.net.IEthernetManager; import android.net.IIpMemoryStore; import android.net.IIpSecService; -import android.net.INetd; import android.net.INetworkPolicyManager; import android.net.IpMemoryStore; import android.net.IpSecManager; @@ -330,11 +329,10 @@ final class SystemServiceRegistry { return new ConnectivityManager(context, service); }}); - registerService(Context.NETD_SERVICE, INetd.class, new StaticServiceFetcher<INetd>() { + registerService(Context.NETD_SERVICE, IBinder.class, new StaticServiceFetcher<IBinder>() { @Override - public INetd createService() throws ServiceNotFoundException { - return INetd.Stub.asInterface( - ServiceManager.getServiceOrThrow(Context.NETD_SERVICE)); + public IBinder createService() throws ServiceNotFoundException { + return ServiceManager.getServiceOrThrow(Context.NETD_SERVICE); } }); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index d781a96420c7..a5e7e95f0874 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1790,6 +1790,35 @@ 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 + * @see #EXTRA_USER + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) + @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. @@ -2080,6 +2109,22 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_REVIEW_APP_PERMISSION_USAGE = "android.intent.action.REVIEW_APP_PERMISSION_USAGE"; + /** + * Activity action: Launch UI to review running accessibility services. + * <p> + * Input: Nothing. + * </p> + * <p> + * Output: Nothing. + * </p> + * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REVIEW_ACCESSIBILITY_SERVICES = + "android.intent.action.REVIEW_ACCESSIBILITY_SERVICES"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent broadcast actions (see action variable). diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index eaf6c5a9d1cc..3ea78df6ae39 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -5910,24 +5910,24 @@ public abstract class PackageManager { /** * Flag to denote no restrictions. This should be used to clear any restrictions that may have * been previously set for the package. - * @see PackageManager.DistractionRestriction * @hide + * @see #setDistractingPackageRestrictions(String[], int) */ @SystemApi public static final int RESTRICTION_NONE = 0x0; /** * Flag to denote that a package should be hidden from any suggestions to the user. - * @see PackageManager.DistractionRestriction * @hide + * @see #setDistractingPackageRestrictions(String[], int) */ @SystemApi public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 0x00000001; /** * Flag to denote that a package's notifications should be hidden. - * @see PackageManager.DistractionRestriction * @hide + * @see #setDistractingPackageRestrictions(String[], int) */ @SystemApi public static final int RESTRICTION_HIDE_NOTIFICATIONS = 0x00000002; @@ -5939,7 +5939,6 @@ public abstract class PackageManager { * @see #setDistractingPackageRestrictions(String[], int) * @hide */ - @SystemApi @IntDef(flag = true, prefix = {"RESTRICTION_"}, value = { RESTRICTION_NONE, RESTRICTION_HIDE_FROM_SUGGESTIONS, @@ -5957,14 +5956,16 @@ public abstract class PackageManager { * <p>The caller must hold {@link android.Manifest.permission#SUSPEND_APPS} to use this API. * * @param packages Packages to mark as distracting. - * @param restrictionFlags Any combination of {@link DistractionRestriction restrictions} to - * impose on the given packages. {@link #RESTRICTION_NONE} can be used - * to clear any existing restrictions. + * @param restrictionFlags Any combination of restrictions to impose on the given packages. + * {@link #RESTRICTION_NONE} can be used to clear any existing + * restrictions. * @return A list of packages that could not have the {@code restrictionFlags} set. The system * may prevent restricting critical packages to preserve normal device function. * - * @see DistractionRestriction * @hide + * @see #RESTRICTION_NONE + * @see #RESTRICTION_HIDE_FROM_SUGGESTIONS + * @see #RESTRICTION_HIDE_NOTIFICATIONS */ @SystemApi @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index cfe35b061151..270e3879a71f 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -163,6 +163,30 @@ public abstract class PackageManagerInternal { } /** + * Provider for default home + */ + public interface DefaultHomeProvider { + + /** + * Get the package name of the default home. + * + * @param userId the user id + * + * @return the package name of the default home, or {@code null} if none + */ + @Nullable + String getDefaultHome(@UserIdInt int userId); + + /** + * Set the package name of the default home. + * + * @param packageName package name of the default home, or {@code null} to remove + * @param userId the user id + */ + void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId); + } + + /** * Sets the location provider packages provider. * @param provider The packages provider. */ @@ -886,4 +910,11 @@ public abstract class PackageManagerInternal { * @param provider the provider */ public abstract void setDefaultBrowserProvider(@NonNull DefaultBrowserProvider provider); + + /** + * Sets the default home provider. + * + * @param provider the provider + */ + public abstract void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 0abd5eaaf2aa..0f67262bf901 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -4848,7 +4848,7 @@ public class PackageParser { } /** - * Sets every the max aspect ratio of every child activity that doesn't already have an aspect + * Sets every the min aspect ratio of every child activity that doesn't already have an aspect * ratio set. */ private void setMinAspectRatio(Package owner) { diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 2aca55aacf7a..68d36de19963 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -678,11 +678,20 @@ public class ConnectivityManager { @Deprecated public static final int TYPE_VPN = 17; + /** + * A network that is exclusively meant to be used for testing + * + * @deprecated Use {@link NetworkCapabilities} instead. + * @hide + */ + @Deprecated + public static final int TYPE_TEST = 18; // TODO: Remove this once NetworkTypes are unused. + /** {@hide} */ - public static final int MAX_RADIO_TYPE = TYPE_VPN; + public static final int MAX_RADIO_TYPE = TYPE_TEST; /** {@hide} */ - public static final int MAX_NETWORK_TYPE = TYPE_VPN; + public static final int MAX_NETWORK_TYPE = TYPE_TEST; private static final int MIN_NETWORK_TYPE = TYPE_MOBILE; diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java index 62cf7d7ceb25..b9d49c14f6c6 100644 --- a/core/java/android/net/InterfaceConfiguration.java +++ b/core/java/android/net/InterfaceConfiguration.java @@ -36,8 +36,9 @@ public class InterfaceConfiguration implements Parcelable { private LinkAddress mAddr; private HashSet<String> mFlags = Sets.newHashSet(); - private static final String FLAG_UP = INetd.IF_STATE_UP; - private static final String FLAG_DOWN = INetd.IF_STATE_DOWN; + // Must be kept in sync with constant in INetd.aidl + private static final String FLAG_UP = "up"; + private static final String FLAG_DOWN = "down"; private static final String[] EMPTY_STRING_ARRAY = new String[0]; diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 7e9bda14b199..1d2d81dc4fbe 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -597,6 +597,7 @@ public final class NetworkCapabilities implements Parcelable { TRANSPORT_VPN, TRANSPORT_WIFI_AWARE, TRANSPORT_LOWPAN, + TRANSPORT_TEST, }) public @interface Transport { } @@ -635,10 +636,18 @@ public final class NetworkCapabilities implements Parcelable { */ public static final int TRANSPORT_LOWPAN = 6; + /** + * Indicates this network uses a Test-only virtual interface as a transport. + * + * @hide + */ + @TestApi + public static final int TRANSPORT_TEST = 7; + /** @hide */ public static final int MIN_TRANSPORT = TRANSPORT_CELLULAR; /** @hide */ - public static final int MAX_TRANSPORT = TRANSPORT_LOWPAN; + public static final int MAX_TRANSPORT = TRANSPORT_TEST; /** @hide */ public static boolean isValidTransport(@Transport int transportType) { @@ -652,7 +661,8 @@ public final class NetworkCapabilities implements Parcelable { "ETHERNET", "VPN", "WIFI_AWARE", - "LOWPAN" + "LOWPAN", + "TEST" }; /** diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 5ab34e9aa6e8..bf272625e713 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -92,16 +92,6 @@ public class NetworkPolicyManager { public static final int MASK_ALL_NETWORKS = 0b11110000; public static final int FIREWALL_RULE_DEFAULT = 0; - public static final int FIREWALL_RULE_ALLOW = INetd.FIREWALL_RULE_ALLOW; - public static final int FIREWALL_RULE_DENY = INetd.FIREWALL_RULE_DENY; - - public static final int FIREWALL_TYPE_WHITELIST = INetd.FIREWALL_WHITELIST; - public static final int FIREWALL_TYPE_BLACKLIST = INetd.FIREWALL_BLACKLIST; - - public static final int FIREWALL_CHAIN_NONE = INetd.FIREWALL_CHAIN_NONE; - public static final int FIREWALL_CHAIN_DOZABLE = INetd.FIREWALL_CHAIN_DOZABLE; - public static final int FIREWALL_CHAIN_STANDBY = INetd.FIREWALL_CHAIN_STANDBY; - public static final int FIREWALL_CHAIN_POWERSAVE = INetd.FIREWALL_CHAIN_POWERSAVE; public static final String FIREWALL_CHAIN_NAME_NONE = "none"; public static final String FIREWALL_CHAIN_NAME_DOZABLE = "dozable"; diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index ab6dd7c07b42..b7e65b9151b9 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -46,6 +46,7 @@ import com.android.internal.os.BatteryStatsHelper; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -262,6 +263,7 @@ public abstract class BatteryStats implements Parcelable { private static final long BYTES_PER_KB = 1024; private static final long BYTES_PER_MB = 1048576; // 1024^2 private static final long BYTES_PER_GB = 1073741824; //1024^3 + public static final double MILLISECONDS_IN_HOUR = 3600 * 1000; private static final String VERSION_DATA = "vers"; private static final String UID_DATA = "uid"; @@ -482,6 +484,13 @@ public abstract class BatteryStats implements Parcelable { * yield a value of 0 if the device doesn't support power calculations. */ public abstract LongCounter getPowerCounter(); + + /** + * @return a non-null {@link LongCounter} representing total power monitored on the rails + * in mAms (miliamps-milliseconds). The counter may always yield a value of 0 if the device + * doesn't support power rail monitoring. + */ + public abstract LongCounter getMonitoredRailChargeConsumedMaMs(); } /** @@ -1526,6 +1535,9 @@ public abstract class BatteryStats implements Parcelable { // The charge of the battery in micro-Ampere-hours. public int batteryChargeUAh; + public double modemRailChargeMah; + public double wifiRailChargeMah; + // Constants from SCREEN_BRIGHTNESS_* public static final int STATE_BRIGHTNESS_SHIFT = 0; public static final int STATE_BRIGHTNESS_MASK = 0x7; @@ -1738,6 +1750,8 @@ public abstract class BatteryStats implements Parcelable { | ((((int)batteryVoltage)<<16)&0xffff0000); dest.writeInt(bat); dest.writeInt(batteryChargeUAh); + dest.writeDouble(modemRailChargeMah); + dest.writeDouble(wifiRailChargeMah); dest.writeInt(states); dest.writeInt(states2); if (wakelockTag != null) { @@ -1767,6 +1781,8 @@ public abstract class BatteryStats implements Parcelable { batteryTemperature = (short)(bat2&0xffff); batteryVoltage = (char)((bat2>>16)&0xffff); batteryChargeUAh = src.readInt(); + modemRailChargeMah = src.readDouble(); + wifiRailChargeMah = src.readDouble(); states = src.readInt(); states2 = src.readInt(); if ((bat&0x10000000) != 0) { @@ -1807,6 +1823,8 @@ public abstract class BatteryStats implements Parcelable { batteryTemperature = 0; batteryVoltage = 0; batteryChargeUAh = 0; + modemRailChargeMah = 0; + wifiRailChargeMah = 0; states = 0; states2 = 0; wakelockTag = null; @@ -1835,6 +1853,8 @@ public abstract class BatteryStats implements Parcelable { batteryTemperature = o.batteryTemperature; batteryVoltage = o.batteryVoltage; batteryChargeUAh = o.batteryChargeUAh; + modemRailChargeMah = o.modemRailChargeMah; + wifiRailChargeMah = o.wifiRailChargeMah; states = o.states; states2 = o.states2; if (o.wakelockTag != null) { @@ -1867,6 +1887,8 @@ public abstract class BatteryStats implements Parcelable { && batteryTemperature == o.batteryTemperature && batteryVoltage == o.batteryVoltage && batteryChargeUAh == o.batteryChargeUAh + && modemRailChargeMah == o.modemRailChargeMah + && wifiRailChargeMah == o.wifiRailChargeMah && states == o.states && states2 == o.states2 && currentTime == o.currentTime; @@ -3311,7 +3333,8 @@ public abstract class BatteryStats implements Parcelable { if (counter.getIdleTimeCounter().getCountLocked(which) != 0 || counter.getRxTimeCounter().getCountLocked(which) != 0 - || counter.getPowerCounter().getCountLocked(which) != 0) { + || counter.getPowerCounter().getCountLocked(which) != 0 + || counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which) != 0) { return true; } @@ -3345,7 +3368,10 @@ public abstract class BatteryStats implements Parcelable { pw.print(","); pw.print(counter.getRxTimeCounter().getCountLocked(which)); pw.print(","); - pw.print(counter.getPowerCounter().getCountLocked(which) / (1000 * 60 * 60)); + pw.print(counter.getPowerCounter().getCountLocked(which) / (MILLISECONDS_IN_HOUR)); + pw.print(","); + pw.print(counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which) + / (MILLISECONDS_IN_HOUR)); for (LongCounter c : counter.getTxTimeCounters()) { pw.print(","); pw.print(c.getCountLocked(which)); @@ -3370,7 +3396,10 @@ public abstract class BatteryStats implements Parcelable { proto.write(ControllerActivityProto.RX_DURATION_MS, counter.getRxTimeCounter().getCountLocked(which)); proto.write(ControllerActivityProto.POWER_MAH, - counter.getPowerCounter().getCountLocked(which) / (1000 * 60 * 60)); + counter.getPowerCounter().getCountLocked(which) / (MILLISECONDS_IN_HOUR)); + proto.write(ControllerActivityProto.MONITORED_RAIL_CHARGE_MAH, + counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which) + / (MILLISECONDS_IN_HOUR)); long tToken; LongCounter[] txCounters = counter.getTxTimeCounters(); @@ -3400,6 +3429,8 @@ public abstract class BatteryStats implements Parcelable { final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which); final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which); final long powerDrainMaMs = counter.getPowerCounter().getCountLocked(which); + final long monitoredRailChargeConsumedMaMs = + counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which); // Battery real time final long totalControllerActivityTimeMs = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000; @@ -3522,10 +3553,22 @@ public abstract class BatteryStats implements Parcelable { sb.append(" "); sb.append(controllerName); sb.append(" Battery drain: ").append( - BatteryStatsHelper.makemAh(powerDrainMaMs / (double) (1000*60*60))); + BatteryStatsHelper.makemAh(powerDrainMaMs / MILLISECONDS_IN_HOUR)); sb.append("mAh"); pw.println(sb.toString()); } + + if (monitoredRailChargeConsumedMaMs > 0) { + sb.setLength(0); + sb.append(prefix); + sb.append(" "); + sb.append(controllerName); + sb.append(" Monitored rail energy drain: ").append( + new DecimalFormat("#.##").format( + monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR)); + sb.append(" mAh"); + pw.println(sb.toString()); + } } /** @@ -6103,6 +6146,8 @@ public abstract class BatteryStats implements Parcelable { int oldTemp = -1; int oldVolt = -1; int oldChargeMAh = -1; + double oldModemRailChargeMah = -1; + double oldWifiRailChargeMah = -1; long lastTime = -1; void reset() { @@ -6114,6 +6159,8 @@ public abstract class BatteryStats implements Parcelable { oldTemp = -1; oldVolt = -1; oldChargeMAh = -1; + oldModemRailChargeMah = -1; + oldWifiRailChargeMah = -1; } public void printNextItem(PrintWriter pw, HistoryItem rec, long baseTime, boolean checkin, @@ -6299,6 +6346,16 @@ public abstract class BatteryStats implements Parcelable { item.append(checkin ? ",Bcc=" : " charge="); item.append(oldChargeMAh); } + if (oldModemRailChargeMah != rec.modemRailChargeMah) { + oldModemRailChargeMah = rec.modemRailChargeMah; + item.append(checkin ? ",Mrc=" : " modemRailChargemAh="); + item.append(new DecimalFormat("#.##").format(oldModemRailChargeMah)); + } + if (oldWifiRailChargeMah != rec.wifiRailChargeMah) { + oldWifiRailChargeMah = rec.wifiRailChargeMah; + item.append(checkin ? ",Wrc=" : " wifiRailChargemAh="); + item.append(new DecimalFormat("#.##").format(oldWifiRailChargeMah)); + } printBitDescriptions(item, oldState, rec.states, rec.wakelockTag, HISTORY_STATE_DESCRIPTIONS, !checkin); printBitDescriptions(item, oldState2, rec.states2, null, diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 8813e400bee0..cc241b3b7756 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -25,16 +25,11 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; -import android.gamedriver.GameDriverProto.Blacklist; -import android.gamedriver.GameDriverProto.Blacklists; import android.opengl.EGL14; import android.provider.Settings; -import android.util.Base64; import android.util.Log; import android.widget.Toast; -import com.android.framework.protobuf.InvalidProtocolBufferException; - import dalvik.system.VMRuntime; import java.io.File; @@ -69,8 +64,7 @@ public class GraphicsEnvironment { private static final String ANGLE_RULES_FILE = "a4a_rules.json"; private static final String ANGLE_TEMP_RULES = "debug.angle.rules"; private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID"; - private static final String GAME_DRIVER_BLACKLIST_FLAG = "blacklist"; - private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP; + private static final String GAME_DRIVER_WHITELIST_ALL = "*"; private ClassLoader mClassLoader; private String mLayerPath; @@ -630,43 +624,23 @@ public class GraphicsEnvironment { final boolean isOptIn = getGlobalSettingsString(null, coreSettings, Settings.Global.GAME_DRIVER_OPT_IN_APPS).contains(packageName); - if (!isOptIn - && !getGlobalSettingsString(null, coreSettings, - Settings.Global.GAME_DRIVER_WHITELIST).contains(packageName)) { + final List<String> whitelist = getGlobalSettingsString(null, coreSettings, + Settings.Global.GAME_DRIVER_WHITELIST); + if (!isOptIn && whitelist.indexOf(GAME_DRIVER_WHITELIST_ALL) != 0 + && !whitelist.contains(packageName)) { if (DEBUG) { Log.w(TAG, packageName + " is not on the whitelist."); } return false; } - if (!isOptIn) { - // At this point, the application is on the whitelist only, check whether it's - // on the blacklist, terminate early when it's on the blacklist. - try { - // TODO(b/121350991) Switch to DeviceConfig with property listener. - final String base64String = - coreSettings.getString(Settings.Global.GAME_DRIVER_BLACKLIST); - if (base64String != null && !base64String.isEmpty()) { - final Blacklists blacklistsProto = - Blacklists.parseFrom(Base64.decode(base64String, BASE64_FLAGS)); - final List<Blacklist> blacklists = blacklistsProto.getBlacklistsList(); - final long driverVersionCode = driverAppInfo.longVersionCode; - for (Blacklist blacklist : blacklists) { - if (blacklist.getVersionCode() == driverVersionCode) { - for (String pkgName : blacklist.getPackageNamesList()) { - if (pkgName == packageName) { - return false; - } - } - break; - } - } - } - } catch (InvalidProtocolBufferException e) { - if (DEBUG) { - Log.w(TAG, "Can't parse blacklist, skip and continue..."); - } - } + // If the application is not opted-in and check whether it's on the blacklist, + // terminate early if it's on the blacklist and fallback to system driver. + if (!isOptIn + && getGlobalSettingsString(null, coreSettings, + Settings.Global.GAME_DRIVER_BLACKLIST) + .contains(ai.packageName)) { + return false; } } diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl index f1bba1ab2977..6d4c5a034b54 100644 --- a/core/java/android/os/IStatsManager.aidl +++ b/core/java/android/os/IStatsManager.aidl @@ -196,4 +196,25 @@ interface IStatsManager { * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS */ oneway void unregisterPullerCallback(int atomTag, String packageName); + + /** + * The install requires staging. + */ + const int FLAG_REQUIRE_STAGING = 0x01; + + /** + * Rollback is enabled with this install. + */ + const int FLAG_ROLLBACK_ENABLED = 0x02; + + /** + * Requires low latency monitoring. + */ + const int FLAG_REQUIRE_LOW_LATENCY_MONITOR = 0x04; + + /** + * Logs an event for binary push for module updates. + */ + oneway void sendBinaryPushStateChangedAtom(in String trainName, in long trainVersionCode, + in int options, in int state, in long[] experimentId); } diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java index 87e1b7d21f53..1420e2f5ce10 100644 --- a/core/java/android/os/LocaleList.java +++ b/core/java/android/os/LocaleList.java @@ -20,6 +20,7 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; +import android.annotation.SystemApi; import android.content.LocaleProto; import android.icu.util.ULocale; import android.util.proto.ProtoOutputStream; @@ -324,6 +325,15 @@ public final class LocaleList implements Parcelable { return LOCALE_EN_XA.equals(locale) || LOCALE_AR_XB.equals(locale); } + /** + * Returns true if locale is a pseudo-locale, false otherwise. + * {@hide} + */ + @SystemApi + public static boolean isPseudoLocale(@Nullable ULocale locale) { + return isPseudoLocale(locale != null ? locale.toLocale() : null); + } + @IntRange(from=0, to=1) private static int matchScore(Locale supported, Locale desired) { if (supported.equals(desired)) { diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 2ecf9d1159f0..cfe2d28f1024 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1784,7 +1784,7 @@ public final class PowerManager { * * see {@link #registerThermalStatusCallback} */ - public void unregisterThermalStatusCallback(ThermalStatusCallback callback) { + public void unregisterThermalStatusCallback(@NonNull ThermalStatusCallback callback) { Preconditions.checkNotNull(callback, "callback cannnot be null"); synchronized (this) { if (mThermalService == null) { diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 8492b0cf58e2..3ee54ceebaa9 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -32,6 +32,7 @@ import android.content.pm.PackageManager; import android.provider.Settings; import android.telephony.euicc.EuiccManager; import android.text.TextUtils; +import android.text.format.DateFormat; import android.util.Log; import android.view.Display; import android.view.WindowManager; @@ -762,7 +763,8 @@ public class RecoverySystem { String reasonArg = null; if (!TextUtils.isEmpty(reason)) { - reasonArg = "--reason=" + sanitizeArg(reason); + String timeStamp = DateFormat.format("yyyy-MM-ddTHH:mm:ssZ", System.currentTimeMillis()).toString(); + reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp); } final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ; diff --git a/core/java/android/os/connectivity/CellularBatteryStats.java b/core/java/android/os/connectivity/CellularBatteryStats.java index 2593c85cff12..c99ecb32d418 100644 --- a/core/java/android/os/connectivity/CellularBatteryStats.java +++ b/core/java/android/os/connectivity/CellularBatteryStats.java @@ -44,6 +44,7 @@ public final class CellularBatteryStats implements Parcelable { private long[] mTimeInRatMs; private long[] mTimeInRxSignalStrengthLevelMs; private long[] mTxTimeMs; + private long mMonitoredRailChargeConsumedMaMs; public static final Parcelable.Creator<CellularBatteryStats> CREATOR = new Parcelable.Creator<CellularBatteryStats>() { @@ -74,6 +75,7 @@ public final class CellularBatteryStats implements Parcelable { out.writeLongArray(mTimeInRatMs); out.writeLongArray(mTimeInRxSignalStrengthLevelMs); out.writeLongArray(mTxTimeMs); + out.writeLong(mMonitoredRailChargeConsumedMaMs); } public void readFromParcel(Parcel in) { @@ -90,6 +92,7 @@ public final class CellularBatteryStats implements Parcelable { in.readLongArray(mTimeInRatMs); in.readLongArray(mTimeInRxSignalStrengthLevelMs); in.readLongArray(mTxTimeMs); + mMonitoredRailChargeConsumedMaMs = in.readLong(); } public long getLoggingDurationMs() { @@ -144,6 +147,10 @@ public final class CellularBatteryStats implements Parcelable { return mTxTimeMs; } + public long getMonitoredRailChargeConsumedMaMs() { + return mMonitoredRailChargeConsumedMaMs; + } + public void setLoggingDurationMs(long t) { mLoggingDurationMs = t; return; @@ -211,6 +218,11 @@ public final class CellularBatteryStats implements Parcelable { return; } + public void setMonitoredRailChargeConsumedMaMs(long monitoredRailEnergyConsumedMaMs) { + mMonitoredRailChargeConsumedMaMs = monitoredRailEnergyConsumedMaMs; + return; + } + public int describeContents() { return 0; } @@ -237,6 +249,7 @@ public final class CellularBatteryStats implements Parcelable { Arrays.fill(mTimeInRxSignalStrengthLevelMs, 0); mTxTimeMs = new long[ModemActivityInfo.TX_POWER_LEVELS]; Arrays.fill(mTxTimeMs, 0); + mMonitoredRailChargeConsumedMaMs = 0; return; } }
\ No newline at end of file diff --git a/core/java/android/os/connectivity/WifiBatteryStats.java b/core/java/android/os/connectivity/WifiBatteryStats.java index e5341eeeb17b..3639c71ae3b5 100644 --- a/core/java/android/os/connectivity/WifiBatteryStats.java +++ b/core/java/android/os/connectivity/WifiBatteryStats.java @@ -44,6 +44,7 @@ public final class WifiBatteryStats implements Parcelable { private long[] mTimeInStateMs; private long[] mTimeInSupplicantStateMs; private long[] mTimeInRxSignalStrengthLevelMs; + private long mMonitoredRailChargeConsumedMaMs; public static final Parcelable.Creator<WifiBatteryStats> CREATOR = new Parcelable.Creator<WifiBatteryStats>() { @@ -77,6 +78,7 @@ public final class WifiBatteryStats implements Parcelable { out.writeLongArray(mTimeInStateMs); out.writeLongArray(mTimeInRxSignalStrengthLevelMs); out.writeLongArray(mTimeInSupplicantStateMs); + out.writeLong(mMonitoredRailChargeConsumedMaMs); } public void readFromParcel(Parcel in) { @@ -96,6 +98,7 @@ public final class WifiBatteryStats implements Parcelable { in.readLongArray(mTimeInStateMs); in.readLongArray(mTimeInRxSignalStrengthLevelMs); in.readLongArray(mTimeInSupplicantStateMs); + mMonitoredRailChargeConsumedMaMs = in.readLong(); } public long getLoggingDurationMs() { @@ -162,6 +165,10 @@ public final class WifiBatteryStats implements Parcelable { return mTimeInSupplicantStateMs; } + public long getMonitoredRailChargeConsumedMaMs() { + return mMonitoredRailChargeConsumedMaMs; + } + public void setLoggingDurationMs(long t) { mLoggingDurationMs = t; return; @@ -245,6 +252,11 @@ public final class WifiBatteryStats implements Parcelable { return; } + public void setMonitoredRailChargeConsumedMaMs(long monitoredRailEnergyConsumedMaMs) { + mMonitoredRailChargeConsumedMaMs = monitoredRailEnergyConsumedMaMs; + return; + } + public int describeContents() { return 0; } @@ -274,6 +286,7 @@ public final class WifiBatteryStats implements Parcelable { Arrays.fill(mTimeInRxSignalStrengthLevelMs, 0); mTimeInSupplicantStateMs = new long[BatteryStats.NUM_WIFI_SUPPL_STATES]; Arrays.fill(mTimeInSupplicantStateMs, 0); + mMonitoredRailChargeConsumedMaMs = 0; return; } }
\ No newline at end of file diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 104b61dc410d..7eb03007ee6a 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -316,10 +316,17 @@ public final class DeviceConfig { public interface Rollback { String NAMESPACE = "rollback"; + String BOOT_NAMESPACE = "rollback_boot"; + /** * Timeout in milliseconds for enabling package rollback. */ String ENABLE_ROLLBACK_TIMEOUT = "enable_rollback_timeout"; + + /** + * The lifetime duration of rollback packages in millis + */ + String ROLLBACK_LIFETIME_IN_MILLIS = "rollback_lifetime_in_millis"; } /** diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 5f1c56016fa7..570834265237 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -853,10 +853,8 @@ public final class DocumentsContract { private static final String PATH_DOCUMENT = "document"; private static final String PATH_CHILDREN = "children"; private static final String PATH_SEARCH = "search"; - // TODO(b/72055774): make private again once ScopedAccessProvider is refactored - /** {@hide} */ @UnsupportedAppUsage - public static final String PATH_TREE = "tree"; + private static final String PATH_TREE = "tree"; private static final String PARAM_QUERY = "query"; private static final String PARAM_MANAGE = "manage"; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index d75c5e28e3f6..d925d7ee2676 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1657,6 +1657,30 @@ public final class Settings { public static final String ACTION_STORAGE_VOLUME_ACCESS_SETTINGS = "android.settings.STORAGE_VOLUME_ACCESS_SETTINGS"; + + /** + * Activity Action: Show screen that let user select enable (or disable) Content Capture. + * <p> + * Input: Nothing. + * + * <p> + * Output: {@link android.app.Activity#RESULT_OK} if user enabled Content Capture, + * {@link android.app.Activity#RESULT_CANCELED} if user disabled it, cancelled, or if the caller + * is not the Content Capture service associated with the user. + * + * <p> + * <b>NOTE: </b> Caller should call + * {@link android.view.contentcapture.ContentCaptureManager#isContentCaptureFeatureEnabled()} + * first to check whether the feature is already enabled. + * + * @hide + */ + @SystemApi + @TestApi + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = + "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE"; + // End of Intent actions for Settings /** @@ -6103,7 +6127,7 @@ public final class Settings { * Indicates which clock face to show on lock screen and AOD while docked. * @hide */ - private static final String DOCKED_CLOCK_FACE = "docked_clock_face"; + public static final String DOCKED_CLOCK_FACE = "docked_clock_face"; /** * Set by the system to track if the user needs to see the call to action for diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java index 81d066d3d656..8695da237f97 100644 --- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java +++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java @@ -61,8 +61,6 @@ import java.util.List; */ @SystemApi @TestApi -// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service -// in the same package as the test, and that module is compiled with SDK=test_current public abstract class AugmentedAutofillService extends Service { private static final String TAG = AugmentedAutofillService.class.getSimpleName(); diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java index f2a7a35b2825..b989dd9cd4eb 100644 --- a/core/java/android/service/autofill/augmented/FillCallback.java +++ b/core/java/android/service/autofill/augmented/FillCallback.java @@ -31,8 +31,6 @@ import android.util.Log; */ @SystemApi @TestApi -//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service -//in the same package as the test, and that module is compiled with SDK=test_current public final class FillCallback { private static final String TAG = FillCallback.class.getSimpleName(); diff --git a/core/java/android/service/autofill/augmented/FillController.java b/core/java/android/service/autofill/augmented/FillController.java index d7bc893f884a..67f23d599e1d 100644 --- a/core/java/android/service/autofill/augmented/FillController.java +++ b/core/java/android/service/autofill/augmented/FillController.java @@ -38,10 +38,8 @@ import java.util.List; */ @SystemApi @TestApi -//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service -//in the same package as the test, and that module is compiled with SDK=test_current public final class FillController { - private static final String TAG = "FillController"; + private static final String TAG = FillController.class.getSimpleName(); private final AutofillProxy mProxy; diff --git a/core/java/android/service/autofill/augmented/FillRequest.java b/core/java/android/service/autofill/augmented/FillRequest.java index af9905f480df..9a97bb203f5a 100644 --- a/core/java/android/service/autofill/augmented/FillRequest.java +++ b/core/java/android/service/autofill/augmented/FillRequest.java @@ -31,8 +31,6 @@ import android.view.autofill.AutofillValue; @SystemApi // TODO(b/123100811): pass a requestId and/or sessionId? @TestApi -// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service -// in the same package as the test, and that module is compiled with SDK=test_current public final class FillRequest { final AutofillProxy mProxy; diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java index f1e904a7d8bc..2ac406c5b08c 100644 --- a/core/java/android/service/autofill/augmented/FillResponse.java +++ b/core/java/android/service/autofill/augmented/FillResponse.java @@ -30,8 +30,6 @@ import java.util.List; */ @SystemApi @TestApi -//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service -//in the same package as the test, and that module is compiled with SDK=test_current public final class FillResponse { private final FillWindow mFillWindow; @@ -53,8 +51,6 @@ public final class FillResponse { */ @SystemApi @TestApi - //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service - //in the same package as the test, and that module is compiled with SDK=test_current public static final class Builder { private FillWindow mFillWindow; diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java index 40e3a1219501..6e06754e7b8a 100644 --- a/core/java/android/service/autofill/augmented/FillWindow.java +++ b/core/java/android/service/autofill/augmented/FillWindow.java @@ -63,10 +63,8 @@ import java.io.PrintWriter; */ @SystemApi @TestApi -//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service -//in the same package as the test, and that module is compiled with SDK=test_current public final class FillWindow implements AutoCloseable { - private static final String TAG = "FillWindow"; + private static final String TAG = FillWindow.class.getSimpleName(); private final Object mLock = new Object(); private final CloseGuard mCloseGuard = CloseGuard.get(); diff --git a/core/java/android/service/autofill/augmented/PresentationParams.java b/core/java/android/service/autofill/augmented/PresentationParams.java index 1fb9032c9af5..334487dd6ab4 100644 --- a/core/java/android/service/autofill/augmented/PresentationParams.java +++ b/core/java/android/service/autofill/augmented/PresentationParams.java @@ -15,32 +15,19 @@ */ package android.service.autofill.augmented; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; import android.graphics.Rect; import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy; -import android.util.DebugUtils; import android.view.View; import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; /** * Abstraction of a "Smart Suggestion" component responsible to embed the autofill UI provided by - * the intelligence service. - * - * <p>The Smart Suggestion can embed the autofill UI in 3 distinct places: - * - * <ul> - * <li>A small area associated with suggestions (like a small strip in the top of the IME), - * returned by {@link #getSuggestionArea()} - * <li>The full area (like the full IME window), returned by {@link #getFullArea()} - * <li>A subset of the aforementioned areas, returned by {@link Area#getSubArea(Rect)} - * </ul> + * the augmented autofill service. * * <p>The Smart Suggestion is represented by a {@link Area} object that contains the * dimensions the smart suggestion window, so the service can use it to calculate the size of the @@ -50,54 +37,8 @@ import java.lang.annotation.RetentionPolicy; */ @SystemApi @TestApi -//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service -//in the same package as the test, and that module is compiled with SDK=test_current public abstract class PresentationParams { - /** - * Flag indicating the Smart Suggestion is hosted in the top of its container. - */ - public static final int FLAG_HINT_GRAVITY_TOP = 0x1; - - /** - * Flag indicating the Smart Suggestion is hosted in the bottom of its container. - */ - public static final int FLAG_HINT_GRAVITY_BOTTOM = 0x2; - - /** - * Flag indicating the Smart Suggestion is hosted in the left of its container. - */ - public static final int FLAG_HINT_GRAVITY_LEFT = 0x4; - - /** - * Flag indicating the Smart Suggestion is hosted in the right of its container. - */ - public static final int FLAG_HINT_GRAVITY_RIGHT = 0x8; - - /** - * Flag indicating the Smart Suggestion is hosted by the IME. - */ - public static final int FLAG_HOST_IME = 0x10; - - /** - * Flag indicating the Smart Suggestion is hosted by the Android System as a floating popup - * window. - */ - public static final int FLAG_HOST_SYSTEM = 0x20; - - /** @hide */ - @IntDef(flag = true, prefix = { "FLAG_" }, value = { - FLAG_HINT_GRAVITY_TOP, - FLAG_HINT_GRAVITY_BOTTOM, - FLAG_HINT_GRAVITY_LEFT, - FLAG_HINT_GRAVITY_RIGHT, - FLAG_HOST_IME, - FLAG_HOST_SYSTEM - }) - @Retention(RetentionPolicy.SOURCE) - @interface Flags {} - - // /** @hide */ PresentationParams() {} @@ -112,40 +53,7 @@ public abstract class PresentationParams { return null; } - /** - * Gets the full area for the of the Smart Suggestion provider. - * - * @return full dimensions, or {@code null} if the Smart Suggestion provider does not support - * embeding the UI on its full area. - */ - @Nullable - public Area getFullArea() { - return null; - } - - /** - * Gets flags associated with the Smart Suggestion. - * - * @return any combination of {@link #FLAG_HINT_GRAVITY_TOP}, - * {@link #FLAG_HINT_GRAVITY_BOTTOM}, {@link #FLAG_HINT_GRAVITY_LEFT}, - * {@link #FLAG_HINT_GRAVITY_RIGHT}, {@link #FLAG_HOST_IME}, or - * {@link #FLAG_HOST_SYSTEM}, - */ - public @Flags int getFlags() { - return 0; - } - - /** @hide */ - void dump(@NonNull String prefix, @NonNull PrintWriter pw) { - final int flags = getFlags(); - if (flags > 0) { - pw.print(prefix); pw.print("flags: "); pw.println(flagsToString(flags)); - } - } - - private static String flagsToString(int flags) { - return DebugUtils.flagsToString(PresentationParams.class, "FLAG_", flags); - } + abstract void dump(String prefix, PrintWriter pw); /** * Area associated with a {@link PresentationParams Smart Suggestions} provider. @@ -154,8 +62,6 @@ public abstract class PresentationParams { */ @SystemApi @TestApi - //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service - //in the same package as the test, and that module is compiled with SDK=test_current public abstract static class Area { /** @hide */ @@ -176,24 +82,6 @@ public abstract class PresentationParams { return mBounds; } - /** - * Gets a subarea limited by given boundaries. - * - * @param bounds boundaries relative to this Area. - * - * @return new subarea, or {@code null} if the Smart Suggestion host does not support such - * subaarea. - * - * @throws IllegalArgumentException if the {@code bounds} is not fully-contained inside this - * full Area. - * - */ - @Nullable - public Area getSubArea(@NonNull Rect bounds) { - // TODO(b/123100712): implement / check boundaries / throw IAE / add unit test - return null; - } - @Override public String toString() { return mBounds.toString(); @@ -220,13 +108,7 @@ public abstract class PresentationParams { } @Override - public int getFlags() { - return FLAG_HOST_SYSTEM | FLAG_HINT_GRAVITY_BOTTOM; - } - - @Override void dump(@NonNull String prefix, @NonNull PrintWriter pw) { - super.dump(prefix, pw); pw.print(prefix); pw.print("area: "); pw.println(mSuggestionArea); } } diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index c98f09e13d97..d2f98599e9a6 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -339,7 +339,7 @@ public abstract class ContentCaptureService extends Service { } switch (event.getType()) { case ContentCaptureEvent.TYPE_SESSION_STARTED: - final ContentCaptureContext clientContext = event.getClientContext(); + final ContentCaptureContext clientContext = event.getContentCaptureContext(); clientContext.setParentSessionId(event.getParentSessionId()); mSessionUids.put(sessionIdString, uid); onCreateContentCaptureSession(clientContext, sessionId); @@ -383,8 +383,8 @@ public abstract class ContentCaptureService extends Service { } final Integer rightUid = mSessionUids.get(sessionId); if (rightUid == null) { - if (DEBUG) { - Log.d(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId + if (VERBOSE) { + Log.v(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId + ": " + mSessionUids); } // Just ignore, as the session could have been finished already diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 551bb8ada044..954dc3943019 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -372,6 +372,13 @@ public class StatusBarNotification implements Parcelable { /** * @hide */ + public void clearPackageContext() { + mContext = null; + } + + /** + * @hide + */ @UnsupportedAppUsage public Context getPackageContext(Context context) { if (mContext == null) { diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index ea2a25d52434..e3e63e539591 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -342,37 +342,17 @@ public class VoiceInteractionService extends Service { } /** - * Requests that the voice state UI indicate the given state. + * Provide hints to be reflected in the system UI. * - * @param state value indicating whether the assistant is listening, fulfilling, etc. + * @param hints Arguments used to show UI. */ - public final void setVoiceState(int state) { - try { - mSystemService.setVoiceState(mInterface, state); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + public final void setUiHints(@NonNull Bundle hints) { + if (hints == null) { + throw new IllegalArgumentException("Hints must be non-null"); } - } - /** - * Displays the given voice transcription contents. - */ - public final void setTranscription(@NonNull String transcription) { - try { - mSystemService.setTranscription(mInterface, transcription); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Hides transcription. - * - * @param immediate if {@code true}, remove before transcription animation completes. - */ - public final void clearTranscription(boolean immediate) { try { - mSystemService.clearTranscription(mInterface, immediate); + mSystemService.setUiHints(mInterface, hints); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/text/style/DynamicDrawableSpan.java b/core/java/android/text/style/DynamicDrawableSpan.java index be772af529ef..575401458691 100644 --- a/core/java/android/text/style/DynamicDrawableSpan.java +++ b/core/java/android/text/style/DynamicDrawableSpan.java @@ -77,6 +77,13 @@ public abstract class DynamicDrawableSpan extends ReplacementSpan { */ public static final int ALIGN_BASELINE = 1; + /** + * A constant indicating that this span should be vertically centered between + * the top and the lowest descender. + * @hide + */ + public static final int ALIGN_CENTER = 2; + protected final int mVerticalAlignment; @UnsupportedAppUsage @@ -142,6 +149,8 @@ public abstract class DynamicDrawableSpan extends ReplacementSpan { int transY = bottom - b.getBounds().bottom; if (mVerticalAlignment == ALIGN_BASELINE) { transY -= paint.getFontMetricsInt().descent; + } else if (mVerticalAlignment == ALIGN_CENTER) { + transY = (bottom - top) / 2 - b.getBounds().height() / 2; } canvas.translate(x, transY); diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java index ace4bf477af6..29ced3eda4ec 100644 --- a/core/java/android/util/StatsLog.java +++ b/core/java/android/util/StatsLog.java @@ -16,7 +16,14 @@ package android.util; +import static android.Manifest.permission.DUMP; +import static android.Manifest.permission.PACKAGE_USAGE_STATS; + +import android.Manifest; import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.app.IActivityManager; +import android.content.Context; import android.os.IStatsManager; import android.os.RemoteException; import android.os.ServiceManager; @@ -31,7 +38,10 @@ public final class StatsLog extends StatsLogInternal { private static IStatsManager sService; - private StatsLog() {} + private static Object sLogLock = new Object(); + + private StatsLog() { + } /** * Logs a start event. @@ -40,11 +50,13 @@ public final class StatsLog extends StatsLogInternal { * @return True if the log request was sent to statsd. */ public static boolean logStart(int label) { - synchronized (StatsLog.class) { + synchronized (sLogLock) { try { IStatsManager service = getIStatsManagerLocked(); if (service == null) { - if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging start"); + if (DEBUG) { + Slog.d(TAG, "Failed to find statsd when logging start"); + } return false; } service.sendAppBreadcrumbAtom(label, @@ -52,7 +64,9 @@ public final class StatsLog extends StatsLogInternal { return true; } catch (RemoteException e) { sService = null; - if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging start"); + if (DEBUG) { + Slog.d(TAG, "Failed to connect to statsd when logging start"); + } return false; } } @@ -65,18 +79,22 @@ public final class StatsLog extends StatsLogInternal { * @return True if the log request was sent to statsd. */ public static boolean logStop(int label) { - synchronized (StatsLog.class) { + synchronized (sLogLock) { try { IStatsManager service = getIStatsManagerLocked(); if (service == null) { - if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging stop"); + if (DEBUG) { + Slog.d(TAG, "Failed to find statsd when logging stop"); + } return false; } service.sendAppBreadcrumbAtom(label, StatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP); return true; } catch (RemoteException e) { sService = null; - if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging stop"); + if (DEBUG) { + Slog.d(TAG, "Failed to connect to statsd when logging stop"); + } return false; } } @@ -89,11 +107,13 @@ public final class StatsLog extends StatsLogInternal { * @return True if the log request was sent to statsd. */ public static boolean logEvent(int label) { - synchronized (StatsLog.class) { + synchronized (sLogLock) { try { IStatsManager service = getIStatsManagerLocked(); if (service == null) { - if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging event"); + if (DEBUG) { + Slog.d(TAG, "Failed to find statsd when logging event"); + } return false; } service.sendAppBreadcrumbAtom( @@ -101,7 +121,51 @@ public final class StatsLog extends StatsLogInternal { return true; } catch (RemoteException e) { sService = null; - if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging event"); + if (DEBUG) { + Slog.d(TAG, "Failed to connect to statsd when logging event"); + } + return false; + } + } + } + + /** + * Logs an event for binary push for module updates. + * + * @param trainName name of install train. + * @param trainVersionCode version code of the train. + * @param options optional flags about this install. + * @param state current install state. + * @param experimentIds experiment ids. + * @return True if the log request was sent to statsd. + */ + @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS}) + public static boolean logBinaryPushStateChanged(@NonNull String trainName, + long trainVersionCode, int options, int state, + @NonNull long[] experimentIds) { + synchronized (sLogLock) { + try { + IStatsManager service = getIStatsManagerLocked(); + if (service == null) { + if (DEBUG) { + Slog.d(TAG, "Failed to find statsd when logging event"); + } + return false; + } + int userId = IActivityManager.Stub.asInterface( + ServiceManager.getService("activity")) + .getCurrentUser() + .id; + service.sendBinaryPushStateChangedAtom( + trainName, trainVersionCode, options, state, experimentIds); + return true; + } catch (RemoteException e) { + sService = null; + if (DEBUG) { + Slog.d(TAG, + "Failed to connect to StatsCompanionService when logging " + + "BinaryPushStateChanged"); + } return false; } } @@ -118,7 +182,7 @@ public final class StatsLog extends StatsLogInternal { /** * Add a log to the stats log. * - * @param id The id of the atom + * @param id The id of the atom * @param params The parameters of the atom's message. */ public static void write(int id, @NonNull Object... params) { @@ -128,4 +192,13 @@ public final class StatsLog extends StatsLogInternal { (boolean) params[4], (int) params[5]); } } + + private static void enforceDumpCallingPermission(Context context) { + context.enforceCallingPermission(android.Manifest.permission.DUMP, "Need DUMP permission."); + } + + private static void enforcesageStatsCallingPermission(Context context) { + context.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS, + "Need PACKAGE_USAGE_STATS permission."); + } } diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index 62ed901c5e06..f37c9162d98a 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -336,7 +336,7 @@ public final class AccessibilityInteractionController { } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; final View root = findViewByAccessibilityId(accessibilityViewId); - if (root != null) { + if (root != null && isShown(root)) { mPrefetcher.prefetchAccessibilityNodeInfos( root, virtualDescendantId, flags, infos, arguments); } @@ -448,7 +448,7 @@ public final class AccessibilityInteractionController { } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; final View root = findViewByAccessibilityId(accessibilityViewId); - if (root != null) { + if (root != null && isShown(root)) { AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider(); if (provider != null) { infos = provider.findAccessibilityNodeInfosByText(text, @@ -531,7 +531,7 @@ public final class AccessibilityInteractionController { } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; final View root = findViewByAccessibilityId(accessibilityViewId); - if (root != null) { + if (root != null && isShown(root)) { switch (focusType) { case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: { View host = mViewRootImpl.mAccessibilityFocusedHost; @@ -621,7 +621,7 @@ public final class AccessibilityInteractionController { } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; final View root = findViewByAccessibilityId(accessibilityViewId); - if (root != null) { + if (root != null && isShown(root)) { View nextView = root.focusSearch(direction); if (nextView != null) { next = nextView.createAccessibilityNodeInfo(); @@ -676,7 +676,7 @@ public final class AccessibilityInteractionController { } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; final View target = findViewByAccessibilityId(accessibilityViewId); - if (target != null) { + if (target != null && isShown(target)) { if (action == R.id.accessibilityActionClickOnClickableSpan) { // Handle this hidden action separately succeeded = handleClickableSpanActionUiThread( @@ -759,9 +759,7 @@ public final class AccessibilityInteractionController { if (accessibilityId == AccessibilityNodeInfo.ROOT_ITEM_ID) { return mViewRootImpl.mView; } else { - final View foundView = - AccessibilityNodeIdManager.getInstance().findView(accessibilityId); - return isShown(foundView) ? foundView : null; + return AccessibilityNodeIdManager.getInstance().findView(accessibilityId); } } diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index 1dbc46b3e883..6cfc9f22a692 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -25,6 +25,7 @@ import android.graphics.Paint; import android.graphics.RecordingCanvas; import android.graphics.RenderNode; import android.os.Build; +import android.os.Handler; import android.util.SparseIntArray; import com.android.internal.util.VirtualRefBasePtr; @@ -84,6 +85,7 @@ public class RenderNodeAnimator extends Animator { private VirtualRefBasePtr mNativePtr; + private Handler mHandler; private RenderNode mTarget; private View mViewTarget; private int mRenderProperty = -1; @@ -222,6 +224,9 @@ public class RenderNodeAnimator extends Animator { private void moveToRunningState() { mState = STATE_RUNNING; if (mNativePtr != null) { + if (mHandler == null) { + mHandler = new Handler(); + } nStart(mNativePtr.get()); } notifyStartListeners(); @@ -497,7 +502,7 @@ public class RenderNodeAnimator extends Animator { // Called by native @UnsupportedAppUsage private static void callOnFinished(RenderNodeAnimator animator) { - animator.onFinished(); + animator.mHandler.post(animator::onFinished); } @Override diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 47b206ca0dca..20978127f510 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -453,7 +453,7 @@ public final class ThreadedRenderer extends HardwareRenderer { */ void destroyHardwareResources(View view) { destroyResources(view); - destroyHardwareResources(); + clearContent(); } private static void destroyResources(View view) { @@ -735,7 +735,9 @@ public final class ThreadedRenderer extends HardwareRenderer { if (callback != null) { setFrameCallback(callback); } - syncAndDrawFrame(vsync); + createRenderRequest() + .setVsyncTime(vsync) + .syncAndDraw(); } } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index bc1be26ac0e0..2b440dc80cfd 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -24,6 +24,7 @@ import static java.lang.Math.max; import android.animation.AnimatorInflater; import android.animation.StateListAnimator; +import android.annotation.AttrRes; import android.annotation.CallSuper; import android.annotation.ColorInt; import android.annotation.DrawableRes; @@ -5108,7 +5109,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private SparseIntArray mAttributeSourceResId; @Nullable - private int[] mAttributeResolutionStack; + private SparseArray<int[]> mAttributeResolutionStacks; @StyleRes private int mExplicitStyle; @@ -5963,11 +5964,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <b>Note:</b> this method will only return actual values if the view attribute debugging * is enabled in Android developer options. * + * @param attribute Attribute resource ID for which the resolution stack should be returned. * @return ordered list of resource ID that are considered when resolving attribute values for * this {@link View}. */ @NonNull - public List<Integer> getAttributeResolutionStack() { + public List<Integer> getAttributeResolutionStack(@AttrRes int attribute) { ArrayList<Integer> stack = new ArrayList<>(); if (!sDebugViewAttributes) { return stack; @@ -5975,8 +5977,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mSourceLayoutId != ID_NULL) { stack.add(mSourceLayoutId); } - for (int i = 0; i < mAttributeResolutionStack.length; i++) { - stack.add(mAttributeResolutionStack[i]); + int[] attributeResolutionStack = mAttributeResolutionStacks.get(attribute); + if (attributeResolutionStack == null) { + return stack; + } + for (int i = 0; i < attributeResolutionStack.length; i++) { + stack.add(attributeResolutionStack[i]); } return stack; } @@ -6143,9 +6149,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return; } - mAttributeResolutionStack = context.getTheme().getAttributeResolutionStack( + int[] attributeResolutionStack = context.getTheme().getAttributeResolutionStack( defStyleAttr, defStyleRes, mExplicitStyle); + if (mAttributeResolutionStacks == null) { + mAttributeResolutionStacks = new SparseArray<>(); + } + if (mAttributeSourceResId == null) { mAttributeSourceResId = new SparseIntArray(); } @@ -6154,6 +6164,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, for (int j = 0; j < indexCount; ++j) { final int index = t.getIndex(j); mAttributeSourceResId.append(styleable[index], t.getSourceResourceId(index, 0)); + mAttributeResolutionStacks.append(styleable[index], attributeResolutionStack); } } @@ -9476,7 +9487,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Gets the session used to notify Content Capture events. * * @return session explicitly set by {@link #setContentCaptureSession(ContentCaptureSession)}, - * inherited by ancestore, default session or {@code null} if content capture is disabled for + * inherited by ancestors, default session or {@code null} if content capture is disabled for * this view. */ @Nullable diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 1a782ee5d3dd..b1fee2d17079 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -3404,21 +3404,25 @@ public final class ViewRootImpl implements ViewParent, .captureFrameCommitCallbacks(); if (mReportNextDraw) { usingAsyncReport = true; - mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> { - // TODO: Use the frame number - pendingDrawFinished(); - if (commitCallbacks != null) { - for (int i = 0; i < commitCallbacks.size(); i++) { - commitCallbacks.get(i).run(); - } - } - }); + final Handler handler = mAttachInfo.mHandler; + mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> + handler.post(() -> { + // TODO: Use the frame number + pendingDrawFinished(); + if (commitCallbacks != null) { + for (int i = 0; i < commitCallbacks.size(); i++) { + commitCallbacks.get(i).run(); + } + } + })); } else if (commitCallbacks != null && commitCallbacks.size() > 0) { - mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> { - for (int i = 0; i < commitCallbacks.size(); i++) { - commitCallbacks.get(i).run(); - } - }); + final Handler handler = mAttachInfo.mHandler; + mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> + handler.post(() -> { + for (int i = 0; i < commitCallbacks.size(); i++) { + commitCallbacks.get(i).run(); + } + })); } } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 5e5c8265f782..f93ac4ce7d16 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -1782,10 +1782,6 @@ public final class AutofillManager { /** * Explicitly limits augmented autofill to the given packages and activities. * - * <p>When the whitelist is set, it overrides the values passed to - * {@link #setActivityAugmentedAutofillEnabled(ComponentName, boolean)} - * and {@link #setPackageAugmentedAutofillEnabled(String, boolean)}. - * * <p>To reset the whitelist, call it passing {@code null} to both arguments. * * <p>Useful when the service wants to restrict augmented autofill to a category of apps, like @@ -1803,8 +1799,6 @@ public final class AutofillManager { */ @SystemApi @TestApi - //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service - //in the same package as the test, and that module is compiled with SDK=test_current public void setAugmentedAutofillWhitelist(@Nullable List<String> packages, @Nullable List<ComponentName> activities) { // TODO(b/123100824): implement diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java index acb81e086461..13e8a6584218 100644 --- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java @@ -20,10 +20,6 @@ import android.annotation.Nullable; import android.view.autofill.AutofillId; import android.view.contentcapture.ViewNode.ViewStructureImpl; -import com.android.internal.util.Preconditions; - -import java.io.PrintWriter; - /** * A session that is explicitly created by the app (and hence is a descendant of * {@link MainContentCaptureSession}). @@ -35,21 +31,11 @@ final class ChildContentCaptureSession extends ContentCaptureSession { @NonNull private final ContentCaptureSession mParent; - /** - * {@link ContentCaptureContext} set by client, or {@code null} when it's the - * {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the - * context. - * - * @hide - */ - @NonNull - private final ContentCaptureContext mClientContext; - /** @hide */ protected ChildContentCaptureSession(@NonNull ContentCaptureSession parent, @NonNull ContentCaptureContext clientContext) { + super(clientContext); mParent = parent; - mClientContext = Preconditions.checkNotNull(clientContext); } @Override @@ -73,6 +59,11 @@ final class ChildContentCaptureSession extends ContentCaptureSession { } @Override + public void updateContentCaptureContext(@Nullable ContentCaptureContext context) { + getMainCaptureSession().notifyContextUpdated(mId, context); + } + + @Override void onDestroy() { getMainCaptureSession().notifyChildSessionFinished(mParent.mId, mId); } @@ -101,13 +92,4 @@ final class ChildContentCaptureSession extends ContentCaptureSession { boolean isContentCaptureEnabled() { return getMainCaptureSession().isContentCaptureEnabled(); } - - @Override - void dump(String prefix, PrintWriter pw) { - if (mClientContext != null) { - // NOTE: we don't dump clientContent because it could have PII - pw.print(prefix); pw.println("hasClientContext"); - } - super.dump(prefix, pw); - } } diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java index 22254cd94059..9cdbefac3d1d 100644 --- a/core/java/android/view/contentcapture/ContentCaptureEvent.java +++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java @@ -91,13 +91,22 @@ public final class ContentCaptureEvent implements Parcelable { */ public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; + /** + * Called after a call to + * {@link ContentCaptureSession#setContentCaptureContext(ContentCaptureContext)}. + * + * <p>The passed context is available through {@link #getContentCaptureContext()}. + */ + public static final int TYPE_CONTEXT_UPDATED = 6; + /** @hide */ @IntDef(prefix = { "TYPE_" }, value = { TYPE_VIEW_APPEARED, TYPE_VIEW_DISAPPEARED, TYPE_VIEW_TEXT_CHANGED, TYPE_INITIAL_VIEW_TREE_APPEARING, - TYPE_INITIAL_VIEW_TREE_APPEARED + TYPE_INITIAL_VIEW_TREE_APPEARED, + TYPE_CONTEXT_UPDATED }) @Retention(RetentionPolicy.SOURCE) public @interface EventType{} @@ -193,12 +202,13 @@ public final class ContentCaptureEvent implements Parcelable { } /** - * Used by {@link #TYPE_SESSION_STARTED}. + * Gets the {@link ContentCaptureContext} set calls to + * {@link ContentCaptureSession#setContentCaptureContext(ContentCaptureContext)}. * - * @hide + * <p>Only set on {@link #TYPE_CONTEXT_UPDATED} events. */ @Nullable - public ContentCaptureContext getClientContext() { + public ContentCaptureContext getContentCaptureContext() { return mClientContext; } @@ -220,8 +230,8 @@ public final class ContentCaptureEvent implements Parcelable { * Gets the type of the event. * * @return one of {@link #TYPE_VIEW_APPEARED}, {@link #TYPE_VIEW_DISAPPEARED}, - * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_INITIAL_VIEW_TREE_APPEARING}, or - * {@link #TYPE_INITIAL_VIEW_TREE_APPEARED}. + * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_INITIAL_VIEW_TREE_APPEARING}, + * {@link #TYPE_INITIAL_VIEW_TREE_APPEARED}, or {@link #TYPE_CONTEXT_UPDATED}. */ public @EventType int getType() { return mType; @@ -299,6 +309,10 @@ public final class ContentCaptureEvent implements Parcelable { if (mText != null) { pw.print(", text="); pw.println(getSanitizedString(mText)); } + if (mClientContext != null) { + pw.print(", context="); mClientContext.dump(pw); pw.println(); + + } } @Override @@ -325,6 +339,9 @@ public final class ContentCaptureEvent implements Parcelable { if (mText != null) { string.append(", text=").append(getSanitizedString(mText)); } + if (mClientContext != null) { + string.append(", context=").append(mClientContext); + } return string.append(']').toString(); } @@ -345,7 +362,7 @@ public final class ContentCaptureEvent implements Parcelable { if (mType == TYPE_SESSION_STARTED || mType == TYPE_SESSION_FINISHED) { parcel.writeString(mParentSessionId); } - if (mType == TYPE_SESSION_STARTED) { + if (mType == TYPE_SESSION_STARTED || mType == TYPE_CONTEXT_UPDATED) { parcel.writeParcelable(mClientContext, flags); } } @@ -375,7 +392,7 @@ public final class ContentCaptureEvent implements Parcelable { if (type == TYPE_SESSION_STARTED || type == TYPE_SESSION_FINISHED) { event.setParentSessionId(parcel.readString()); } - if (type == TYPE_SESSION_STARTED) { + if (type == TYPE_SESSION_STARTED || type == TYPE_CONTEXT_UPDATED) { event.setClientContext(parcel.readParcelable(null)); } return event; @@ -404,6 +421,8 @@ public final class ContentCaptureEvent implements Parcelable { return "INITIAL_VIEW_HIERARCHY_STARTED"; case TYPE_INITIAL_VIEW_TREE_APPEARED: return "INITIAL_VIEW_HIERARCHY_FINISHED"; + case TYPE_CONTEXT_UPDATED: + return "CONTEXT_UPDATED"; default: return "UKNOWN_TYPE: " + type; } diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index e028961692f9..b8d3fa6f4404 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -166,6 +166,14 @@ public abstract class ContentCaptureSession implements AutoCloseable { private ContentCaptureSessionId mContentCaptureSessionId; /** + * {@link ContentCaptureContext} set by client, or {@code null} when it's the + * {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the + * context. + */ + @Nullable + private ContentCaptureContext mClientContext; + + /** * List of children session. */ @Nullable @@ -183,6 +191,12 @@ public abstract class ContentCaptureSession implements AutoCloseable { mId = Preconditions.checkNotNull(id); } + // Used by ChildCOntentCaptureSession + ContentCaptureSession(@NonNull ContentCaptureContext initialContext) { + this(); + mClientContext = Preconditions.checkNotNull(initialContext); + } + /** @hide */ @NonNull abstract MainContentCaptureSession getMainCaptureSession(); @@ -240,6 +254,30 @@ public abstract class ContentCaptureSession implements AutoCloseable { abstract void flush(@FlushReason int reason); /** + * Sets the {@link ContentCaptureContext} associated with the session. + * + * <p>Typically used to change the context associated with the default session from an activity. + */ + public final void setContentCaptureContext(@Nullable ContentCaptureContext context) { + mClientContext = context; + updateContentCaptureContext(context); + } + + abstract void updateContentCaptureContext(@Nullable ContentCaptureContext context); + + /** + * Gets the {@link ContentCaptureContext} associated with the session. + * + * @return context set on constructor or by + * {@link #setContentCaptureContext(ContentCaptureContext)}, or {@code null} if never + * explicitly set. + */ + @Nullable + public final ContentCaptureContext getContentCaptureContext() { + return mClientContext; + } + + /** * Destroys this session, flushing out all pending notifications to the service. * * <p>Once destroyed, any new notification will be dropped. @@ -424,6 +462,9 @@ public abstract class ContentCaptureSession implements AutoCloseable { @CallSuper void dump(@NonNull String prefix, @NonNull PrintWriter pw) { pw.print(prefix); pw.print("id: "); pw.println(mId); + if (mClientContext != null) { + pw.print(prefix); mClientContext.dump(pw); pw.println(); + } synchronized (mLock) { pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed); if (mChildren != null && !mChildren.isEmpty()) { diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index 810c967ce2c8..d949f45ba5e6 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -15,6 +15,7 @@ */ package android.view.contentcapture; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARING; import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED; @@ -269,11 +270,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession { private void sendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) { final int eventType = event.getType(); if (VERBOSE) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event); - if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED) { + if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED + && eventType != ContentCaptureEvent.TYPE_CONTEXT_UPDATED) { // TODO(b/120494182): comment when this could happen (dialogs?) Log.v(TAG, "handleSendEvent(" + getDebugState() + ", " + ContentCaptureEvent.getTypeAsString(eventType) - + "): session not started yet"); + + "): dropping because session not started yet"); return; } if (mDisabled.get()) { @@ -476,6 +478,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } } + @Override + public void updateContentCaptureContext(@Nullable ContentCaptureContext context) { + notifyContextUpdated(mId, context); + } + /** * Resets the buffer and return a {@link ParceledListSlice} with the previous events. */ @@ -613,6 +620,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } } + void notifyContextUpdated(@NonNull String sessionId, + @Nullable ContentCaptureContext context) { + sendEvent(new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED) + .setClientContext(context)); + } + @Override void dump(@NonNull String prefix, @NonNull PrintWriter pw) { pw.print(prefix); pw.print("mContext: "); pw.println(mContext); diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 2171fc52a0ba..3555822b1f1f 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -760,7 +760,7 @@ public class WebView extends AbsoluteLayout * <p> * The {@code encoding} parameter specifies whether the data is base64 or URL * encoded. If the data is base64 encoded, the value of the encoding - * parameter must be 'base64'. HTML can be encoded with {@link + * parameter must be {@code "base64"}. HTML can be encoded with {@link * android.util.Base64#encodeToString(byte[],int)} like so: * <pre> * String unencodedHtml = @@ -768,11 +768,15 @@ public class WebView extends AbsoluteLayout * String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(), Base64.NO_PADDING); * webView.loadData(encodedHtml, "text/html", "base64"); * </pre> - * <p> + * <p class="note"> * For all other values of {@code encoding} (including {@code null}) it is assumed that the * data uses ASCII encoding for octets inside the range of safe URL characters and use the * standard %xx hex encoding of URLs for octets outside that range. See <a * href="https://tools.ietf.org/html/rfc3986#section-2.2">RFC 3986</a> for more information. + * Applications targeting {@link android.os.Build.VERSION_CODES#Q} or later must either use + * base64 or encode any {@code #} characters in the content as {@code %23}, otherwise they + * will be treated as the end of the content and the remaining text used as a document + * fragment identifier. * <p> * The {@code mimeType} parameter specifies the format of the data. * If WebView can't handle the specified MIME type, it will download the data. @@ -820,7 +824,8 @@ public class WebView extends AbsoluteLayout * <p> * If the base URL uses the data scheme, this method is equivalent to * calling {@link #loadData(String,String,String) loadData()} and the - * historyUrl is ignored, and the data will be treated as part of a data: URL. + * historyUrl is ignored, and the data will be treated as part of a data: URL, + * including the requirement that the content be URL-encoded or base64 encoded. * If the base URL uses any other scheme, then the data will be loaded into * the WebView as a plain string (i.e. not part of a data URL) and any URL-encoded * entities in the string will not be decoded. diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index 904a86261e6c..89e205caa693 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -1230,9 +1230,16 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe * Ensures that the drop down is not obscuring the IME. * @param visible whether the ime should be in front. If false, the ime is pushed to * the background. + * + * This method is deprecated. Please use the following methods instead. + * Use {@link #setInputMethodMode} to ensure that the drop down is not obscuring the IME. + * Use {@link #showDropDown()} to show the drop down immediately + * A combination of {@link #isDropDownAlwaysVisible()} and {@link #enoughToFilter()} to decide + * whether to manually trigger {@link #showDropDown()} or not. + * * @hide internal used only here and SearchDialog */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768913) public void ensureImeVisible(boolean visible) { mPopup.setInputMethodMode(visible ? ListPopupWindow.INPUT_METHOD_NEEDED : ListPopupWindow.INPUT_METHOD_NOT_NEEDED); @@ -1242,14 +1249,39 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } /** - * @hide internal used only here and SearchDialog + * This method is deprecated. Please use {@link #getInputMethodMode()} instead. + * + * @hide This API is not being used and can be removed. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public boolean isInputMethodNotNeeded() { return mPopup.getInputMethodMode() == ListPopupWindow.INPUT_METHOD_NOT_NEEDED; } /** + * Returns the input method mode used by the auto complete dropdown. + */ + public int getInputMethodMode() { + return mPopup.getInputMethodMode(); + } + + /** + * Use this method to specify when the IME should be displayed. This function can be used to + * prevent the dropdown from obscuring the IME. + * + * @param mode speficies the input method mode. use one of the following values: + * + * {@link ListPopupWindow#INPUT_METHOD_FROM_FOCUSABLE} IME Displayed if the auto-complete box is + * focusable. + * {@link ListPopupWindow#INPUT_METHOD_NEEDED} Always display the IME. + * {@link ListPopupWindow#INPUT_METHOD_NOT_NEEDED}. The auto-complete suggestions are always + * displayed, even if the suggestions cover/hide the input method. + */ + public void setInputMethodMode(int mode) { + mPopup.setInputMethodMode(mode); + } + + /** * <p>Displays the drop down on screen.</p> */ public void showDropDown() { diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 8ebcef5133b6..fbf80914e2f1 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -46,6 +46,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; +import android.content.res.Configuration; import android.database.Cursor; import android.database.DataSetObserver; import android.graphics.Bitmap; @@ -486,6 +487,27 @@ public class ChooserActivity extends ResolverActivity { } } + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + int width = -1; + if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { + width = getResources().getDimensionPixelSize(R.dimen.chooser_preview_width); + } + + updateLayoutWidth(R.id.content_preview_text_layout, width); + updateLayoutWidth(R.id.content_preview_title_layout, width); + updateLayoutWidth(R.id.content_preview_file_layout, width); + } + + private void updateLayoutWidth(int layoutResourceId, int width) { + View view = findViewById(layoutResourceId); + LayoutParams params = view.getLayoutParams(); + params.width = width; + view.setLayoutParams(params); + } + private void displayContentPreview(@ContentPreviewType int previewType, Intent targetIntent) { switch (previewType) { case CONTENT_PREVIEW_TEXT: @@ -633,40 +655,46 @@ public class ChooserActivity extends ResolverActivity { // due to permissions issues findViewById(R.id.file_copy_button).setVisibility(View.GONE); - ContentResolver resolver = getContentResolver(); - TextView fileNameView = findViewById(R.id.content_preview_filename); - String action = targetIntent.getAction(); - if (Intent.ACTION_SEND.equals(action)) { - Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); + try { + ContentResolver resolver = getContentResolver(); + TextView fileNameView = findViewById(R.id.content_preview_filename); + String action = targetIntent.getAction(); + if (Intent.ACTION_SEND.equals(action)) { + Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); - FileInfo fileInfo = extractFileInfo(uri, resolver); - fileNameView.setText(fileInfo.name); + FileInfo fileInfo = extractFileInfo(uri, resolver); + fileNameView.setText(fileInfo.name); - if (fileInfo.hasThumbnail) { - loadUriIntoView(R.id.content_preview_file_thumbnail, uri); + if (fileInfo.hasThumbnail) { + loadUriIntoView(R.id.content_preview_file_thumbnail, uri); + } else { + ImageView fileIconView = findViewById(R.id.content_preview_file_icon); + fileIconView.setVisibility(View.VISIBLE); + fileIconView.setImageResource(R.drawable.ic_doc_generic); + } } else { + List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); + if (uris.size() == 0) { + contentPreviewLayout.setVisibility(View.GONE); + Log.i(TAG, + "Appears to be no uris available in EXTRA_STREAM, removing preview " + + "area"); + return; + } + + FileInfo fileInfo = extractFileInfo(uris.get(0), resolver); + int remFileCount = uris.size() - 1; + String fileName = getResources().getQuantityString(R.plurals.file_count, + remFileCount, fileInfo.name, remFileCount); + + fileNameView.setText(fileName); ImageView fileIconView = findViewById(R.id.content_preview_file_icon); fileIconView.setVisibility(View.VISIBLE); - fileIconView.setImageResource(R.drawable.ic_doc_generic); + fileIconView.setImageResource(R.drawable.ic_file_copy); } - } else { - List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); - if (uris.size() == 0) { - contentPreviewLayout.setVisibility(View.GONE); - Log.i(TAG, - "Appears to be no uris available in EXTRA_STREAM, removing preview area"); - return; - } - - FileInfo fileInfo = extractFileInfo(uris.get(0), resolver); - int remFileCount = uris.size() - 1; - String fileName = getResources().getQuantityString(R.plurals.file_count, - remFileCount, fileInfo.name, remFileCount); - - fileNameView.setText(fileName); - ImageView fileIconView = findViewById(R.id.content_preview_file_icon); - fileIconView.setVisibility(View.VISIBLE); - fileIconView.setImageResource(R.drawable.ic_file_copy); + } catch (SecurityException e) { + Log.w(TAG, "Error loading file preview", e); + contentPreviewLayout.setVisibility(View.GONE); } } diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl index 8dde44e6bfee..9ce7ed1562ce 100644 --- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl @@ -153,17 +153,7 @@ interface IVoiceInteractionManagerService { in IVoiceActionCheckCallback callback); /** - * Sets the transcribed voice to the given string. + * Provide hints for showing UI. */ - void setTranscription(IVoiceInteractionService service, String transcription); - - /** - * Indicates that the transcription session is finished. - */ - void clearTranscription(IVoiceInteractionService service, boolean immediate); - - /** - * Sets the voice state indication based upon the given value. - */ - void setVoiceState(IVoiceInteractionService service, int state); + void setUiHints(in IVoiceInteractionService service, in Bundle hints); } diff --git a/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl index 674ad5b4ab67..bc757e24c852 100644 --- a/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl @@ -16,6 +16,8 @@ package com.android.internal.app; + import android.os.Bundle; + oneway interface IVoiceInteractionSessionListener { /** * Called when a voice session is shown. @@ -28,18 +30,7 @@ void onVoiceSessionHidden(); /** - * Called when voice assistant transcription has been updated to the given string. - */ - void onTranscriptionUpdate(in String transcription); - - /** - * Called when voice transcription is completed. - */ - void onTranscriptionComplete(in boolean immediate); - - /** - * Called when the voice assistant's state has changed. Values are from - * VoiceInteractionService's VOICE_STATE* constants. + * Called when UI hints were received. */ - void onVoiceStateChange(in int state); + void onSetUiHints(in Bundle args); }
\ No newline at end of file diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 52e1748c621c..4ff99482feff 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -187,6 +187,8 @@ public class BatteryStatsImpl extends BatteryStats { static final int MSG_REPORT_RESET_STATS = 4; static final long DELAY_UPDATE_WAKELOCKS = 5*1000; + private static final double MILLISECONDS_IN_HOUR = 3600 * 1000; + private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader(); private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats(); @@ -252,6 +254,9 @@ public class BatteryStatsImpl extends BatteryStats { private static final long RPM_STATS_UPDATE_FREQ_MS = 1000; /** Last time that RPM stats were updated by updateRpmStatsLocked. */ private long mLastRpmStatsUpdateTimeMs = -RPM_STATS_UPDATE_FREQ_MS; + + /** Container for Rail Energy Data stats. */ + private final RailStats mTmpRailStats = new RailStats(); /** * Use a queue to delay removing UIDs from {@link KernelCpuUidUserSysTimeReader}, * {@link KernelCpuUidActiveTimeReader}, {@link KernelCpuUidClusterTimeReader}, @@ -327,6 +332,15 @@ public class BatteryStatsImpl extends BatteryStats { public String getSubsystemLowPowerStats(); } + /** interface to update rail information for power monitor */ + public interface RailEnergyDataCallback { + /** Function to fill the map for the rail data stats + * Used for power monitoring feature + * @param railStats + */ + void fillRailDataStats(RailStats railStats); + } + public static abstract class UserInfoProvider { private int[] userIds; protected abstract @Nullable int[] getUserIds(); @@ -361,6 +375,8 @@ public class BatteryStatsImpl extends BatteryStats { } }; + public final RailEnergyDataCallback mRailEnergyDataCallback; + /** * This handler is running on {@link BackgroundThread}. */ @@ -593,7 +609,9 @@ public class BatteryStatsImpl extends BatteryStats { int UPDATE_RADIO = 0x04; int UPDATE_BT = 0x08; int UPDATE_RPM = 0x10; // 16 - int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM; + int UPDATE_RAIL = 0x20; // 32 + int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM + | UPDATE_RAIL; Future<?> scheduleSync(String reason, int flags); Future<?> scheduleCpuSyncDueToRemovedUid(int uid); @@ -1078,6 +1096,7 @@ public class BatteryStatsImpl extends BatteryStats { mBatteryStatsHistory = null; mHandler = null; mPlatformIdleStateCallback = null; + mRailEnergyDataCallback = null; mUserInfoProvider = null; mConstants = new Constants(mHandler); clearHistoryLocked(); @@ -3005,6 +3024,7 @@ public class BatteryStatsImpl extends BatteryStats { private final LongSamplingCounter mRxTimeMillis; private final LongSamplingCounter[] mTxTimeMillis; private final LongSamplingCounter mPowerDrainMaMs; + private final LongSamplingCounter mMonitoredRailChargeConsumedMaMs; public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates) { mIdleTimeMillis = new LongSamplingCounter(timeBase); @@ -3016,6 +3036,7 @@ public class BatteryStatsImpl extends BatteryStats { mTxTimeMillis[i] = new LongSamplingCounter(timeBase); } mPowerDrainMaMs = new LongSamplingCounter(timeBase); + mMonitoredRailChargeConsumedMaMs = new LongSamplingCounter(timeBase); } public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates, Parcel in) { @@ -3033,6 +3054,7 @@ public class BatteryStatsImpl extends BatteryStats { mTxTimeMillis[i] = new LongSamplingCounter(timeBase, in); } mPowerDrainMaMs = new LongSamplingCounter(timeBase, in); + mMonitoredRailChargeConsumedMaMs = new LongSamplingCounter(timeBase, in); } public void readSummaryFromParcel(Parcel in) { @@ -3048,6 +3070,7 @@ public class BatteryStatsImpl extends BatteryStats { counter.readSummaryFromParcelLocked(in); } mPowerDrainMaMs.readSummaryFromParcelLocked(in); + mMonitoredRailChargeConsumedMaMs.readSummaryFromParcelLocked(in); } @Override @@ -3065,6 +3088,7 @@ public class BatteryStatsImpl extends BatteryStats { counter.writeSummaryFromParcelLocked(dest); } mPowerDrainMaMs.writeSummaryFromParcelLocked(dest); + mMonitoredRailChargeConsumedMaMs.writeSummaryFromParcelLocked(dest); } @Override @@ -3078,6 +3102,7 @@ public class BatteryStatsImpl extends BatteryStats { counter.writeToParcel(dest); } mPowerDrainMaMs.writeToParcel(dest); + mMonitoredRailChargeConsumedMaMs.writeToParcel(dest); } public void reset(boolean detachIfReset) { @@ -3089,6 +3114,7 @@ public class BatteryStatsImpl extends BatteryStats { counter.reset(detachIfReset); } mPowerDrainMaMs.reset(detachIfReset); + mMonitoredRailChargeConsumedMaMs.reset(detachIfReset); } public void detach() { @@ -3100,6 +3126,7 @@ public class BatteryStatsImpl extends BatteryStats { counter.detach(); } mPowerDrainMaMs.detach(); + mMonitoredRailChargeConsumedMaMs.detach(); } /** @@ -3154,6 +3181,15 @@ public class BatteryStatsImpl extends BatteryStats { public LongSamplingCounter getPowerCounter() { return mPowerDrainMaMs; } + + /** + * @return a LongSamplingCounter, measuring actual monitored rail energy consumed + * milli-ampere milli-seconds (mAmS). + */ + @Override + public LongSamplingCounter getMonitoredRailChargeConsumedMaMs() { + return mMonitoredRailChargeConsumedMaMs; + } } /** Get Resource Power Manager stats. Create a new one if it doesn't already exist. */ @@ -3497,6 +3533,8 @@ public class BatteryStatsImpl extends BatteryStats { if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryChargeUAh=" + cur.batteryChargeUAh); dest.writeInt(cur.batteryChargeUAh); } + dest.writeDouble(cur.modemRailChargeMah); + dest.writeDouble(cur.wifiRailChargeMah); } private int buildBatteryLevelInt(HistoryItem h) { @@ -3747,6 +3785,8 @@ public class BatteryStatsImpl extends BatteryStats { if ((firstToken&DELTA_BATTERY_CHARGE_FLAG) != 0) { cur.batteryChargeUAh = src.readInt(); } + cur.modemRailChargeMah = src.readDouble(); + cur.wifiRailChargeMah = src.readDouble(); } @Override @@ -10111,12 +10151,12 @@ public class BatteryStatsImpl extends BatteryStats { } public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb, - UserInfoProvider userInfoProvider) { - this(new SystemClocks(), systemDir, handler, cb, userInfoProvider); + RailEnergyDataCallback railStatsCb, UserInfoProvider userInfoProvider) { + this(new SystemClocks(), systemDir, handler, cb, railStatsCb, userInfoProvider); } private BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler, - PlatformIdleStateCallback cb, + PlatformIdleStateCallback cb, RailEnergyDataCallback railStatsCb, UserInfoProvider userInfoProvider) { init(clocks); @@ -10218,6 +10258,7 @@ public class BatteryStatsImpl extends BatteryStats { clearHistoryLocked(); updateDailyDeadlineLocked(); mPlatformIdleStateCallback = cb; + mRailEnergyDataCallback = railStatsCb; mUserInfoProvider = userInfoProvider; } @@ -10238,6 +10279,7 @@ public class BatteryStatsImpl extends BatteryStats { mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer); readFromParcel(p); mPlatformIdleStateCallback = null; + mRailEnergyDataCallback = null; } public void setPowerProfileLocked(PowerProfile profile) { @@ -10934,6 +10976,8 @@ public class BatteryStatsImpl extends BatteryStats { mWakeupReasonStats.clear(); } + mTmpRailStats.reset(); + mLastHistoryStepDetails = null; mLastStepCpuUserTime = mLastStepCpuSystemTime = 0; mCurStepCpuUserTime = mCurStepCpuSystemTime = 0; @@ -11321,6 +11365,16 @@ public class BatteryStatsImpl extends BatteryStats { mWifiActivity.getPowerCounter().addCountLocked( (long) (info.getControllerEnergyUsed() / opVolt)); } + // Converting uWs to mAms. + // Conversion: (uWs * (1000ms / 1s) * (1mW / 1000uW)) / mV = mAms + long monitoredRailChargeConsumedMaMs = + (long) (mTmpRailStats.getWifiTotalEnergyUseduWs() / opVolt); + mWifiActivity.getMonitoredRailChargeConsumedMaMs().addCountLocked( + monitoredRailChargeConsumedMaMs); + mHistoryCur.wifiRailChargeMah += + (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR); + addHistoryRecordLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis()); + mTmpRailStats.resetWifiTotalEnergyUsed(); } } } @@ -11411,9 +11465,18 @@ public class BatteryStatsImpl extends BatteryStats { // We store the power drain as mAms. mModemActivity.getPowerCounter().addCountLocked((long) energyUsed); + // Converting uWs to mAms. + // Conversion: (uWs * (1000ms / 1s) * (1mW / 1000uW)) / mV = mAms + long monitoredRailChargeConsumedMaMs = + (long) (mTmpRailStats.getCellularTotalEnergyUseduWs() / opVolt); + mModemActivity.getMonitoredRailChargeConsumedMaMs().addCountLocked( + monitoredRailChargeConsumedMaMs); + mHistoryCur.modemRailChargeMah += + (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR); + addHistoryRecordLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis()); + mTmpRailStats.resetCellularTotalEnergyUsed(); } } - final long elapsedRealtimeMs = mClocks.elapsedRealtime(); long radioTime = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked( elapsedRealtimeMs * 1000); @@ -11812,6 +11875,16 @@ public class BatteryStatsImpl extends BatteryStats { } /** + * Read and record Rail Energy data. + */ + public void updateRailStatsLocked() { + if (mRailEnergyDataCallback == null || !mTmpRailStats.isRailStatsAvailable()) { + return; + } + mRailEnergyDataCallback.fillRailDataStats(mTmpRailStats); + } + + /** * Read and distribute kernel wake lock use across apps. */ public void updateKernelWakelocksLocked() { @@ -12950,6 +13023,8 @@ public class BatteryStatsImpl extends BatteryStats { final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which); final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which); final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which); + final long monitoredRailChargeConsumedMaMs = + counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which); long[] timeInRatMs = new long[BatteryStats.NUM_DATA_CONNECTION_TYPES]; for (int i = 0; i < timeInRatMs.length; i++) { timeInRatMs[i] = getPhoneDataConnectionTime(i, rawRealTime, which) / 1000; @@ -12979,58 +13054,62 @@ public class BatteryStatsImpl extends BatteryStats { s.setTimeInRatMs(timeInRatMs); s.setTimeInRxSignalStrengthLevelMs(timeInRxSignalStrengthLevelMs); s.setTxTimeMs(txTimeMs); + s.setMonitoredRailChargeConsumedMaMs(monitoredRailChargeConsumedMaMs); return s; } - /*@hide */ - public WifiBatteryStats getWifiBatteryStats() { - WifiBatteryStats s = new WifiBatteryStats(); - final int which = STATS_SINCE_CHARGED; - final long rawRealTime = SystemClock.elapsedRealtime() * 1000; - final ControllerActivityCounter counter = getWifiControllerActivity(); - final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which); - final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which); - final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which); - final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(which); - final long totalControllerActivityTimeMs - = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000; - final long sleepTimeMs - = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + txTimeMs); - final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which); - long numAppScanRequest = 0; - for (int i = 0; i < mUidStats.size(); i++) { - numAppScanRequest += mUidStats.valueAt(i).mWifiScanTimer.getCountLocked(which); - } - long[] timeInStateMs = new long[NUM_WIFI_STATES]; - for (int i=0; i<NUM_WIFI_STATES; i++) { + /*@hide */ + public WifiBatteryStats getWifiBatteryStats() { + WifiBatteryStats s = new WifiBatteryStats(); + final int which = STATS_SINCE_CHARGED; + final long rawRealTime = SystemClock.elapsedRealtime() * 1000; + final ControllerActivityCounter counter = getWifiControllerActivity(); + final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which); + final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which); + final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which); + final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(which); + final long totalControllerActivityTimeMs + = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000; + final long sleepTimeMs + = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + txTimeMs); + final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which); + final long monitoredRailChargeConsumedMaMs = + counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which); + long numAppScanRequest = 0; + for (int i = 0; i < mUidStats.size(); i++) { + numAppScanRequest += mUidStats.valueAt(i).mWifiScanTimer.getCountLocked(which); + } + long[] timeInStateMs = new long[NUM_WIFI_STATES]; + for (int i=0; i<NUM_WIFI_STATES; i++) { timeInStateMs[i] = getWifiStateTime(i, rawRealTime, which) / 1000; - } - long[] timeInSupplStateMs = new long[NUM_WIFI_SUPPL_STATES]; - for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) { - timeInSupplStateMs[i] = getWifiSupplStateTime(i, rawRealTime, which) / 1000; - } - long[] timeSignalStrengthTimeMs = new long[NUM_WIFI_SIGNAL_STRENGTH_BINS]; - for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) { - timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTime, which) / 1000; - } - s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000); - s.setKernelActiveTimeMs(getWifiActiveTime(rawRealTime, which) / 1000); - s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which)); - s.setNumBytesTx(getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which)); - s.setNumPacketsRx(getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which)); - s.setNumBytesRx(getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which)); - s.setSleepTimeMs(sleepTimeMs); - s.setIdleTimeMs(idleTimeMs); - s.setRxTimeMs(rxTimeMs); - s.setTxTimeMs(txTimeMs); - s.setScanTimeMs(scanTimeMs); - s.setEnergyConsumedMaMs(energyConsumedMaMs); - s.setNumAppScanRequest(numAppScanRequest); - s.setTimeInStateMs(timeInStateMs); - s.setTimeInSupplicantStateMs(timeInSupplStateMs); - s.setTimeInRxSignalStrengthLevelMs(timeSignalStrengthTimeMs); - return s; - } + } + long[] timeInSupplStateMs = new long[NUM_WIFI_SUPPL_STATES]; + for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) { + timeInSupplStateMs[i] = getWifiSupplStateTime(i, rawRealTime, which) / 1000; + } + long[] timeSignalStrengthTimeMs = new long[NUM_WIFI_SIGNAL_STRENGTH_BINS]; + for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) { + timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTime, which) / 1000; + } + s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000); + s.setKernelActiveTimeMs(getWifiActiveTime(rawRealTime, which) / 1000); + s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which)); + s.setNumBytesTx(getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which)); + s.setNumPacketsRx(getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which)); + s.setNumBytesRx(getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which)); + s.setSleepTimeMs(sleepTimeMs); + s.setIdleTimeMs(idleTimeMs); + s.setRxTimeMs(rxTimeMs); + s.setTxTimeMs(txTimeMs); + s.setScanTimeMs(scanTimeMs); + s.setEnergyConsumedMaMs(energyConsumedMaMs); + s.setNumAppScanRequest(numAppScanRequest); + s.setTimeInStateMs(timeInStateMs); + s.setTimeInSupplicantStateMs(timeInSupplStateMs); + s.setTimeInRxSignalStrengthLevelMs(timeSignalStrengthTimeMs); + s.setMonitoredRailChargeConsumedMaMs(monitoredRailChargeConsumedMaMs); + return s; + } /*@hide */ public GpsBatteryStats getGpsBatteryStats() { diff --git a/core/java/com/android/internal/os/RailStats.java b/core/java/com/android/internal/os/RailStats.java new file mode 100644 index 000000000000..ff0083138b30 --- /dev/null +++ b/core/java/com/android/internal/os/RailStats.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.os; + +import android.util.ArrayMap; +import android.util.Slog; + +import java.util.Map; + +/** Rail Stats Power Monitoring Class */ +public final class RailStats { + private static final String TAG = "RailStats"; + + private static final String WIFI_SUBSYSTEM = "wifi"; + private static final String CELLULAR_SUBSYSTEM = "cellular"; + + private Map<Long, RailInfoData> mRailInfoData = new ArrayMap<>(); + + private long mCellularTotalEnergyUseduWs = 0; + private long mWifiTotalEnergyUseduWs = 0; + private boolean mRailStatsAvailability = true; + + /** Updates the rail data map of all power monitor rails being monitored + * Function is called from native side + * @param index + * @param railName + * @param subSystemName + * @param timestampSinceBootMs + * @param energyUsedSinceBootuWs + */ + public void updateRailData(long index, String railName, String subSystemName, + long timestampSinceBootMs, long energyUsedSinceBootuWs) { + if (!(subSystemName.equals(WIFI_SUBSYSTEM) || subSystemName.equals(CELLULAR_SUBSYSTEM))) { + return; + } + RailInfoData node = mRailInfoData.get(index); + if (node == null) { + mRailInfoData.put(index, new RailInfoData(index, railName, subSystemName, + timestampSinceBootMs, energyUsedSinceBootuWs)); + if (subSystemName.equals(WIFI_SUBSYSTEM)) { + mWifiTotalEnergyUseduWs += energyUsedSinceBootuWs; + return; + } + if (subSystemName.equals(CELLULAR_SUBSYSTEM)) { + mCellularTotalEnergyUseduWs += energyUsedSinceBootuWs; + } + return; + } + long timeSinceLastLogMs = timestampSinceBootMs - node.timestampSinceBootMs; + long energyUsedSinceLastLoguWs = energyUsedSinceBootuWs - node.energyUsedSinceBootuWs; + if (timeSinceLastLogMs < 0 || energyUsedSinceLastLoguWs < 0) { + energyUsedSinceLastLoguWs = node.energyUsedSinceBootuWs; + } + node.timestampSinceBootMs = timestampSinceBootMs; + node.energyUsedSinceBootuWs = energyUsedSinceBootuWs; + if (subSystemName.equals(WIFI_SUBSYSTEM)) { + mWifiTotalEnergyUseduWs += energyUsedSinceLastLoguWs; + return; + } + if (subSystemName.equals(CELLULAR_SUBSYSTEM)) { + mCellularTotalEnergyUseduWs += energyUsedSinceLastLoguWs; + } + } + + /** resets the cellular total energy used aspect. + */ + public void resetCellularTotalEnergyUsed() { + mCellularTotalEnergyUseduWs = 0; + } + + /** resets the wifi total energy used aspect. + */ + public void resetWifiTotalEnergyUsed() { + mWifiTotalEnergyUseduWs = 0; + } + + public long getCellularTotalEnergyUseduWs() { + return mCellularTotalEnergyUseduWs; + } + + public long getWifiTotalEnergyUseduWs() { + return mWifiTotalEnergyUseduWs; + } + + /** reset the total energy subsystems + * + */ + public void reset() { + mCellularTotalEnergyUseduWs = 0; + mWifiTotalEnergyUseduWs = 0; + } + + public RailStats getRailStats() { + return this; + } + + public void setRailStatsAvailability(boolean railStatsAvailability) { + mRailStatsAvailability = railStatsAvailability; + } + + public boolean isRailStatsAvailable() { + return mRailStatsAvailability; + } + + /** Container class to contain rail data information */ + public static class RailInfoData { + private static final String TAG = "RailInfoData"; + public long index; + public String railName; + public String subSystemName; + public long timestampSinceBootMs; + public long energyUsedSinceBootuWs; + + private RailInfoData(long index, String railName, String subSystemName, + long timestampSinceBootMs, long energyUsedSinceBoot) { + this.index = index; + this.railName = railName; + this.subSystemName = subSystemName; + this.timestampSinceBootMs = timestampSinceBootMs; + this.energyUsedSinceBootuWs = energyUsedSinceBoot; + } + + /** print the rail data + * + */ + public void printData() { + Slog.d(TAG, "Index = " + index); + Slog.d(TAG, "RailName = " + railName); + Slog.d(TAG, "SubSystemName = " + subSystemName); + Slog.d(TAG, "TimestampSinceBootMs = " + timestampSinceBootMs); + Slog.d(TAG, "EnergyUsedSinceBootuWs = " + energyUsedSinceBootuWs); + } + } +} diff --git a/core/jni/OWNERS b/core/jni/OWNERS index 774c2242e144..7ff15f2e182d 100644 --- a/core/jni/OWNERS +++ b/core/jni/OWNERS @@ -6,4 +6,4 @@ per-file *Camera*,*camera* = shuzhenwang@google.com, yinchiayeh@google.com, zhij per-file android_net_* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com # Zygote -per-file com_android_inernal_os_Zygote.*,fd_utils.* = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com +per-file com_android_internal_os_Zygote.*,fd_utils.* = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index e8172172f5d2..8f007594dd67 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -355,9 +355,16 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, colorType = kN32_SkColorType; } + sk_sp<SkColorSpace> colorSpace; + if (colorType == kAlpha_8_SkColorType) { + colorSpace = nullptr; + } else { + colorSpace = GraphicsJNI::getNativeColorSpace(colorSpacePtr); + } + SkBitmap bitmap; bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType, - GraphicsJNI::getNativeColorSpace(colorSpacePtr))); + colorSpace)); sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(&bitmap); if (!nativeBitmap) { @@ -385,15 +392,17 @@ static bool bitmapCopyTo(SkBitmap* dst, SkColorType dstCT, const SkBitmap& src, case kRGB_565_SkColorType: dstInfo = dstInfo.makeAlphaType(kOpaque_SkAlphaType); break; - case kRGBA_F16_SkColorType: - // The caller does not have an opportunity to pass a dst color space. Assume that - // they want linear sRGB. - dstInfo = dstInfo.makeColorSpace(SkColorSpace::MakeSRGBLinear()); + case kAlpha_8_SkColorType: + dstInfo = dstInfo.makeColorSpace(nullptr); break; default: break; } + if (!dstInfo.colorSpace() && dstCT != kAlpha_8_SkColorType) { + dstInfo = dstInfo.makeColorSpace(SkColorSpace::MakeSRGB()); + } + if (!dst->setInfo(dstInfo)) { return false; } @@ -608,14 +617,6 @@ static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) { return static_cast<jint>(bitmap->getGenerationID()); } -static jboolean Bitmap_isConfigF16(JNIEnv* env, jobject, jlong bitmapHandle) { - LocalScopedBitmap bitmap(bitmapHandle); - if (bitmap->info().colorType() == kRGBA_F16_SkColorType) { - return JNI_TRUE; - } - return JNI_FALSE; -} - static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) { LocalScopedBitmap bitmap(bitmapHandle); if (bitmap->info().alphaType() == kPremul_SkAlphaType) { @@ -684,9 +685,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { const SkAlphaType alphaType = (SkAlphaType)p->readInt32(); const uint32_t colorSpaceSize = p->readUint32(); sk_sp<SkColorSpace> colorSpace; - if (kRGBA_F16_SkColorType == colorType) { - colorSpace = SkColorSpace::MakeSRGBLinear(); - } else if (colorSpaceSize > 0) { + if (colorSpaceSize > 0) { if (colorSpaceSize > kMaxColorSpaceSerializedBytes) { ALOGD("Bitmap_createFromParcel: Serialized SkColorSpace is larger than expected: " "%d bytes\n", colorSpaceSize); @@ -811,7 +810,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, p->writeInt32(bitmap.colorType()); p->writeInt32(bitmap.alphaType()); SkColorSpace* colorSpace = bitmap.colorSpace(); - if (colorSpace != nullptr && bitmap.colorType() != kRGBA_F16_SkColorType) { + if (colorSpace != nullptr) { sk_sp<SkData> data = colorSpace->serialize(); size_t size = data->size(); p->writeUint32(size); @@ -924,44 +923,14 @@ static jboolean Bitmap_isSRGBLinear(JNIEnv* env, jobject, jlong bitmapHandle) { return colorSpace == srgbLinear.get() ? JNI_TRUE : JNI_FALSE; } -static jboolean Bitmap_getColorSpace(JNIEnv* env, jobject, jlong bitmapHandle, - jfloatArray xyzArray, jfloatArray paramsArray) { - +static jobject Bitmap_computeColorSpace(JNIEnv* env, jobject, jlong bitmapHandle) { LocalScopedBitmap bitmapHolder(bitmapHandle); - if (!bitmapHolder.valid()) return JNI_FALSE; + if (!bitmapHolder.valid()) return nullptr; SkColorSpace* colorSpace = bitmapHolder->info().colorSpace(); - if (colorSpace == nullptr) return JNI_FALSE; - - skcms_Matrix3x3 xyzMatrix; - if (!colorSpace->toXYZD50(&xyzMatrix)) return JNI_FALSE; - - jfloat* xyz = env->GetFloatArrayElements(xyzArray, NULL); - xyz[0] = xyzMatrix.vals[0][0]; - xyz[1] = xyzMatrix.vals[1][0]; - xyz[2] = xyzMatrix.vals[2][0]; - xyz[3] = xyzMatrix.vals[0][1]; - xyz[4] = xyzMatrix.vals[1][1]; - xyz[5] = xyzMatrix.vals[2][1]; - xyz[6] = xyzMatrix.vals[0][2]; - xyz[7] = xyzMatrix.vals[1][2]; - xyz[8] = xyzMatrix.vals[2][2]; - env->ReleaseFloatArrayElements(xyzArray, xyz, 0); - - skcms_TransferFunction transferParams; - if (!colorSpace->isNumericalTransferFn(&transferParams)) return JNI_FALSE; - - jfloat* params = env->GetFloatArrayElements(paramsArray, NULL); - params[0] = transferParams.a; - params[1] = transferParams.b; - params[2] = transferParams.c; - params[3] = transferParams.d; - params[4] = transferParams.e; - params[5] = transferParams.f; - params[6] = transferParams.g; - env->ReleaseFloatArrayElements(paramsArray, params, 0); + if (colorSpace == nullptr) return nullptr; - return JNI_TRUE; + return GraphicsJNI::getColorSpace(env, colorSpace, bitmapHolder->info().colorType()); } static void Bitmap_setColorSpace(JNIEnv* env, jobject, jlong bitmapHandle, jlong colorSpacePtr) { @@ -1174,13 +1143,6 @@ static jobject Bitmap_createGraphicBufferHandle(JNIEnv* env, jobject, jlong bitm return createJavaGraphicBuffer(env, buffer); } -static void Bitmap_copyColorSpace(JNIEnv* env, jobject, jlong srcBitmapPtr, jlong dstBitmapPtr) { - LocalScopedBitmap srcBitmapHandle(srcBitmapPtr); - LocalScopedBitmap dstBitmapHandle(dstBitmapPtr); - - dstBitmapHandle->bitmap().setColorSpace(srcBitmapHandle->bitmap().info().refColorSpace()); -} - static jboolean Bitmap_isImmutable(jlong bitmapHandle) { LocalScopedBitmap bitmapHolder(bitmapHandle); if (!bitmapHolder.valid()) return JNI_FALSE; @@ -1215,7 +1177,6 @@ static const JNINativeMethod gBitmapMethods[] = { { "nativeErase", "(JJJ)V", (void*)Bitmap_eraseLong }, { "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes }, { "nativeConfig", "(J)I", (void*)Bitmap_config }, - { "nativeIsConfigF16", "(J)Z", (void*)Bitmap_isConfigF16 }, { "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha }, { "nativeIsPremultiplied", "(J)Z", (void*)Bitmap_isPremultiplied}, { "nativeSetHasAlpha", "(JZZ)V", (void*)Bitmap_setHasAlpha}, @@ -1248,12 +1209,10 @@ static const JNINativeMethod gBitmapMethods[] = { (void*) Bitmap_wrapHardwareBufferBitmap }, { "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;", (void*) Bitmap_createGraphicBufferHandle }, - { "nativeGetColorSpace", "(J[F[F)Z", (void*)Bitmap_getColorSpace }, + { "nativeComputeColorSpace", "(J)Landroid/graphics/ColorSpace;", (void*)Bitmap_computeColorSpace }, { "nativeSetColorSpace", "(JJ)V", (void*)Bitmap_setColorSpace }, { "nativeIsSRGB", "(J)Z", (void*)Bitmap_isSRGB }, { "nativeIsSRGBLinear", "(J)Z", (void*)Bitmap_isSRGBLinear}, - { "nativeCopyColorSpace", "(JJ)V", - (void*)Bitmap_copyColorSpace }, { "nativeSetImmutable", "(J)V", (void*)Bitmap_setImmutable}, // ------------ @CriticalNative ---------------- diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 70e6604fddeb..4ba4540f7dbc 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -307,7 +307,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream, env->SetObjectField(options, gOptions_outConfigFieldID, config); env->SetObjectField(options, gOptions_outColorSpaceFieldID, - GraphicsJNI::getColorSpace(env, decodeColorSpace, decodeColorType)); + GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType)); if (onlyDecodeSize) { return nullptr; diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp index d65f324d1065..9c07e2d64c6e 100644 --- a/core/jni/android/graphics/BitmapRegionDecoder.cpp +++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp @@ -215,7 +215,7 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in env->SetObjectField(options, gOptions_outConfigFieldID, config); env->SetObjectField(options, gOptions_outColorSpaceFieldID, - GraphicsJNI::getColorSpace(env, decodeColorSpace, decodeColorType)); + GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType)); } // If we may have reused a bitmap, we need to indicate that the pixels have changed. diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 6570992b4b23..2987c5ed56b5 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -187,6 +187,8 @@ static jmethodID gColorSpaceRGB_constructorMethodID; static jclass gColorSpace_Named_class; static jfieldID gColorSpace_Named_sRGBFieldID; +static jfieldID gColorSpace_Named_ExtendedSRGBFieldID; +static jfieldID gColorSpace_Named_LinearSRGBFieldID; static jfieldID gColorSpace_Named_LinearExtendedSRGBFieldID; static jclass gTransferParameters_class; @@ -412,67 +414,78 @@ jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region) /////////////////////////////////////////////////////////////////////////////// -jobject GraphicsJNI::getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColorSpace, +jobject GraphicsJNI::getColorSpace(JNIEnv* env, SkColorSpace* decodeColorSpace, SkColorType decodeColorType) { - jobject colorSpace = nullptr; + if (!decodeColorSpace || decodeColorType == kAlpha_8_SkColorType) { + return nullptr; + } - // No need to match, we know what the output color space will be + // Special checks for the common sRGB cases and their extended variants. + jobject namedCS = nullptr; + sk_sp<SkColorSpace> srgbLinear = SkColorSpace::MakeSRGBLinear(); if (decodeColorType == kRGBA_F16_SkColorType) { - jobject linearExtendedSRGB = env->GetStaticObjectField( - gColorSpace_Named_class, gColorSpace_Named_LinearExtendedSRGBFieldID); - colorSpace = env->CallStaticObjectMethod(gColorSpace_class, - gColorSpace_getMethodID, linearExtendedSRGB); - } else { - // Same here, no need to match + // An F16 Bitmap will always report that it is EXTENDED if + // it matches a ColorSpace that has an EXTENDED variant. if (decodeColorSpace->isSRGB()) { - jobject sRGB = env->GetStaticObjectField( - gColorSpace_Named_class, gColorSpace_Named_sRGBFieldID); - colorSpace = env->CallStaticObjectMethod(gColorSpace_class, - gColorSpace_getMethodID, sRGB); - } else if (decodeColorSpace.get() != nullptr) { - // Try to match against known RGB color spaces using the CIE XYZ D50 - // conversion matrix and numerical transfer function parameters - skcms_Matrix3x3 xyzMatrix; - LOG_ALWAYS_FATAL_IF(!decodeColorSpace->toXYZD50(&xyzMatrix)); - - skcms_TransferFunction transferParams; - // We can only handle numerical transfer functions at the moment - LOG_ALWAYS_FATAL_IF(!decodeColorSpace->isNumericalTransferFn(&transferParams)); - - jobject params = env->NewObject(gTransferParameters_class, - gTransferParameters_constructorMethodID, - transferParams.a, transferParams.b, transferParams.c, - transferParams.d, transferParams.e, transferParams.f, - transferParams.g); - - jfloatArray xyzArray = env->NewFloatArray(9); - jfloat xyz[9] = { - xyzMatrix.vals[0][0], - xyzMatrix.vals[1][0], - xyzMatrix.vals[2][0], - xyzMatrix.vals[0][1], - xyzMatrix.vals[1][1], - xyzMatrix.vals[2][1], - xyzMatrix.vals[0][2], - xyzMatrix.vals[1][2], - xyzMatrix.vals[2][2] - }; - env->SetFloatArrayRegion(xyzArray, 0, 9, xyz); - - colorSpace = env->CallStaticObjectMethod(gColorSpace_class, - gColorSpace_matchMethodID, xyzArray, params); - - if (colorSpace == nullptr) { - // We couldn't find an exact match, let's create a new color space - // instance with the 3x3 conversion matrix and transfer function - colorSpace = env->NewObject(gColorSpaceRGB_class, - gColorSpaceRGB_constructorMethodID, - env->NewStringUTF("Unknown"), xyzArray, params); - } - - env->DeleteLocalRef(xyzArray); + namedCS = env->GetStaticObjectField(gColorSpace_Named_class, + gColorSpace_Named_ExtendedSRGBFieldID); + } else if (decodeColorSpace == srgbLinear.get()) { + namedCS = env->GetStaticObjectField(gColorSpace_Named_class, + gColorSpace_Named_LinearExtendedSRGBFieldID); } + } else if (decodeColorSpace->isSRGB()) { + namedCS = env->GetStaticObjectField(gColorSpace_Named_class, + gColorSpace_Named_sRGBFieldID); + } else if (decodeColorSpace == srgbLinear.get()) { + namedCS = env->GetStaticObjectField(gColorSpace_Named_class, + gColorSpace_Named_LinearSRGBFieldID); + } + + if (namedCS) { + return env->CallStaticObjectMethod(gColorSpace_class, gColorSpace_getMethodID, namedCS); } + + // Try to match against known RGB color spaces using the CIE XYZ D50 + // conversion matrix and numerical transfer function parameters + skcms_Matrix3x3 xyzMatrix; + LOG_ALWAYS_FATAL_IF(!decodeColorSpace->toXYZD50(&xyzMatrix)); + + skcms_TransferFunction transferParams; + // We can only handle numerical transfer functions at the moment + LOG_ALWAYS_FATAL_IF(!decodeColorSpace->isNumericalTransferFn(&transferParams)); + + jobject params = env->NewObject(gTransferParameters_class, + gTransferParameters_constructorMethodID, + transferParams.a, transferParams.b, transferParams.c, + transferParams.d, transferParams.e, transferParams.f, + transferParams.g); + + jfloatArray xyzArray = env->NewFloatArray(9); + jfloat xyz[9] = { + xyzMatrix.vals[0][0], + xyzMatrix.vals[1][0], + xyzMatrix.vals[2][0], + xyzMatrix.vals[0][1], + xyzMatrix.vals[1][1], + xyzMatrix.vals[2][1], + xyzMatrix.vals[0][2], + xyzMatrix.vals[1][2], + xyzMatrix.vals[2][2] + }; + env->SetFloatArrayRegion(xyzArray, 0, 9, xyz); + + jobject colorSpace = env->CallStaticObjectMethod(gColorSpace_class, + gColorSpace_matchMethodID, xyzArray, params); + + if (colorSpace == nullptr) { + // We couldn't find an exact match, let's create a new color space + // instance with the 3x3 conversion matrix and transfer function + colorSpace = env->NewObject(gColorSpaceRGB_class, + gColorSpaceRGB_constructorMethodID, + env->NewStringUTF("Unknown"), xyzArray, params); + } + + env->DeleteLocalRef(xyzArray); return colorSpace; } @@ -658,6 +671,10 @@ int register_android_graphics_Graphics(JNIEnv* env) FindClassOrDie(env, "android/graphics/ColorSpace$Named")); gColorSpace_Named_sRGBFieldID = GetStaticFieldIDOrDie(env, gColorSpace_Named_class, "SRGB", "Landroid/graphics/ColorSpace$Named;"); + gColorSpace_Named_ExtendedSRGBFieldID = GetStaticFieldIDOrDie(env, + gColorSpace_Named_class, "EXTENDED_SRGB", "Landroid/graphics/ColorSpace$Named;"); + gColorSpace_Named_LinearSRGBFieldID = GetStaticFieldIDOrDie(env, + gColorSpace_Named_class, "LINEAR_SRGB", "Landroid/graphics/ColorSpace$Named;"); gColorSpace_Named_LinearExtendedSRGBFieldID = GetStaticFieldIDOrDie(env, gColorSpace_Named_class, "LINEAR_EXTENDED_SRGB", "Landroid/graphics/ColorSpace$Named;"); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index dc0d022d94c0..f80651c30d64 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -109,7 +109,13 @@ public: */ static sk_sp<SkColorSpace> getNativeColorSpace(jlong colorSpaceHandle); - static jobject getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColorSpace, + /** + * Return the android.graphics.ColorSpace Java object that corresponds to decodeColorSpace + * and decodeColorType. + * + * This may create a new object if none of the Named ColorSpaces match. + */ + static jobject getColorSpace(JNIEnv* env, SkColorSpace* decodeColorSpace, SkColorType decodeColorType); /** diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp index 2d83ac320733..9efcace06be3 100644 --- a/core/jni/android/graphics/ImageDecoder.cpp +++ b/core/jni/android/graphics/ImageDecoder.cpp @@ -506,9 +506,9 @@ static jstring ImageDecoder_nGetMimeType(JNIEnv* env, jobject /*clazz*/, jlong n static jobject ImageDecoder_nGetColorSpace(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { auto* codec = reinterpret_cast<ImageDecoder*>(nativePtr)->mCodec.get(); - auto colorType = codec->computeOutputColorType(codec->getInfo().colorType()); + auto colorType = codec->computeOutputColorType(kN32_SkColorType); sk_sp<SkColorSpace> colorSpace = codec->computeOutputColorSpace(colorType); - return GraphicsJNI::getColorSpace(env, colorSpace, colorType); + return GraphicsJNI::getColorSpace(env, colorSpace.get(), colorType); } static const JNINativeMethod gImageDecoderMethods[] = { diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 8c7363021d3b..2aa5cb41d5ab 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -33,7 +33,6 @@ #include <private/EGL/cache.h> -#include <utils/Looper.h> #include <utils/RefBase.h> #include <utils/StrongPointer.h> #include <utils/Timers.h> @@ -144,52 +143,22 @@ private: uint32_t mRequestId; }; -class RenderingException : public MessageHandler { +class FrameCompleteWrapper : public LightRefBase<FrameCompleteWrapper> { public: - RenderingException(JavaVM* vm, const std::string& message) - : mVm(vm) - , mMessage(message) { - } - - virtual void handleMessage(const Message&) { - throwException(mVm, mMessage); - } - - static void throwException(JavaVM* vm, const std::string& message) { - JNIEnv* env = getenv(vm); - jniThrowException(env, "java/lang/IllegalStateException", message.c_str()); - } - -private: - JavaVM* mVm; - std::string mMessage; -}; - -class FrameCompleteWrapper : public MessageHandler { -public: - FrameCompleteWrapper(JNIEnv* env, jobject jobject) { - mLooper = Looper::getForThread(); - LOG_ALWAYS_FATAL_IF(!mLooper.get(), "Must create runnable on a Looper thread!"); + explicit FrameCompleteWrapper(JNIEnv* env, jobject jobject) { env->GetJavaVM(&mVm); mObject = env->NewGlobalRef(jobject); LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref"); } - virtual ~FrameCompleteWrapper() { + ~FrameCompleteWrapper() { releaseObject(); } - void postFrameComplete(int64_t frameNr) { - if (mObject) { - mFrameNr = frameNr; - mLooper->sendMessage(this, 0); - } - } - - virtual void handleMessage(const Message&) { + void onFrameComplete(int64_t frameNr) { if (mObject) { - ATRACE_FORMAT("frameComplete %" PRId64, mFrameNr); - getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, mFrameNr); + ATRACE_FORMAT("frameComplete %" PRId64, frameNr); + getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, frameNr); releaseObject(); } } @@ -197,8 +166,6 @@ public: private: JavaVM* mVm; jobject mObject; - sp<Looper> mLooper; - int64_t mFrameNr = -1; void releaseObject() { if (mObject) { @@ -211,16 +178,14 @@ private: class RootRenderNode : public RenderNode, ErrorHandler { public: explicit RootRenderNode(JNIEnv* env) : RenderNode() { - mLooper = Looper::getForThread(); - LOG_ALWAYS_FATAL_IF(!mLooper.get(), - "Must create RootRenderNode on a thread with a looper!"); env->GetJavaVM(&mVm); } virtual ~RootRenderNode() {} virtual void onError(const std::string& message) override { - mLooper->sendMessage(new RenderingException(mVm, message), 0); + JNIEnv* env = getenv(mVm); + jniThrowException(env, "java/lang/IllegalStateException", message.c_str()); } virtual void prepareTree(TreeInfo& info) override { @@ -249,14 +214,6 @@ public: info.errorHandler = nullptr; } - void sendMessage(const sp<MessageHandler>& handler) { - mLooper->sendMessage(handler, 0); - } - - void sendMessageDelayed(const sp<MessageHandler>& handler, nsecs_t delayInMs) { - mLooper->sendMessageDelayed(ms2ns(delayInMs), handler, 0); - } - void attachAnimatingNode(RenderNode* animatingNode) { mPendingAnimatingRenderNodes.push_back(animatingNode); } @@ -404,7 +361,6 @@ public: } private: - sp<Looper> mLooper; JavaVM* mVm; std::vector< sp<RenderNode> > mPendingAnimatingRenderNodes; std::set< sp<PropertyValuesAnimatorSet> > mPendingVectorDrawableAnimators; @@ -435,7 +391,9 @@ private: // the onFinished callback will then be ignored. sp<FinishAndInvokeListener> message = new FinishAndInvokeListener(anim); - sendMessageDelayed(message, remainingTimeInMs); + auto looper = Looper::getForThread(); + LOG_ALWAYS_FATAL_IF(looper == nullptr, "Not on a looper thread?"); + looper->sendMessageDelayed(ms2ns(remainingTimeInMs), message, 0); anim->clearOneShotListener(); } } @@ -463,7 +421,6 @@ public: virtual void runRemainingAnimations(TreeInfo& info) { AnimationContext::runRemainingAnimations(info); mRootNode->runVectorDrawableAnimators(this, info); - postOnFinishedEvents(); } virtual void pauseAnimators() override { @@ -471,27 +428,16 @@ public: } virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) { - OnFinishedEvent event(animator, listener); - mOnFinishedEvents.push_back(event); + listener->onAnimationFinished(animator); } virtual void destroy() { AnimationContext::destroy(); mRootNode->detachAnimators(); - postOnFinishedEvents(); } private: sp<RootRenderNode> mRootNode; - std::vector<OnFinishedEvent> mOnFinishedEvents; - - void postOnFinishedEvents() { - if (mOnFinishedEvents.size()) { - sp<InvokeAnimationListeners> message - = new InvokeAnimationListeners(mOnFinishedEvents); - mRootNode->sendMessage(message); - } - } }; class ContextFactoryImpl : public IContextFactory { @@ -958,7 +904,7 @@ static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env, } else { sp<FrameCompleteWrapper> wrapper = new FrameCompleteWrapper{env, callback}; proxy->setFrameCompleteCallback([wrapper](int64_t frameNr) { - wrapper->postFrameComplete(frameNr); + wrapper->onFrameComplete(frameNr); }); } } diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto index a4167c187194..516fa7b9336b 100644 --- a/core/proto/android/os/batterystats.proto +++ b/core/proto/android/os/batterystats.proto @@ -58,6 +58,10 @@ message ControllerActivityProto { optional int64 duration_ms = 2; } repeated TxLevel tx = 4; + + // Total rail charge consumed by the monitored rails by the controller. The value may + // always be 0 if the device doesn't support monitored rail calculations. + optional double monitored_rail_charge_mah = 5; } message SystemProto { diff --git a/core/proto/android/server/connectivity/Android.bp b/core/proto/android/server/connectivity/Android.bp new file mode 100644 index 000000000000..c0ac2cb8f800 --- /dev/null +++ b/core/proto/android/server/connectivity/Android.bp @@ -0,0 +1,25 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +java_library_static { + name: "datastallprotosnano", + proto: { + type: "nano", + }, + srcs: [ + "data_stall_event.proto", + ], + sdk_version: "system_current", + no_framework_libs: true, +}
\ No newline at end of file diff --git a/core/res/res/drawable/ic_qs_night_display_on.xml b/core/res/res/drawable/ic_qs_night_display_on.xml index 35907cc83fe0..a4755ee256e2 100644 --- a/core/res/res/drawable/ic_qs_night_display_on.xml +++ b/core/res/res/drawable/ic_qs_night_display_on.xml @@ -1,5 +1,5 @@ <!-- - Copyright (C) 2017 The Android Open Source Project + Copyright (C) 2019 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,14 +14,13 @@ limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="64dp" - android:height="64dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0"> - <group - android:translateX="-1.0"> - <path - android:pathData="M13,12c0,-3.57 2.2,-6.62 5.31,-7.87 0.89,-0.36 0.75,-1.69 -0.19,-1.9 -1.1,-0.24 -2.27,-0.3 -3.48,-0.14 -4.51,0.6 -8.12,4.31 -8.59,8.83C5.43,16.93 10.12,22 16,22c0.73,0 1.43,-0.08 2.12,-0.23 0.95,-0.21 1.1,-1.53 0.2,-1.9A8.471,8.471 0,0 1,13 12z" - android:fillColor="#FFF"/> - </group> -</vector>
\ No newline at end of file + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + <path + android:fillColor="#FFFFFF" + android:pathData="M6.28,4.81c0,0.16,0.01,0.32,0.02,0.48c0.38,6.58,5.83,12.03,12.41,12.41c0.16,0.01,0.32,0.02,0.47,0.02 c-1.58,1.2-3.53,1.88-5.56,1.88c-0.46,0-0.93-0.03-1.4-0.1c-3.96-0.58-7.13-3.75-7.71-7.71C4.13,9.24,4.8,6.75,6.28,4.81 M8.27,0.6 c-0.08,0-0.17,0.02-0.25,0.07c-3.8,2.2-6.2,6.56-5.49,11.4c0.7,4.82,4.59,8.7,9.4,9.4c0.57,0.08,1.13,0.12,1.69,0.12 c4.15,0,7.78-2.26,9.72-5.62c0.2-0.35-0.07-0.76-0.44-0.76c-0.05,0-0.1,0.01-0.15,0.02c-1.03,0.31-2.12,0.48-3.25,0.48 c-0.22,0-0.44-0.01-0.67-0.02C13.23,15.38,8.62,10.77,8.29,5.17C8.21,3.81,8.38,2.49,8.75,1.26C8.86,0.91,8.59,0.6,8.27,0.6 L8.27,0.6z" /> +</vector> + diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml index 3683bfd02e87..10798ad51792 100644 --- a/core/res/res/layout/chooser_grid.xml +++ b/core/res/res/layout/chooser_grid.xml @@ -20,12 +20,10 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:maxWidth="@dimen/resolver_max_width" android:maxCollapsedHeight="288dp" android:maxCollapsedHeightSmall="56dp" android:id="@id/contentPanel"> - <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" @@ -153,8 +151,9 @@ android:background="?attr/colorBackgroundFloating"> <LinearLayout - android:layout_width="match_parent" + android:layout_width="@dimen/chooser_preview_width" android:layout_height="wrap_content" + android:layout_gravity="center" android:orientation="horizontal" android:paddingLeft="@dimen/chooser_edge_margin_normal" android:paddingRight="@dimen/chooser_edge_margin_normal" @@ -182,8 +181,9 @@ <!-- Required sub-layout so we can get the nice rounded corners--> <!-- around this section --> <LinearLayout - android:layout_width="match_parent" + android:layout_width="@dimen/chooser_preview_width" android:layout_height="wrap_content" + android:layout_gravity="center" android:orientation="horizontal" android:layout_marginLeft="@dimen/chooser_edge_margin_thin" android:layout_marginRight="@dimen/chooser_edge_margin_thin" @@ -224,8 +224,9 @@ android:background="?attr/colorBackgroundFloating"> <LinearLayout - android:layout_width="match_parent" + android:layout_width="@dimen/chooser_preview_width" android:layout_height="wrap_content" + android:layout_gravity="center" android:orientation="horizontal" android:paddingLeft="@dimen/chooser_edge_margin_normal" android:paddingRight="@dimen/chooser_edge_margin_normal" diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml index 351bd818b09a..9e87a47219f3 100644 --- a/core/res/res/values-land/dimens.xml +++ b/core/res/res/values-land/dimens.xml @@ -76,4 +76,6 @@ <!-- Floating toolbar dimensions --> <dimen name="floating_toolbar_preferred_width">544dp</dimen> + <dimen name="chooser_preview_width">480dp</dimen> + </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index d14164f69850..dae2692c7722 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3252,7 +3252,7 @@ skinny aspect ratio that is not expected to be widely used. --> <item name="config_pictureInPictureMinAspectRatio" format="float" type="dimen">0.41841004184</item> - <!-- The minimum aspect ratio (width/height) that is supported for picture-in-picture. Any + <!-- The maximum aspect ratio (width/height) that is supported for picture-in-picture. Any ratio larger than this is considered to wide and short to be usable. Currently 2.39:1. --> <item name="config_pictureInPictureMaxAspectRatio" format="float" type="dimen">2.39</item> @@ -3273,6 +3273,11 @@ --> <integer name="config_dockedStackDividerSnapMode">0</integer> + <!-- The maximum aspect ratio (longerSide/shorterSide) that is treated as close-to-square. If + config_forceDefaultOrientation is set to true, the rotation on a close-to-square display + will be fixed. --> + <item name="config_closeToSquareDisplayMaxAspectRatio" format="float" type="dimen">1.333</item> + <!-- List of comma separated package names for which we the system will not show crash, ANR, etc. dialogs. --> <string translatable="false" name="config_appsNotReportingCrashes"></string> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 9f86f8416fb9..39cbd269c75a 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -721,4 +721,5 @@ <dimen name="chooser_edge_margin_thin">16dp</dimen> <dimen name="chooser_edge_margin_normal">24dp</dimen> <dimen name="chooser_preview_image_font_size">20sp</dimen> + <dimen name="chooser_preview_width">-1px</dimen> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a1bafbf8de69..08fc36db928e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -403,6 +403,7 @@ <java-symbol type="integer" name="config_defaultPictureInPictureGravity" /> <java-symbol type="dimen" name="config_pictureInPictureMinAspectRatio" /> <java-symbol type="dimen" name="config_pictureInPictureMaxAspectRatio" /> + <java-symbol type="dimen" name="config_closeToSquareDisplayMaxAspectRatio" /> <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" /> <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_factor" /> <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" /> @@ -2755,6 +2756,7 @@ <java-symbol type="dimen" name="chooser_edge_margin_thin" /> <java-symbol type="dimen" name="chooser_edge_margin_normal" /> <java-symbol type="dimen" name="chooser_preview_image_font_size"/> + <java-symbol type="dimen" name="chooser_preview_width" /> <java-symbol type="layout" name="chooser_grid" /> <java-symbol type="layout" name="resolve_grid_item" /> <java-symbol type="id" name="day_picker_view_pager" /> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 4d2f005998c3..c57b609023ca 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -621,6 +621,7 @@ public class SettingsBackupTest { Settings.Secure.DISABLED_PRINT_SERVICES, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS, Settings.Secure.DISPLAY_DENSITY_FORCED, + Settings.Secure.DOCKED_CLOCK_FACE, Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION, Settings.Secure.ENABLED_INPUT_METHODS, // Intentionally removed in P diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java index f325d8943ea3..a97c3fa362eb 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java @@ -15,6 +15,7 @@ */ package android.view.contentcapture; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED; @@ -174,7 +175,8 @@ public class ContentCaptureEventTest { assertThat(event.getIds()).isNull(); assertThat(event.getText()).isNull(); assertThat(event.getViewNode()).isNull(); - final ContentCaptureContext clientContext = event.getClientContext(); + final ContentCaptureContext clientContext = event.getContentCaptureContext(); + assertThat(clientContext).isNotNull(); assertThat(clientContext.getAction()).isEqualTo("WHATEVER"); } @@ -205,9 +207,44 @@ public class ContentCaptureEventTest { assertThat(event.getIds()).isNull(); assertThat(event.getText()).isNull(); assertThat(event.getViewNode()).isNull(); - assertThat(event.getClientContext()).isNull(); + assertThat(event.getContentCaptureContext()).isNull(); } + + @Test + public void testContextUpdated_directly() { + final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_CONTEXT_UPDATED) + .setClientContext(mClientContext); + assertThat(event).isNotNull(); + assertContextUpdatedEvent(event); + } + + @Test + public void testContextUpdated_throughParcel() { + final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_CONTEXT_UPDATED) + .setClientContext(mClientContext); + assertThat(event).isNotNull(); + final ContentCaptureEvent clone = cloneThroughParcel(event); + assertContextUpdatedEvent(clone); + } + + private void assertContextUpdatedEvent(ContentCaptureEvent event) { + assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_UPDATED); + assertThat(event.getEventTime()).isAtLeast(MY_EPOCH); + assertThat(event.getSessionId()).isEqualTo("42"); + assertThat(event.getParentSessionId()).isNull(); + assertThat(event.getId()).isNull(); + assertThat(event.getIds()).isNull(); + assertThat(event.getText()).isNull(); + assertThat(event.getViewNode()).isNull(); + final ContentCaptureContext clientContext = event.getContentCaptureContext(); + assertThat(clientContext).isNotNull(); + assertThat(clientContext.getAction()).isEqualTo("WHATEVER"); + } + + // TODO(b/123036895): add test for all events type (right now we're just testing the 3 types + // that use logic to write to parcel + private ContentCaptureEvent cloneThroughParcel(ContentCaptureEvent event) { Parcel parcel = Parcel.obtain(); diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java index 34fdebfdf348..b6717e16256f 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java @@ -160,5 +160,10 @@ public class ContentCaptureSessionTest { public void internalNotifyViewHierarchyEvent(boolean started) { throw new UnsupportedOperationException("should not have been called"); } + + @Override + public void updateContentCaptureContext(ContentCaptureContext context) { + throw new UnsupportedOperationException("should not have been called"); + } } } diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 18f0cae4733c..bdb63643f615 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -891,8 +891,10 @@ public final class Bitmap implements Parcelable { } } + ColorSpace cs = source.getColorSpace(); + if (m == null || m.isIdentity()) { - bitmap = createBitmap(neww, newh, newConfig, source.hasAlpha()); + bitmap = createBitmap(null, neww, newh, newConfig, source.hasAlpha(), cs); paint = null; // not needed } else { final boolean transformed = !m.rectStaysRect(); @@ -906,9 +908,14 @@ public final class Bitmap implements Parcelable { if (transformed) { if (transformedConfig != Config.ARGB_8888 && transformedConfig != Config.RGBA_F16) { transformedConfig = Config.ARGB_8888; + if (cs == null) { + cs = ColorSpace.get(ColorSpace.Named.SRGB); + } } } - bitmap = createBitmap(neww, newh, transformedConfig, transformed || source.hasAlpha()); + + bitmap = createBitmap(null, neww, newh, transformedConfig, + transformed || source.hasAlpha(), cs); paint = new Paint(); paint.setFilterBitmap(filter); @@ -917,8 +924,6 @@ public final class Bitmap implements Parcelable { } } - nativeCopyColorSpace(source.mNativePtr, bitmap.mNativePtr); - // The new bitmap was created from a known bitmap source so assume that // they use the same density bitmap.mDensity = source.mDensity; @@ -1000,10 +1005,10 @@ public final class Bitmap implements Parcelable { * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to * mark the bitmap as opaque. Doing so will clear the bitmap in black * instead of transparent. - * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16}, - * {@link ColorSpace.Named#EXTENDED_SRGB scRGB} is assumed, and if the - * config is not {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB} - * is assumed. + * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16} + * and {@link ColorSpace.Named#SRGB sRGB} or + * {@link ColorSpace.Named#LINEAR_SRGB Linear sRGB} is provided then the + * corresponding extended range variant is assumed. * * @throws IllegalArgumentException if the width or height are <= 0, if * Config is Config.HARDWARE (because hardware bitmaps are always @@ -1055,10 +1060,10 @@ public final class Bitmap implements Parcelable { * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to * mark the bitmap as opaque. Doing so will clear the bitmap in black * instead of transparent. - * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16}, - * {@link ColorSpace.Named#EXTENDED_SRGB scRGB} is assumed, and if the - * config is not {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB} - * is assumed. + * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16} + * and {@link ColorSpace.Named#SRGB sRGB} or + * {@link ColorSpace.Named#LINEAR_SRGB Linear sRGB} is provided then the + * corresponding extended range variant is assumed. * * @throws IllegalArgumentException if the width or height are <= 0, if * Config is Config.HARDWARE (because hardware bitmaps are always @@ -1075,22 +1080,12 @@ public final class Bitmap implements Parcelable { if (config == Config.HARDWARE) { throw new IllegalArgumentException("can't create mutable bitmap with Config.HARDWARE"); } - if (colorSpace == null) { + if (colorSpace == null && config != Config.ALPHA_8) { throw new IllegalArgumentException("can't create bitmap without a color space"); } - if (config != Config.ARGB_8888) { - if (config == Config.RGBA_F16) { - // FIXME: This should be LINEAR_EXTENDED_SRGB, but that would fail a CTS test. See - // b/120960866. SRGB matches the old (incorrect) behavior. - //colorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB); - colorSpace = ColorSpace.get(ColorSpace.Named.SRGB); - } else { - colorSpace = ColorSpace.get(ColorSpace.Named.SRGB); - } - } Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true, - colorSpace.getNativeInstance()); + colorSpace == null ? 0 : colorSpace.getNativeInstance()); if (display != null) { bm.mDensity = display.densityDpi; @@ -1701,41 +1696,9 @@ public final class Bitmap implements Parcelable { @Nullable public final ColorSpace getColorSpace() { checkRecycled("getColorSpace called on a recycled bitmap"); - // Cache the color space retrieval since it can be fairly expensive if (mColorSpace == null) { - if (nativeIsConfigF16(mNativePtr)) { - // an F16 bitmaps is intended to always be linear extended, but due to - // inconsistencies in Bitmap.create() functions it is possible to have - // rendered into a bitmap in non-linear sRGB. - if (nativeIsSRGB(mNativePtr)) { - mColorSpace = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB); - } else { - mColorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB); - } - } else if (nativeIsSRGB(mNativePtr)) { - mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB); - } else if (nativeIsSRGBLinear(mNativePtr)) { - mColorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_SRGB); - } else { - float[] xyz = new float[9]; - float[] params = new float[7]; - - boolean hasColorSpace = nativeGetColorSpace(mNativePtr, xyz, params); - if (hasColorSpace) { - ColorSpace.Rgb.TransferParameters parameters = - new ColorSpace.Rgb.TransferParameters( - params[0], params[1], params[2], - params[3], params[4], params[5], params[6]); - ColorSpace cs = ColorSpace.match(xyz, parameters); - if (cs != null) { - mColorSpace = cs; - } else { - mColorSpace = new ColorSpace.Rgb("Unknown", xyz, parameters); - } - } - } + mColorSpace = nativeComputeColorSpace(mNativePtr); } - return mColorSpace; } @@ -1749,6 +1712,9 @@ public final class Bitmap implements Parcelable { * components min/max values reduce the numerical range compared to the * previously assigned color space. * + * @throws IllegalArgumentException If the {@code Config} (returned by {@link #getConfig()}) + * is {@link Config#ALPHA_8}. + * * @param colorSpace to assign to the bitmap */ public void setColorSpace(@NonNull ColorSpace colorSpace) { @@ -1756,29 +1722,47 @@ public final class Bitmap implements Parcelable { if (colorSpace == null) { throw new IllegalArgumentException("The colorSpace cannot be set to null"); } - if (getColorSpace() != null) { - if (mColorSpace.getComponentCount() != colorSpace.getComponentCount()) { + + if (getConfig() == Config.ALPHA_8) { + throw new IllegalArgumentException("Cannot set a ColorSpace on ALPHA_8"); + } + + // Keep track of the old ColorSpace for comparison, and so we can reset it in case of an + // Exception. + final ColorSpace oldColorSpace = getColorSpace(); + nativeSetColorSpace(mNativePtr, colorSpace.getNativeInstance()); + + // This will update mColorSpace. It may not be the same as |colorSpace|, e.g. if we + // corrected it because the Bitmap is F16. + mColorSpace = null; + final ColorSpace newColorSpace = getColorSpace(); + + try { + if (oldColorSpace.getComponentCount() != newColorSpace.getComponentCount()) { throw new IllegalArgumentException("The new ColorSpace must have the same " + "component count as the current ColorSpace"); - } - for (int i = 0; i < mColorSpace.getComponentCount(); i++) { - if (mColorSpace.getMinValue(i) < colorSpace.getMinValue(i)) { - throw new IllegalArgumentException("The new ColorSpace cannot increase the " - + "minimum value for any of the components compared to the current " - + "ColorSpace. To perform this type of conversion create a new Bitmap " - + "in the desired ColorSpace and draw this Bitmap into it."); - } - if (mColorSpace.getMaxValue(i) > colorSpace.getMaxValue(i)) { - throw new IllegalArgumentException("The new ColorSpace cannot decrease the " - + "maximum value for any of the components compared to the current " - + "ColorSpace/ To perform this type of conversion create a new Bitmap" - + "in the desired ColorSpace and draw this Bitmap into it."); + } else { + for (int i = 0; i < oldColorSpace.getComponentCount(); i++) { + if (oldColorSpace.getMinValue(i) < newColorSpace.getMinValue(i)) { + throw new IllegalArgumentException("The new ColorSpace cannot increase the " + + "minimum value for any of the components compared to the current " + + "ColorSpace. To perform this type of conversion create a new " + + "Bitmap in the desired ColorSpace and draw this Bitmap into it."); + } + if (oldColorSpace.getMaxValue(i) > newColorSpace.getMaxValue(i)) { + throw new IllegalArgumentException("The new ColorSpace cannot decrease the " + + "maximum value for any of the components compared to the current " + + "ColorSpace/ To perform this type of conversion create a new " + + "Bitmap in the desired ColorSpace and draw this Bitmap into it."); + } } } + } catch (IllegalArgumentException e) { + // Undo the change to the ColorSpace. + mColorSpace = oldColorSpace; + nativeSetColorSpace(mNativePtr, mColorSpace.getNativeInstance()); + throw e; } - - nativeSetColorSpace(mNativePtr, colorSpace.getNativeInstance()); - mColorSpace = colorSpace; } /** @@ -2197,7 +2181,6 @@ public final class Bitmap implements Parcelable { private static native void nativeErase(long nativeBitmap, long colorSpacePtr, long color); private static native int nativeRowBytes(long nativeBitmap); private static native int nativeConfig(long nativeBitmap); - private static native boolean nativeIsConfigF16(long nativeBitmap); private static native int nativeGetPixel(long nativeBitmap, int x, int y); private static native long nativeGetColor(long nativeBitmap, int x, int y); @@ -2241,11 +2224,10 @@ public final class Bitmap implements Parcelable { private static native Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer, long nativeColorSpace); private static native GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap); - private static native boolean nativeGetColorSpace(long nativePtr, float[] xyz, float[] params); + private static native ColorSpace nativeComputeColorSpace(long nativePtr); private static native void nativeSetColorSpace(long nativePtr, long nativeColorSpace); private static native boolean nativeIsSRGB(long nativePtr); private static native boolean nativeIsSRGBLinear(long nativePtr); - private static native void nativeCopyColorSpace(long srcBitmap, long dstBitmap); private static native void nativeSetImmutable(long nativePtr); diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index 7aff0414106a..49c3a3ba68b8 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -151,12 +151,9 @@ public class BitmapFactory { * the decoder will pick either the color space embedded in the image * or the color space best suited for the requested image configuration * (for instance {@link ColorSpace.Named#SRGB sRGB} for - * the {@link Bitmap.Config#ARGB_8888} configuration).</p> - * - * <p>{@link Bitmap.Config#RGBA_F16} always uses the - * {@link ColorSpace.Named#LINEAR_EXTENDED_SRGB scRGB} color space). - * Bitmaps in other configurations without an embedded color space are - * assumed to be in the {@link ColorSpace.Named#SRGB sRGB} color space.</p> + * {@link Bitmap.Config#ARGB_8888} configuration and + * {@link ColorSpace.Named#EXTENDED_SRGB EXTENDED_SRGB} for + * {@link Bitmap.Config#RGBA_F16}).</p> * * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are * currently supported. An <code>IllegalArgumentException</code> will diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index c9e46942a51a..0d5233880674 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -1475,7 +1475,7 @@ public abstract class ColorSpace { x -> absRcpResponse(x, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4), x -> absResponse(x, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4), -0.799f, 2.399f, - null, // FIXME: Use SRGB_TRANSFER_PARAMETERS + SRGB_TRANSFER_PARAMETERS, Named.EXTENDED_SRGB.ordinal() ); sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb( diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index 7ec76d79dfb1..99d8c1b42a4a 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -20,7 +20,6 @@ import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.TestApi; import android.app.Activity; import android.app.ActivityManager; import android.os.IBinder; @@ -28,13 +27,16 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; +import android.util.TimeUtils; import android.view.FrameMetricsObserver; import android.view.IGraphicsStats; import android.view.IGraphicsStatsCallback; import android.view.NativeVectorDrawableAnimator; +import android.view.PixelCopy; import android.view.Surface; import android.view.SurfaceHolder; import android.view.TextureLayer; +import android.view.animation.AnimationUtils; import com.android.internal.util.VirtualRefBasePtr; @@ -42,6 +44,7 @@ import java.io.File; import java.io.FileDescriptor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; import sun.misc.Cleaner; @@ -50,13 +53,8 @@ import sun.misc.Cleaner; * from {@link RenderNode}'s to an output {@link android.view.Surface}. There can be as many * HardwareRenderer instances as desired.</p> * - * <h3>Threading</h3> - * <p>HardwareRenderer is not thread safe. An instance of a HardwareRenderer must only be - * created & used from a single thread. It does not matter what thread is used, however - * it must have a {@link android.os.Looper}. Multiple instances do not have to share the same - * thread, although they can.</p> - * * <h3>Resources & lifecycle</h3> + * * <p>All HardwareRenderer instances share a common render thread. The render thread contains * the GPU context & resources necessary to do GPU-accelerated rendering. As such, the first * HardwareRenderer created comes with the cost of also creating the associated GPU contexts, @@ -64,6 +62,7 @@ import sun.misc.Cleaner; * is to have a HardwareRenderer instance for every active {@link Surface}. For example * when an Activity shows a Dialog the system internally will use 2 hardware renderers, both * of which may be drawing at the same time.</p> + * * <p>NOTE: Due to the shared, cooperative nature of the render thread it is critical that * any {@link Surface} used must have a prompt, reliable consuming side. System-provided * consumers such as {@link android.view.SurfaceView}, @@ -73,8 +72,6 @@ import sun.misc.Cleaner; * it is the app's responsibility to ensure that they consume updates promptly and rapidly. * Failure to do so will cause the render thread to stall on that surface, blocking all * HardwareRenderer instances.</p> - * - * @hide */ public class HardwareRenderer { private static final String LOG_TAG = "HardwareRenderer"; @@ -89,18 +86,18 @@ public class HardwareRenderer { * The renderer is requesting a redraw. This can occur if there's an animation that's running * in the RenderNode tree and the hardware renderer is unable to self-animate. * - * If this is returned from syncAndDrawFrame the expectation is that syncAndDrawFrame + * <p>If this is returned from syncAndDraw the expectation is that syncAndDraw * will be called again on the next vsync signal. */ public static final int SYNC_REDRAW_REQUESTED = 1 << 0; /** * The hardware renderer no longer has a valid {@link android.view.Surface} to render to. - * This can happen if {@link Surface#destroy()} was called. The user should no longer - * attempt to call syncAndDrawFrame until a new surface has been provided by calling + * This can happen if {@link Surface#release()} was called. The user should no longer + * attempt to call syncAndDraw until a new surface has been provided by calling * setSurface. * - * Spoiler: the reward is GPU-accelerated drawing, better find that Surface! + * <p>Spoiler: the reward is GPU-accelerated drawing, better find that Surface! */ public static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 1 << 1; @@ -119,6 +116,7 @@ public class HardwareRenderer { */ public static final int SYNC_FRAME_DROPPED = 1 << 3; + /** @hide */ @IntDef(value = { SYNC_OK, SYNC_REDRAW_REQUESTED, SYNC_LOST_SURFACE_REWARD_IF_FOUND, SYNC_CONTEXT_IS_STOPPED, SYNC_FRAME_DROPPED}) @@ -153,7 +151,6 @@ public class HardwareRenderer { protected RenderNode mRootNode; private boolean mOpaque = true; private boolean mForceDark = false; - private FrameInfo mScratchInfo; private boolean mIsWideGamut = false; /** @@ -175,14 +172,14 @@ public class HardwareRenderer { * Destroys the rendering context of this HardwareRenderer. This destroys the resources * associated with this renderer and releases the currently set {@link Surface}. * - * The renderer may be restored from this state by setting a new {@link Surface}, setting + * <p>The renderer may be restored from this state by setting a new {@link Surface}, setting * new rendering content with {@link #setContentRoot(RenderNode)}, and resuming - * rendering with {@link #syncAndDrawFrame(long)}. + * rendering by issuing a new {@link FrameRenderRequest}. * - * It is suggested to call this in response to callbacks such as + * <p>It is suggested to call this in response to callbacks such as * {@link android.view.SurfaceHolder.Callback#surfaceDestroyed(SurfaceHolder)}. * - * Note that if there are any outstanding frame commit callbacks they may end up never being + * <p>Note that if there are any outstanding frame commit callbacks they may never being * invoked if the frame was deferred to a later vsync. */ public void destroy() { @@ -204,14 +201,14 @@ public class HardwareRenderer { * Sets the center of the light source. The light source point controls the directionality * and shape of shadows rendered by RenderNode Z & elevation. * - * The platform's recommendation is to set lightX to 'displayWidth / 2f - windowLeft', set + * <p>The platform's recommendation is to set lightX to 'displayWidth / 2f - windowLeft', set * lightY to 0 - windowTop, lightZ set to 600dp, and lightRadius to 800dp. * - * The light source should be setup both as part of initial configuration, and whenever + * <p>The light source should be setup both as part of initial configuration, and whenever * the window moves to ensure the light source stays anchored in display space instead * of in window space. * - * This must be set at least once along with {@link #setLightSourceAlpha(float, float)} + * <p>This must be set at least once along with {@link #setLightSourceAlpha(float, float)} * before shadows will work. * * @param lightX The X position of the light source @@ -233,10 +230,10 @@ public class HardwareRenderer { * Configures the ambient & spot shadow alphas. This is the alpha used when the shadow * has max alpha, and ramps down from the values provided to zero. * - * These values are typically provided by the current theme, see + * <p>These values are typically provided by the current theme, see * {@link android.R.attr#spotShadowAlpha} and {@link android.R.attr#ambientShadowAlpha}. * - * This must be set at least once along with + * <p>This must be set at least once along with * {@link #setLightSourceGeometry(float, float, float, float)} before shadows will work. * * @param ambientShadowAlpha The alpha for the ambient shadow. If unsure, a reasonable default @@ -254,8 +251,8 @@ public class HardwareRenderer { /** * Sets the content root to render. It is not necessary to call this whenever the content * recording changes. Any mutations to the RenderNode content, or any of the RenderNode's - * contained within the content node, will be applied whenever {@link #syncAndDrawFrame(long)} - * is called. + * contained within the content node, will be applied whenever a new {@link FrameRenderRequest} + * is issued via {@link #createRenderRequest()} and {@link FrameRenderRequest#syncAndDraw()}. * * @param content The content to set as the root RenderNode. If null the content root is removed * and the renderer will draw nothing. @@ -295,53 +292,133 @@ public class HardwareRenderer { } /** - * Syncs the RenderNode tree to the render thread and requests a frame to be drawn. - * - * @hide + * Sets the parameters that can be used to control a render request for a + * {@link HardwareRenderer}. This is not thread-safe and must not be held on to for longer + * than a single frame request. */ - @SyncAndDrawResult - public int syncAndDrawFrame(@NonNull FrameInfo frameInfo) { - return nSyncAndDrawFrame(mNativeProxy, frameInfo.frameInfo, frameInfo.frameInfo.length); + public final class FrameRenderRequest { + private FrameInfo mFrameInfo = new FrameInfo(); + private boolean mWaitForPresent; + + private FrameRenderRequest() { } + + private void reset() { + mWaitForPresent = false; + // Default to the animation time which, if choreographer is in play, will default to the + // current vsync time. Otherwise it will be 'now'. + mRenderRequest.setVsyncTime( + AnimationUtils.currentAnimationTimeMillis() * TimeUtils.NANOS_PER_MS); + } + + /** @hide */ + public void setFrameInfo(FrameInfo info) { + System.arraycopy(info.frameInfo, 0, mFrameInfo.frameInfo, 0, info.frameInfo.length); + } + + /** + * Sets the vsync time that represents the start point of this frame. Typically this + * comes from {@link android.view.Choreographer.FrameCallback}. Other compatible time + * sources include {@link System#nanoTime()}, however if the result is being displayed + * on-screen then using {@link android.view.Choreographer} is strongly recommended to + * ensure smooth animations. + * + * <p>If the clock source is not from a CLOCK_MONOTONIC source then any animations driven + * directly by RenderThread will not be synchronized properly with the current frame. + * + * @param vsyncTime The vsync timestamp for this frame. The timestamp is in nanoseconds + * and should come from a CLOCK_MONOTONIC source. + * + * @return this instance + */ + public FrameRenderRequest setVsyncTime(long vsyncTime) { + mFrameInfo.setVsync(vsyncTime, vsyncTime); + mFrameInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS); + return this; + } + + /** + * Adds a frame commit callback. This callback will be invoked when the current rendering + * content has been rendered into a frame and submitted to the swap chain. The frame may + * not currently be visible on the display when this is invoked, but it has been submitted. + * This callback is useful in combination with {@link PixelCopy} to capture the current + * rendered content of the UI reliably. + * + * @param executor The executor to run the callback on. It is strongly recommended that + * this executor post to a different thread, as the calling thread is + * highly sensitive to being blocked. + * @param frameCommitCallback The callback to invoke when the frame content has been drawn. + * Will be invoked on the given {@link Executor}. + * + * @return this instance + */ + public FrameRenderRequest setFrameCommitCallback(@NonNull Executor executor, + @NonNull Runnable frameCommitCallback) { + setFrameCompleteCallback(frameNr -> executor.execute(frameCommitCallback)); + return this; + } + + /** + * Sets whether or not {@link #syncAndDraw()} should block until the frame has been + * presented. If this is true and {@link #syncAndDraw()} does not return + * {@link #SYNC_FRAME_DROPPED} or an error then when {@link #syncAndDraw()} has returned + * the frame has been submitted to the {@link Surface}. The default and typically + * recommended value is false, as blocking for present will prevent pipelining from + * happening, reducing overall throughput. This is useful for situations such as + * {@link SurfaceHolder.Callback2#surfaceRedrawNeeded(SurfaceHolder)} where it is desired + * to block until a frame has been presented to ensure first-frame consistency with + * other Surfaces. + * + * @param shouldWait If true the next call to {@link #syncAndDraw()} will block until + * completion. + * @return this instance + */ + public FrameRenderRequest setWaitForPresent(boolean shouldWait) { + mWaitForPresent = shouldWait; + return this; + } + + /** + * Syncs the RenderNode tree to the render thread and requests a frame to be drawn. This + * {@link FrameRenderRequest} instance should no longer be used after calling this method. + * The system internally may reuse instances of {@link FrameRenderRequest} to reduce + * allocation churn. + * + * @return The result of the sync operation. See {@link SyncAndDrawResult}. + */ + @SyncAndDrawResult + public int syncAndDraw() { + int syncResult = syncAndDrawFrame(mFrameInfo); + if (mWaitForPresent && (syncResult & SYNC_FRAME_DROPPED) == 0) { + fence(); + } + return syncResult; + } } + private FrameRenderRequest mRenderRequest = new FrameRenderRequest(); + /** - * Syncs the RenderNode tree to the render thread and requests a frame to be drawn. + * Returns a {@link FrameRenderRequest} that can be used to render a new frame. This is used + * to synchronize the RenderNode content provided by {@link #setContentRoot(RenderNode)} with + * the RenderThread and then renders a single frame to the Surface set with + * {@link #setSurface(Surface)}. * - * @param vsyncTime The vsync timestamp for this frame. Typically this comes from - * {@link android.view.Choreographer.FrameCallback}. Must be set and be valid - * as the renderer uses this time internally to drive animations. - * @return The result of the sync operation. See {@link SyncAndDrawResult}. + * @return An instance of {@link FrameRenderRequest}. The instance may be reused for every + * frame, so the caller should not hold onto it for longer than a single render request. */ - @SyncAndDrawResult - public int syncAndDrawFrame(long vsyncTime) { - if (mScratchInfo == null) { - mScratchInfo = new FrameInfo(); - } - mScratchInfo.setVsync(vsyncTime, vsyncTime); - mScratchInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS); - return syncAndDrawFrame(mScratchInfo); + public FrameRenderRequest createRenderRequest() { + mRenderRequest.reset(); + return mRenderRequest; } /** * Syncs the RenderNode tree to the render thread and requests a frame to be drawn. - * frameCommitCallback callback will be invoked when the current rendering content has been - * rendered into a frame and submitted to the swap chain. * - * @param vsyncTime The vsync timestamp for this frame. Typically this comes from - * {@link android.view.Choreographer.FrameCallback}. Must be set and - * be valid as the renderer uses this time internally to drive - * animations. - * @param frameCommitCallback The callback to invoke when the frame content has been drawn. - * Will be invoked on the current {@link android.os.Looper} thread. - * @return The result of the sync operation. See {@link SyncAndDrawResult}. + * @hide */ @SyncAndDrawResult - public int syncAndDrawFrame(long vsyncTime, - @Nullable Runnable frameCommitCallback) { - if (frameCommitCallback != null) { - setFrameCompleteCallback(frameNr -> frameCommitCallback.run()); - } - return syncAndDrawFrame(vsyncTime); + public int syncAndDrawFrame(@NonNull FrameInfo frameInfo) { + return nSyncAndDrawFrame(mNativeProxy, frameInfo.frameInfo, frameInfo.frameInfo.length); } /** @@ -349,10 +426,11 @@ public class HardwareRenderer { * is useful to temporarily suspend using the active Surface in order to do any Surface * mutations necessary. * - * Any subsequent draws will override the pause, resuming normal operation. + * <p>Any subsequent draws will override the pause, resuming normal operation. * * @return true if there was an outstanding render request, false otherwise. If this is true - * the caller should ensure that {@link #syncAndDrawFrame(long)} is called at the soonest + * the caller should ensure that {@link #createRenderRequest()} + * and {@link FrameRenderRequest#syncAndDraw()} is called at the soonest * possible time to resume normal operation. * * TODO Should this be exposed? ViewRootImpl needs it because it destroys the old @@ -367,14 +445,14 @@ public class HardwareRenderer { /** * Hard stops rendering into the surface. If the renderer is stopped it will - * block any attempt to render. Calls to {@link #syncAndDrawFrame(long)} will still - * sync over the latest rendering content, however they will not render and instead + * block any attempt to render. Calls to {@link FrameRenderRequest#syncAndDraw()} will + * still sync over the latest rendering content, however they will not render and instead * {@link #SYNC_CONTEXT_IS_STOPPED} will be returned. * - * If false is passed then rendering will resume as normal. Any pending rendering requests + * <p>If false is passed then rendering will resume as normal. Any pending rendering requests * will produce a new frame at the next vsync signal. * - * This is useful in combination with lifecycle events such as {@link Activity#onStop()} + * <p>This is useful in combination with lifecycle events such as {@link Activity#onStop()} * and {@link Activity#onStart()}. * * @param stopped true to stop all rendering, false to resume @@ -384,24 +462,26 @@ public class HardwareRenderer { } /** - * Destroys all hardware rendering resources associated with the current rendering content. + * Destroys all the display lists associated with the current rendering content. * This includes releasing a reference to the current content root RenderNode. It will * therefore be necessary to call {@link #setContentRoot(RenderNode)} in order to resume - * rendering after calling this. + * rendering after calling this, along with re-recording the display lists for the + * RenderNode tree. * - * It is recommended, but not necessary, to use this in combination with lifecycle events + * <p>It is recommended, but not necessary, to use this in combination with lifecycle events * such as {@link Activity#onStop()} and {@link Activity#onStart()} or in response to * {@link android.content.ComponentCallbacks2#onTrimMemory(int)} signals such as * {@link android.content.ComponentCallbacks2#TRIM_MEMORY_UI_HIDDEN} * * See also {@link #setStopped(boolean)} */ - public void destroyHardwareResources() { + public void clearContent() { nDestroyHardwareResources(mNativeProxy); } /** * Whether or not the force-dark feature should be used for this renderer. + * @hide */ public boolean setForceDark(boolean enable) { if (mForceDark != enable) { @@ -415,20 +495,24 @@ public class HardwareRenderer { /** * Allocate buffers ahead of time to avoid allocation delays during rendering. * - * Typically a Surface will allocate buffers lazily. This is usually fine and reduces the + * <p>Typically a Surface will allocate buffers lazily. This is usually fine and reduces the * memory usage of Surfaces that render rarely or never hit triple buffering. However * for UI it can result in a slight bit of jank on first launch. This hint will * tell the HardwareRenderer that now is a good time to allocate the 3 buffers * necessary for typical rendering. * - * Must be called after a {@link Surface} has been set. + * <p>Must be called after a {@link Surface} has been set. + * + * TODO: Figure out if we even need/want this. Should HWUI just be doing this in response + * to setSurface anyway? Vulkan swapchain makes this murky, so delay making it public + * @hide */ public void allocateBuffers() { nAllocateBuffers(mNativeProxy); } /** - * Notifies the hardware renderer that a call to {@link #syncAndDrawFrame(long)} will + * Notifies the hardware renderer that a call to {@link FrameRenderRequest#syncAndDraw()} will * be coming soon. This is used to help schedule when RenderThread-driven animations will * happen as the renderer wants to avoid producing more than one frame per vsync signal. */ @@ -439,7 +523,7 @@ public class HardwareRenderer { /** * Change the HardwareRenderer's opacity. Will take effect on the next frame produced. * - * If the renderer is set to opaque it is the app's responsibility to ensure that the + * <p>If the renderer is set to opaque it is the app's responsibility to ensure that the * content renders to every pixel of the Surface, otherwise corruption may result. Note that * this includes ensuring that the first draw of any given pixel does not attempt to blend * against the destination. If this is false then the hardware renderer will clear to @@ -527,7 +611,7 @@ public class HardwareRenderer { } /** - * Prevents any further drawing until {@link #syncAndDrawFrame(long)} is called. + * Prevents any further drawing until {@link FrameRenderRequest#syncAndDraw()} is called. * This is a signal that the contents of the RenderNode tree are no longer safe to play back. * In practice this usually means that there are Functor pointers in the * display list that are no longer valid. @@ -718,10 +802,8 @@ public class HardwareRenderer { * Interface for listening to picture captures * @hide */ - @TestApi public interface PictureCapturedCallback { /** @hide */ - @TestApi void onPictureCaptured(Picture picture); } diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index 466a5fc2a770..9b5e33017743 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -372,7 +372,7 @@ public final class ImageDecoder implements AutoCloseable { } mResources = res; mInputStream = is; - mInputDensity = res != null ? inputDensity : Bitmap.DENSITY_NONE; + mInputDensity = inputDensity; } final Resources mResources; @@ -1556,12 +1556,9 @@ public final class ImageDecoder implements AutoCloseable { * decoder will pick either the color space embedded in the image or the * {@link ColorSpace} best suited for the requested image configuration * (for instance {@link ColorSpace.Named#SRGB sRGB} for the - * {@link Bitmap.Config#ARGB_8888} configuration).</p> - * - * <p>{@link Bitmap.Config#RGBA_F16} always uses the - * {@link ColorSpace.Named#LINEAR_EXTENDED_SRGB scRGB} color space. - * Bitmaps in other configurations without an embedded color space are - * assumed to be in the {@link ColorSpace.Named#SRGB sRGB} color space.</p> + * {@link Bitmap.Config#ARGB_8888} configuration and + * {@link ColorSpace.Named#EXTENDED_SRGB EXTENDED_SRGB} for + * {@link Bitmap.Config#RGBA_F16}).</p> * * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are * currently supported. An <code>IllegalArgumentException</code> will diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index 789e38c4e650..d7aee7767524 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -43,6 +43,7 @@ import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.RenderNode; import android.os.Build; +import android.os.Handler; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.IntArray; @@ -1241,6 +1242,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { // If the duration of an animation is more than 300 frames, we cap the sample size to 300. private static final int MAX_SAMPLE_POINTS = 300; + private Handler mHandler; private AnimatorListener mListener = null; private final LongArray mStartDelays = new LongArray(); private PropertyValuesHolder.PropertyValues mTmpValues = @@ -1671,6 +1673,9 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { .mRootName); } mStarted = true; + if (mHandler == null) { + mHandler = new Handler(); + } nStart(mSetPtr, this, ++mLastListenerId); invalidateOwningView(); if (mListener != null) { @@ -1780,7 +1785,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { // onFinished: should be called from native @UnsupportedAppUsage private static void callOnFinished(VectorDrawableAnimatorRT set, int id) { - set.onAnimationEnd(id); + set.mHandler.post(() -> set.onAnimationEnd(id)); } private void transferPendingActions(VectorDrawableAnimator animatorSet) { diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index 76c56609ef47..2ffda839c210 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -107,7 +107,7 @@ CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTran if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) { mRenderThread.requireGlContext(); } else { - mRenderThread.vulkanManager().initialize(); + mRenderThread.requireVkContext(); } if (!image.get()) { return CopyResult::UnknownError; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index 15f53f286261..87cffb52c150 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -120,7 +120,7 @@ bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect } DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { - mVkManager.initialize(); + mRenderThread.requireVkContext(); return new DeferredLayerUpdater(mRenderThread.renderState()); } @@ -136,8 +136,9 @@ bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBeh setSurfaceColorProperties(colorMode); if (surface) { + mRenderThread.requireVkContext(); mVkSurface = mVkManager.createSurface(surface, colorMode, mSurfaceColorSpace, - mSurfaceColorType); + mSurfaceColorType, mRenderThread.getGrContext()); } return mVkSurface != nullptr; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 3904ed20fd77..fc63819120d6 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -173,10 +173,10 @@ void RenderThread::initThreadLocals() { initializeDisplayEventReceiver(); mEglManager = new EglManager(); mRenderState = new RenderState(*this); - mVkManager = new VulkanManager(*this); + mVkManager = new VulkanManager(); mCacheManager = new CacheManager(mDisplayInfo); if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { - mVkManager->initialize(); + requireVkContext(); } } @@ -195,8 +195,7 @@ void RenderThread::requireGlContext() { LOG_ALWAYS_FATAL_IF(!glInterface.get()); GrContextOptions options; - options.fPreferExternalImagesOverES3 = true; - options.fDisableDistanceFieldPaths = true; + initGrContextOptions(options); auto glesVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION)); auto size = glesVersion ? strlen(glesVersion) : -1; cacheManager().configureContext(&options, glesVersion, size); @@ -205,6 +204,25 @@ void RenderThread::requireGlContext() { setGrContext(grContext); } +void RenderThread::requireVkContext() { + if (mVkManager->hasVkContext()) { + return; + } + mVkManager->initialize(); + GrContextOptions options; + initGrContextOptions(options); + // TODO: get a string describing the SPIR-V compiler version and use it here + cacheManager().configureContext(&options, nullptr, 0); + sk_sp<GrContext> grContext = mVkManager->createContext(options); + LOG_ALWAYS_FATAL_IF(!grContext.get()); + setGrContext(grContext); +} + +void RenderThread::initGrContextOptions(GrContextOptions& options) { + options.fPreferExternalImagesOverES3 = true; + options.fDisableDistanceFieldPaths = true; +} + void RenderThread::destroyRenderingContext() { mFunctorManager.onContextDestroyed(); if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index b18292820c6b..419e7c7a6b51 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -112,6 +112,7 @@ public: void dumpGraphicsMemory(int fd); void requireGlContext(); + void requireVkContext(); void destroyRenderingContext(); /** @@ -122,6 +123,8 @@ public: */ static bool isCurrent(); + static void initGrContextOptions(GrContextOptions& options); + protected: virtual bool threadLoop() override; diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 90397fddf618..1e685abd8afa 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -55,11 +55,7 @@ static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& fe #define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F) #define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F) -VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {} - void VulkanManager::destroy() { - mRenderThread.setGrContext(nullptr); - // We don't need to explicitly free the command buffer since it automatically gets freed when we // delete the VkCommandPool below. mDummyCB = VK_NULL_HANDLE; @@ -333,29 +329,10 @@ void VulkanManager::initialize() { LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion)); LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0)); - GrVkExtensions extensions; - this->setupDevice(extensions, mPhysicalDeviceFeatures2); + this->setupDevice(mExtensions, mPhysicalDeviceFeatures2); mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue); - auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) { - if (device != VK_NULL_HANDLE) { - return vkGetDeviceProcAddr(device, proc_name); - } - return vkGetInstanceProcAddr(instance, proc_name); - }; - - GrVkBackendContext backendContext; - backendContext.fInstance = mInstance; - backendContext.fPhysicalDevice = mPhysicalDevice; - backendContext.fDevice = mDevice; - backendContext.fQueue = mGraphicsQueue; - backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex; - backendContext.fMaxAPIVersion = mAPIVersion; - backendContext.fVkExtensions = &extensions; - backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2; - backendContext.fGetProc = std::move(getProc); - // create the command pool for the command buffers if (VK_NULL_HANDLE == mCommandPool) { VkCommandPoolCreateInfo commandPoolInfo; @@ -376,22 +353,35 @@ void VulkanManager::initialize() { } LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE); - mGetDeviceQueue(mDevice, mPresentQueueIndex, 0, &mPresentQueue); - GrContextOptions options; - options.fDisableDistanceFieldPaths = true; - // TODO: get a string describing the SPIR-V compiler version and use it here - mRenderThread.cacheManager().configureContext(&options, nullptr, 0); - sk_sp<GrContext> grContext(GrContext::MakeVulkan(backendContext, options)); - LOG_ALWAYS_FATAL_IF(!grContext.get()); - mRenderThread.setGrContext(grContext); - if (Properties::enablePartialUpdates && Properties::useBufferAge) { mSwapBehavior = SwapBehavior::BufferAge; } } +sk_sp<GrContext> VulkanManager::createContext(GrContextOptions options) { + auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) { + if (device != VK_NULL_HANDLE) { + return vkGetDeviceProcAddr(device, proc_name); + } + return vkGetInstanceProcAddr(instance, proc_name); + }; + + GrVkBackendContext backendContext; + backendContext.fInstance = mInstance; + backendContext.fPhysicalDevice = mPhysicalDevice; + backendContext.fDevice = mDevice; + backendContext.fQueue = mGraphicsQueue; + backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex; + backendContext.fMaxAPIVersion = mAPIVersion; + backendContext.fVkExtensions = &mExtensions; + backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2; + backendContext.fGetProc = std::move(getProc); + + return GrContext::MakeVulkan(backendContext, options); +} + VkFunctorInitParams VulkanManager::getVkFunctorInitParams() const { return VkFunctorInitParams{ .instance = mInstance, @@ -470,8 +460,9 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface** surfaceOut) { ColorMode colorMode = surface->mColorMode; sk_sp<SkColorSpace> colorSpace = surface->mColorSpace; SkColorType colorType = surface->mColorType; + GrContext* grContext = surface->mGrContext; destroySurface(surface); - *surfaceOut = createSurface(window, colorMode, colorSpace, colorType); + *surfaceOut = createSurface(window, colorMode, colorSpace, colorType, grContext); surface = *surfaceOut; if (!surface) { return nullptr; @@ -650,7 +641,7 @@ void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExt VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i]; imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget( - mRenderThread.getGrContext(), backendRT, kTopLeft_GrSurfaceOrigin, + surface->mGrContext, backendRT, kTopLeft_GrSurfaceOrigin, surface->mColorType, surface->mColorSpace, &props); } @@ -880,15 +871,15 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode, sk_sp<SkColorSpace> surfaceColorSpace, - SkColorType surfaceColorType) { - initialize(); - + SkColorType surfaceColorType, + GrContext* grContext) { + LOG_ALWAYS_FATAL_IF(!hasVkContext(), "Not initialized"); if (!window) { return nullptr; } VulkanSurface* surface = new VulkanSurface(colorMode, window, surfaceColorSpace, - surfaceColorType); + surfaceColorType, grContext); VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo; memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR)); diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 1fe6c65b35b8..97636865a629 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -22,6 +22,8 @@ #endif #include <vulkan/vulkan.h> +#include <GrContextOptions.h> +#include <vk/GrVkExtensions.h> #include <SkSurface.h> #include <ui/Fence.h> #include <utils/StrongPointer.h> @@ -39,9 +41,9 @@ class RenderThread; class VulkanSurface { public: VulkanSurface(ColorMode colorMode, ANativeWindow* window, sk_sp<SkColorSpace> colorSpace, - SkColorType colorType) + SkColorType colorType, GrContext* grContext) : mColorMode(colorMode), mNativeWindow(window), mColorSpace(colorSpace), - mColorType(colorType) {} + mColorType(colorType), mGrContext(grContext) {} sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; } @@ -93,6 +95,7 @@ private: SkColorType mColorType; VkSurfaceTransformFlagBitsKHR mTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; SkMatrix mPreTransform; + GrContext* mGrContext; }; // This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue, @@ -100,6 +103,9 @@ private: // windowing contexts. The VulkanManager must be initialized before use. class VulkanManager { public: + explicit VulkanManager() {} + ~VulkanManager() { destroy(); } + // Sets up the vulkan context that is shared amonst all clients of the VulkanManager. This must // be call once before use of the VulkanManager. Multiple calls after the first will simiply // return. @@ -112,7 +118,8 @@ public: // VulkanSurface object which is returned. VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode, sk_sp<SkColorSpace> surfaceColorSpace, - SkColorType surfaceColorType); + SkColorType surfaceColorType, + GrContext* grContext); // Destroy the VulkanSurface and all associated vulkan objects. void destroySurface(VulkanSurface* surface); @@ -143,12 +150,9 @@ public: // Returned pointers are owned by VulkanManager. VkFunctorInitParams getVkFunctorInitParams() const; -private: - friend class RenderThread; - - explicit VulkanManager(RenderThread& thread); - ~VulkanManager() { destroy(); } + sk_sp<GrContext> createContext(GrContextOptions options); +private: // Sets up the VkInstance and VkDevice objects. Also fills out the passed in // VkPhysicalDeviceFeatures struct. void setupDevice(GrVkExtensions&, VkPhysicalDeviceFeatures2&); @@ -231,8 +235,6 @@ private: VkPtr<PFN_vkWaitForFences> mWaitForFences; VkPtr<PFN_vkResetFences> mResetFences; - RenderThread& mRenderThread; - VkInstance mInstance = VK_NULL_HANDLE; VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE; VkDevice mDevice = VK_NULL_HANDLE; @@ -256,6 +258,7 @@ private: BufferAge, }; SwapBehavior mSwapBehavior = SwapBehavior::Discard; + GrVkExtensions mExtensions; }; } /* namespace renderthread */ diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index a9f651d38a06..e8ba15fe92af 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -100,7 +100,7 @@ void TestUtils::TestTask::run() { // RenderState only valid once RenderThread is running, so queried here renderthread::RenderThread& renderThread = renderthread::RenderThread::getInstance(); if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { - renderThread.vulkanManager().initialize(); + renderThread.requireVkContext(); } else { renderThread.requireGlContext(); } diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index d742cc34b57e..733b866d9c4c 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -115,10 +115,14 @@ PointerController::~PointerController() { mLocked.pointerSprite.clear(); - for (size_t i = 0; i < mLocked.spots.size(); i++) { - delete mLocked.spots.itemAt(i); + for (auto& it : mLocked.spotsByDisplay) { + const std::vector<Spot*>& spots = it.second; + size_t numSpots = spots.size(); + for (size_t i = 0; i < numSpots; i++) { + delete spots[i]; + } } - mLocked.spots.clear(); + mLocked.spotsByDisplay.clear(); mLocked.recycledSprites.clear(); } @@ -271,22 +275,30 @@ void PointerController::setPresentation(Presentation presentation) { } void PointerController::setSpots(const PointerCoords* spotCoords, - const uint32_t* spotIdToIndex, BitSet32 spotIdBits) { + const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId) { #if DEBUG_POINTER_UPDATES ALOGD("setSpots: idBits=%08x", spotIdBits.value); for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) { uint32_t id = idBits.firstMarkedBit(); idBits.clearBit(id); const PointerCoords& c = spotCoords[spotIdToIndex[id]]; - ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id, + ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id, c.getAxisValue(AMOTION_EVENT_AXIS_X), c.getAxisValue(AMOTION_EVENT_AXIS_Y), - c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + displayId); } #endif AutoMutex _l(mLock); + std::vector<Spot*> newSpots; + std::map<int32_t, std::vector<Spot*>>::const_iterator iter = + mLocked.spotsByDisplay.find(displayId); + if (iter != mLocked.spotsByDisplay.end()) { + newSpots = iter->second; + } + mSpriteController->openTransaction(); // Add or move spots for fingers that are down. @@ -298,17 +310,17 @@ void PointerController::setSpots(const PointerCoords* spotCoords, float x = c.getAxisValue(AMOTION_EVENT_AXIS_X); float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y); - Spot* spot = getSpotLocked(id); + Spot* spot = getSpot(id, newSpots); if (!spot) { - spot = createAndAddSpotLocked(id); + spot = createAndAddSpotLocked(id, newSpots); } - spot->updateSprite(&icon, x, y); + spot->updateSprite(&icon, x, y, displayId); } // Remove spots for fingers that went up. - for (size_t i = 0; i < mLocked.spots.size(); i++) { - Spot* spot = mLocked.spots.itemAt(i); + for (size_t i = 0; i < newSpots.size(); i++) { + Spot* spot = newSpots[i]; if (spot->id != Spot::INVALID_ID && !spotIdBits.hasBit(spot->id)) { fadeOutAndReleaseSpotLocked(spot); @@ -316,6 +328,7 @@ void PointerController::setSpots(const PointerCoords* spotCoords, } mSpriteController->closeTransaction(); + mLocked.spotsByDisplay[displayId] = newSpots; } void PointerController::clearSpots() { @@ -539,21 +552,33 @@ bool PointerController::doFadingAnimationLocked(nsecs_t timestamp) { } // Animate spots that are fading out and being removed. - for (size_t i = 0; i < mLocked.spots.size();) { - Spot* spot = mLocked.spots.itemAt(i); - if (spot->id == Spot::INVALID_ID) { - spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION; - if (spot->alpha <= 0) { - mLocked.spots.removeAt(i); - releaseSpotLocked(spot); - continue; - } else { - spot->sprite->setAlpha(spot->alpha); - keepAnimating = true; + for(auto it = mLocked.spotsByDisplay.begin(); it != mLocked.spotsByDisplay.end();) { + std::vector<Spot*>& spots = it->second; + size_t numSpots = spots.size(); + for (size_t i = 0; i < numSpots;) { + Spot* spot = spots[i]; + if (spot->id == Spot::INVALID_ID) { + spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION; + if (spot->alpha <= 0) { + spots.erase(spots.begin() + i); + releaseSpotLocked(spot); + numSpots--; + continue; + } else { + spot->sprite->setAlpha(spot->alpha); + keepAnimating = true; + } } + ++i; + } + + if (spots.size() == 0) { + it = mLocked.spotsByDisplay.erase(it); + } else { + ++it; } - ++i; } + return keepAnimating; } @@ -655,47 +680,49 @@ void PointerController::updatePointerLocked() REQUIRES(mLock) { mSpriteController->closeTransaction(); } -PointerController::Spot* PointerController::getSpotLocked(uint32_t id) { - for (size_t i = 0; i < mLocked.spots.size(); i++) { - Spot* spot = mLocked.spots.itemAt(i); +PointerController::Spot* PointerController::getSpot(uint32_t id, const std::vector<Spot*>& spots) { + for (size_t i = 0; i < spots.size(); i++) { + Spot* spot = spots[i]; if (spot->id == id) { return spot; } } - return NULL; + + return nullptr; } -PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id) { +PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id, + std::vector<Spot*>& spots) { // Remove spots until we have fewer than MAX_SPOTS remaining. - while (mLocked.spots.size() >= MAX_SPOTS) { - Spot* spot = removeFirstFadingSpotLocked(); + while (spots.size() >= MAX_SPOTS) { + Spot* spot = removeFirstFadingSpotLocked(spots); if (!spot) { - spot = mLocked.spots.itemAt(0); - mLocked.spots.removeAt(0); + spot = spots[0]; + spots.erase(spots.begin()); } releaseSpotLocked(spot); } // Obtain a sprite from the recycled pool. sp<Sprite> sprite; - if (! mLocked.recycledSprites.isEmpty()) { - sprite = mLocked.recycledSprites.top(); - mLocked.recycledSprites.pop(); + if (! mLocked.recycledSprites.empty()) { + sprite = mLocked.recycledSprites.back(); + mLocked.recycledSprites.pop_back(); } else { sprite = mSpriteController->createSprite(); } // Return the new spot. Spot* spot = new Spot(id, sprite); - mLocked.spots.push(spot); + spots.push_back(spot); return spot; } -PointerController::Spot* PointerController::removeFirstFadingSpotLocked() { - for (size_t i = 0; i < mLocked.spots.size(); i++) { - Spot* spot = mLocked.spots.itemAt(i); +PointerController::Spot* PointerController::removeFirstFadingSpotLocked(std::vector<Spot*>& spots) { + for (size_t i = 0; i < spots.size(); i++) { + Spot* spot = spots[i]; if (spot->id == Spot::INVALID_ID) { - mLocked.spots.removeAt(i); + spots.erase(spots.begin() + i); return spot; } } @@ -706,7 +733,7 @@ void PointerController::releaseSpotLocked(Spot* spot) { spot->sprite->clearIcon(); if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) { - mLocked.recycledSprites.push(spot->sprite); + mLocked.recycledSprites.push_back(spot->sprite); } delete spot; @@ -720,9 +747,13 @@ void PointerController::fadeOutAndReleaseSpotLocked(Spot* spot) { } void PointerController::fadeOutAndReleaseAllSpotsLocked() { - for (size_t i = 0; i < mLocked.spots.size(); i++) { - Spot* spot = mLocked.spots.itemAt(i); - fadeOutAndReleaseSpotLocked(spot); + for (auto& it : mLocked.spotsByDisplay) { + const std::vector<Spot*>& spots = it.second; + size_t numSpots = spots.size(); + for (size_t i = 0; i < numSpots; i++) { + Spot* spot = spots[i]; + fadeOutAndReleaseSpotLocked(spot); + } } } @@ -743,12 +774,13 @@ void PointerController::loadResourcesLocked() REQUIRES(mLock) { // --- PointerController::Spot --- -void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y) { +void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y, + int32_t displayId) { sprite->setLayer(Sprite::BASE_LAYER_SPOT + id); sprite->setAlpha(alpha); sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale)); sprite->setPosition(x, y); - + sprite->setDisplayId(displayId); this->x = x; this->y = y; diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index be057867890d..52305b8244a6 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -103,7 +103,7 @@ public: virtual void setPresentation(Presentation presentation); virtual void setSpots(const PointerCoords* spotCoords, - const uint32_t* spotIdToIndex, BitSet32 spotIdBits); + const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId); virtual void clearSpots(); void updatePointerIcon(int32_t iconId); @@ -133,7 +133,7 @@ private: : id(id), sprite(sprite), alpha(1.0f), scale(1.0f), x(0.0f), y(0.0f), lastIcon(NULL) { } - void updateSprite(const SpriteIcon* icon, float x, float y); + void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId); private: const SpriteIcon* lastIcon; @@ -180,8 +180,8 @@ private: int32_t buttonState; - Vector<Spot*> spots; - Vector<sp<Sprite> > recycledSprites; + std::map<int32_t /* displayId */, std::vector<Spot*>> spotsByDisplay; + std::vector<sp<Sprite> > recycledSprites; } mLocked GUARDED_BY(mLock); bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; @@ -200,9 +200,9 @@ private: void removeInactivityTimeoutLocked(); void updatePointerLocked(); - Spot* getSpotLocked(uint32_t id); - Spot* createAndAddSpotLocked(uint32_t id); - Spot* removeFirstFadingSpotLocked(); + Spot* getSpot(uint32_t id, const std::vector<Spot*>& spots); + Spot* createAndAddSpotLocked(uint32_t id, std::vector<Spot*>& spots); + Spot* removeFirstFadingSpotLocked(std::vector<Spot*>& spots); void releaseSpotLocked(Spot* spot); void fadeOutAndReleaseSpotLocked(Spot* spot); void fadeOutAndReleaseAllSpotsLocked(); diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java index 0caa0c5b377a..b3953fddb622 100644 --- a/location/java/android/location/LocationRequest.java +++ b/location/java/android/location/LocationRequest.java @@ -184,8 +184,7 @@ public final class LocationRequest implements Parcelable { * @return a new location request */ public static LocationRequest create() { - LocationRequest request = new LocationRequest(); - return request; + return new LocationRequest(); } /** @hide */ @@ -230,12 +229,10 @@ public final class LocationRequest implements Parcelable { quality = ACCURACY_FINE; break; default: { - switch (criteria.getPowerRequirement()) { - case Criteria.POWER_HIGH: - quality = POWER_HIGH; - break; - default: - quality = POWER_LOW; + if (criteria.getPowerRequirement() == Criteria.POWER_HIGH) { + quality = POWER_HIGH; + } else { + quality = POWER_LOW; } } } @@ -288,7 +285,7 @@ public final class LocationRequest implements Parcelable { * * @param quality an accuracy or power constant * @return the same object, so that setters can be chained - * @throws InvalidArgumentException if the quality constant is not valid + * @throws IllegalArgumentException if the quality constant is not valid */ public LocationRequest setQuality(int quality) { checkQuality(quality); @@ -331,7 +328,7 @@ public final class LocationRequest implements Parcelable { * * @param millis desired interval in millisecond, inexact * @return the same object, so that setters can be chained - * @throws InvalidArgumentException if the interval is less than zero + * @throws IllegalArgumentException if the interval is less than zero */ public LocationRequest setInterval(long millis) { checkInterval(millis); @@ -433,7 +430,7 @@ public final class LocationRequest implements Parcelable { * * @param millis fastest interval for updates in milliseconds, exact * @return the same object, so that setters can be chained - * @throws InvalidArgumentException if the interval is less than zero + * @throws IllegalArgumentException if the interval is less than zero */ public LocationRequest setFastestInterval(long millis) { checkInterval(millis); @@ -528,7 +525,7 @@ public final class LocationRequest implements Parcelable { * * @param numUpdates the number of location updates requested * @return the same object, so that setters can be chained - * @throws InvalidArgumentException if numUpdates is 0 or less + * @throws IllegalArgumentException if numUpdates is 0 or less */ public LocationRequest setNumUpdates(int numUpdates) { if (numUpdates <= 0) { @@ -668,7 +665,7 @@ public final class LocationRequest implements Parcelable { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private static void checkProvider(String name) { if (name == null) { - throw new IllegalArgumentException("invalid provider: " + name); + throw new IllegalArgumentException("invalid provider: null"); } } @@ -758,9 +755,11 @@ public final class LocationRequest implements Parcelable { if (mNumUpdates != Integer.MAX_VALUE) { s.append(" num=").append(mNumUpdates); } - s.append(" lowPowerMode=").append(mLowPowerMode); + if (mLowPowerMode) { + s.append(" lowPowerMode"); + } if (mLocationSettingsIgnored) { - s.append(" ignoreSettings"); + s.append(" locationSettingsIgnored"); } s.append(']'); return s.toString(); diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java index a45c20d9d09d..af8123ac52f4 100644 --- a/location/java/com/android/internal/location/ProviderRequest.java +++ b/location/java/com/android/internal/location/ProviderRequest.java @@ -40,7 +40,7 @@ public final class ProviderRequest implements Parcelable { * restrictions or any other restricting factors and always satisfy this request to the best of * their ability. This flag should only be used in event of an emergency. */ - public boolean forceLocation = false; + public boolean locationSettingsIgnored = false; /** * Whether provider shall make stronger than normal tradeoffs to substantially restrict power @@ -70,6 +70,7 @@ public final class ProviderRequest implements Parcelable { request.reportLocation = in.readInt() == 1; request.interval = in.readLong(); request.lowPowerMode = in.readBoolean(); + request.locationSettingsIgnored = in.readBoolean(); int count = in.readInt(); for (int i = 0; i < count; i++) { request.locationRequests.add(LocationRequest.CREATOR.createFromParcel(in)); @@ -93,6 +94,7 @@ public final class ProviderRequest implements Parcelable { parcel.writeInt(reportLocation ? 1 : 0); parcel.writeLong(interval); parcel.writeBoolean(lowPowerMode); + parcel.writeBoolean(locationSettingsIgnored); parcel.writeInt(locationRequests.size()); for (LocationRequest request : locationRequests) { request.writeToParcel(parcel, flags); @@ -107,7 +109,12 @@ public final class ProviderRequest implements Parcelable { s.append("ON"); s.append(" interval="); TimeUtils.formatDuration(interval, s); - s.append(" lowPowerMode=" + lowPowerMode); + if (lowPowerMode) { + s.append(" lowPowerMode"); + } + if (locationSettingsIgnored) { + s.append(" locationSettingsIgnored"); + } } else { s.append("OFF"); } diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt index 67d64965ac96..dbb581fe54b2 100644 --- a/location/lib/api/current.txt +++ b/location/lib/api/current.txt @@ -31,6 +31,7 @@ package com.android.location.provider { method public long getInterval(); method public int getQuality(); method public float getSmallestDisplacement(); + method public boolean isLocationSettingsIgnored(); field public static final int ACCURACY_BLOCK = 102; // 0x66 field public static final int ACCURACY_CITY = 104; // 0x68 field public static final int ACCURACY_FINE = 100; // 0x64 @@ -44,10 +45,10 @@ package com.android.location.provider { } public final class ProviderRequestUnbundled { - method public boolean getForceLocation(); method public long getInterval(); method public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests(); method public boolean getReportLocation(); + method public boolean isLocationSettingsIgnored(); } } diff --git a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java index 41fd769b5abd..2511c39caf5c 100644 --- a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java +++ b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java @@ -121,6 +121,15 @@ public final class LocationRequestUnbundled { return delegate.getSmallestDisplacement(); } + /** + * Returns true if location settings will be ignored in order to satisfy this request. + * + * @return true if location settings will be ignored in order to satisfy this request + */ + public boolean isLocationSettingsIgnored() { + return delegate.isLocationSettingsIgnored(); + } + @Override public String toString() { return delegate.toString(); diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java index b825b58cd3e9..febbf1b23e0c 100644 --- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java +++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java @@ -46,15 +46,15 @@ public final class ProviderRequestUnbundled { return mRequest.interval; } - public boolean getForceLocation() { - return mRequest.forceLocation; + public boolean isLocationSettingsIgnored() { + return mRequest.locationSettingsIgnored; } /** * Never null. */ public List<LocationRequestUnbundled> getLocationRequests() { - List<LocationRequestUnbundled> result = new ArrayList<LocationRequestUnbundled>( + List<LocationRequestUnbundled> result = new ArrayList<>( mRequest.locationRequests.size()); for (LocationRequest r : mRequest.locationRequests) { result.add(new LocationRequestUnbundled(r)); diff --git a/media/apex/java/android/media/MediaController2.java b/media/apex/java/android/media/MediaController2.java index 887b4475a4d1..e85d99774986 100644 --- a/media/apex/java/android/media/MediaController2.java +++ b/media/apex/java/android/media/MediaController2.java @@ -425,7 +425,7 @@ public class MediaController2 implements AutoCloseable { public void onDisconnected(@NonNull MediaController2 controller) {} /** - * Called when the playback of the session's playback activeness is changed. + * Called when the session's playback activeness is changed. * * @param controller the controller for this event * @param playbackActive {@code true} if the session's playback is active. diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index fb18c3b67480..e4d356b48f6d 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -1180,13 +1180,8 @@ public class MediaPlayer extends PlayerBase } final File file = new File(path); - if (file.exists()) { - FileInputStream is = new FileInputStream(file); - FileDescriptor fd = is.getFD(); - setDataSource(fd); - is.close(); - } else { - throw new IOException("setDataSource failed."); + try (FileInputStream is = new FileInputStream(file)) { + setDataSource(is.getFD()); } } @@ -2868,15 +2863,9 @@ public class MediaPlayer extends PlayerBase throw new IllegalArgumentException(msg); } - File file = new File(path); - if (file.exists()) { - FileInputStream is = new FileInputStream(file); - FileDescriptor fd = is.getFD(); - addTimedTextSource(fd, mimeType); - is.close(); - } else { - // We do not support the case where the path is not a file. - throw new IOException(path); + final File file = new File(path); + try (FileInputStream is = new FileInputStream(file)) { + addTimedTextSource(is.getFD(), mimeType); } } diff --git a/media/java/android/media/session/ControllerLink.java b/media/java/android/media/session/ControllerLink.java index 64d283f168e1..40c716607697 100644 --- a/media/java/android/media/session/ControllerLink.java +++ b/media/java/android/media/session/ControllerLink.java @@ -493,6 +493,22 @@ public final class ControllerLink implements Parcelable { } /** + * Tell system that a controller requests changing the playback speed. + * + * @param packageName the package name of the controller + * @param caller the {@link ControllerCallbackLink} of the controller + * @param speed the playback speed + */ + void setPlaybackSpeed(@NonNull String packageName, @NonNull ControllerCallbackLink caller, + float speed) { + try { + mISessionController.setPlaybackSpeed(packageName, caller, speed); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + /** * Tell system that a controller sends a custom action. * * @param packageName the package name of the controller @@ -759,6 +775,11 @@ public final class ControllerLink implements Parcelable { @NonNull Rating rating) { } + /** Stub method for ISessionController.setPlaybackSpeed */ + public void setPlaybackSpeed(@NonNull String packageName, + @NonNull ControllerCallbackLink caller, float speed) { + } + /** Stub method for ISessionController.sendCustomAction */ public void sendCustomAction(@NonNull String packageName, @NonNull ControllerCallbackLink caller, @NonNull String action, @@ -953,6 +974,12 @@ public final class ControllerLink implements Parcelable { } @Override + public void setPlaybackSpeed(String packageName, ControllerCallbackLink caller, + float speed) { + mControllerStub.setPlaybackSpeed(packageName, caller, speed); + } + + @Override public void sendCustomAction(String packageName, ControllerCallbackLink caller, String action, Bundle args) { mControllerStub.sendCustomAction(packageName, caller, action, args); diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl index 9b86bfced340..cd33c044d142 100644 --- a/media/java/android/media/session/ISessionCallback.aidl +++ b/media/java/android/media/session/ISessionCallback.aidl @@ -60,6 +60,8 @@ oneway interface ISessionCallback { long pos); void notifyRate(String packageName, int pid, int uid, in ControllerCallbackLink caller, in Rating rating); + void notifySetPlaybackSpeed(String packageName, int pid, int uid, + in ControllerCallbackLink caller, float speed); void notifyCustomAction(String packageName, int pid, int uid, in ControllerCallbackLink caller, String action, in Bundle args); diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl index e697c65e11c0..3e7b4fbdebd3 100644 --- a/media/java/android/media/session/ISessionController.aidl +++ b/media/java/android/media/session/ISessionController.aidl @@ -76,6 +76,7 @@ interface ISessionController { void rewind(String packageName, in ControllerCallbackLink caller); void seekTo(String packageName, in ControllerCallbackLink caller, long pos); void rate(String packageName, in ControllerCallbackLink caller, in Rating rating); + void setPlaybackSpeed(String packageName, in ControllerCallbackLink caller, float speed); void sendCustomAction(String packageName, in ControllerCallbackLink caller, String action, in Bundle args); MediaMetadata getMetadata(); diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index 6e2c8c51c734..9e4199cba47a 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -865,6 +865,19 @@ public final class MediaController { } /** + * Set the playback speed. + * + * @param speed The playback speed + */ + public void setPlaybackSpeed(float speed) { + try { + mSessionBinder.setPlaybackSpeed(mContext.getPackageName(), mCbStub, speed); + } catch (RuntimeException e) { + Log.wtf(TAG, "Error calling setPlaybackSpeed.", e); + } + } + + /** * Send a custom action back for the {@link MediaSession} to perform. * * @param customAction The action to perform. diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 1b9ebdafd328..8ab893b03a62 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -681,6 +681,19 @@ public final class MediaSession { } /** + * Override to handle the playback speed change. + * To update the new playback speed, create a new {@link PlaybackState} by using {@link + * PlaybackState.Builder#setState(int, long, float)}, and set it with + * {@link #setPlaybackState(PlaybackState)}. + * + * @param speed the playback speed + * @see #setPlaybackState(PlaybackState) + * @see PlaybackState.Builder#setState(int, long, float) + */ + public void onSetPlaybackSpeed(float speed) { + } + + /** * Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be * performed. * diff --git a/media/java/android/media/session/MediaSessionEngine.java b/media/java/android/media/session/MediaSessionEngine.java index e19bdbcf923e..266bf3226c49 100644 --- a/media/java/android/media/session/MediaSessionEngine.java +++ b/media/java/android/media/session/MediaSessionEngine.java @@ -538,6 +538,10 @@ public final class MediaSessionEngine implements AutoCloseable { postToCallback(caller, CallbackMessageHandler.MSG_RATE, rating, null); } + void dispatchSetPlaybackSpeed(RemoteUserInfo caller, float speed) { + postToCallback(caller, CallbackMessageHandler.MSG_SET_PLAYBACK_SPEED, speed, null); + } + void dispatchCustomAction(RemoteUserInfo caller, String action, Bundle args) { postToCallback(caller, CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args); } @@ -871,6 +875,17 @@ public final class MediaSessionEngine implements AutoCloseable { } /** + * Override to handle the playback speed change. + * + * @param speed the playback speed + */ + public void onSetPlaybackSpeed(float speed) { + if (mCallback != null) { + mCallback.onSetPlaybackSpeed(speed); + } + } + + /** * Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be * performed. * @@ -1092,10 +1107,11 @@ public final class MediaSessionEngine implements AutoCloseable { private static final int MSG_REWIND = 17; private static final int MSG_SEEK_TO = 18; private static final int MSG_RATE = 19; - private static final int MSG_CUSTOM_ACTION = 20; - private static final int MSG_ADJUST_VOLUME = 21; - private static final int MSG_SET_VOLUME = 22; - private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 23; + private static final int MSG_SET_PLAYBACK_SPEED = 20; + private static final int MSG_CUSTOM_ACTION = 21; + private static final int MSG_ADJUST_VOLUME = 22; + private static final int MSG_SET_VOLUME = 23; + private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 24; @SuppressWarnings("WeakerAccess") /* synthetic access */ CallbackWrapper mCallbackWrapper; @@ -1186,6 +1202,9 @@ public final class MediaSessionEngine implements AutoCloseable { case MSG_RATE: mCallbackWrapper.onSetRating((Rating) obj); break; + case MSG_SET_PLAYBACK_SPEED: + mCallbackWrapper.onSetPlaybackSpeed((Float) obj); + break; case MSG_CUSTOM_ACTION: mCallbackWrapper.onCustomAction((String) obj, msg.getData()); break; diff --git a/media/java/android/media/session/SessionCallbackLink.java b/media/java/android/media/session/SessionCallbackLink.java index f59a69d6e157..f9fa45a1b619 100644 --- a/media/java/android/media/session/SessionCallbackLink.java +++ b/media/java/android/media/session/SessionCallbackLink.java @@ -462,6 +462,25 @@ public final class SessionCallbackLink implements Parcelable { } /** + * Notify session that a controller requests changing playback speed. + * + * @param packageName the package name of the controller + * @param pid the pid of the controller + * @param uid the uid of the controller + * @param caller the {@link ControllerCallbackLink} of the controller + * @param speed the playback speed + */ + @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) + public void notifySetPlaybackSpeed(@NonNull String packageName, int pid, int uid, + @NonNull ControllerCallbackLink caller, float speed) { + try { + mISessionCallback.notifySetPlaybackSpeed(packageName, pid, uid, caller, speed); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + /** * Notify session that a controller sends a custom action. * * @param packageName the package name of the controller @@ -871,6 +890,23 @@ public final class SessionCallbackLink implements Parcelable { } } + @Override + public void notifySetPlaybackSpeed(String packageName, int pid, int uid, + ControllerCallbackLink caller, float speed) { + ensureMediaControlPermission(); + final long token = Binder.clearCallingIdentity(); + try { + MediaSessionEngine sessionImpl = mSessionImpl.get(); + if (sessionImpl != null) { + sessionImpl.dispatchSetPlaybackSpeed( + createRemoteUserInfo(packageName, pid, uid), speed); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override public void notifyCustomAction(String packageName, int pid, int uid, ControllerCallbackLink caller, String action, Bundle args) { ensureMediaControlPermission(); diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java index ce627ce47717..a288d010e59b 100644 --- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java +++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java @@ -175,6 +175,7 @@ public class CaptivePortalLoginActivity extends Activity { webSettings.setSupportZoom(true); webSettings.setBuiltInZoomControls(true); webSettings.setDisplayZoomControls(false); + webSettings.setDomStorageEnabled(true); mWebViewClient = new MyWebViewClient(); webview.setWebViewClient(mWebViewClient); webview.setWebChromeClient(new MyWebChromeClient()); diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java index f36b4aa87636..55c9361ce6c4 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java @@ -106,6 +106,7 @@ public class CaptivePortalLoginActivity extends Activity { webSettings.setLoadWithOverviewMode(true); webSettings.setSupportZoom(true); webSettings.setBuiltInZoomControls(true); + webSettings.setDomStorageEnabled(true); mWebViewClient = new MyWebViewClient(); mWebView.setWebViewClient(mWebViewClient); mWebView.setWebChromeClient(new MyWebChromeClient()); diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml index e4d35915c77c..860ebfbf6da7 100644 --- a/packages/NetworkStack/AndroidManifest.xml +++ b/packages/NetworkStack/AndroidManifest.xml @@ -31,6 +31,7 @@ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> <uses-permission android:name="android.permission.NETWORK_STACK" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> + <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" /> <application android:label="NetworkStack" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java index 9e5991298834..b1f6d246563e 100644 --- a/packages/NetworkStack/src/android/net/ip/IpClient.java +++ b/packages/NetworkStack/src/android/net/ip/IpClient.java @@ -46,6 +46,7 @@ import android.net.shared.ProvisioningConfiguration; import android.net.util.InterfaceParams; import android.net.util.SharedLog; import android.os.ConditionVariable; +import android.os.IBinder; import android.os.Message; import android.os.RemoteException; import android.os.SystemClock; @@ -380,6 +381,13 @@ public class IpClient extends StateMachine { public InterfaceParams getInterfaceParams(String ifname) { return InterfaceParams.getByName(ifname); } + + /** + * Get a INetd connector. + */ + public INetd getNetd(Context context) { + return INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)); + } } public IpClient(Context context, String ifName, IIpClientCallbacks callback, @@ -413,7 +421,7 @@ public class IpClient extends StateMachine { // TODO: Consider creating, constructing, and passing in some kind of // InterfaceController.Dependencies class. - mNetd = mContext.getSystemService(INetd.class); + mNetd = deps.getNetd(mContext); mInterfaceCtrl = new InterfaceController(mInterfaceName, mNetd, mLog); mLinkObserver = new IpClientLinkObserver( diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java index cedcb84e9d08..c6a207f26577 100644 --- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java +++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java @@ -114,7 +114,8 @@ public class NetworkStackService extends Service { NetworkStackConnector(Context context) { mContext = context; - mNetd = (INetd) context.getSystemService(Context.NETD_SERVICE); + mNetd = INetd.Stub.asInterface( + (IBinder) context.getSystemService(Context.NETD_SERVICE)); mObserverRegistry = new NetworkObserverRegistry(); mCm = context.getSystemService(ConnectivityManager.class); diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java index 2e72d8296a37..b9e901b5c5e3 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java @@ -67,15 +67,9 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; -import android.telephony.CellIdentityCdma; -import android.telephony.CellIdentityGsm; -import android.telephony.CellIdentityLte; -import android.telephony.CellIdentityWcdma; -import android.telephony.CellInfo; -import android.telephony.CellInfoCdma; -import android.telephony.CellInfoGsm; -import android.telephony.CellInfoLte; -import android.telephony.CellInfoWcdma; +import android.telephony.AccessNetworkConstants; +import android.telephony.NetworkRegistrationState; +import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -1312,6 +1306,7 @@ public class NetworkMonitor extends StateMachine { urlConnection.setInstanceFollowRedirects(probeType == ValidationProbeEvent.PROBE_PAC); urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS); urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS); + urlConnection.setRequestProperty("Connection", "close"); urlConnection.setUseCaches(false); if (mCaptivePortalUserAgent != null) { urlConnection.setRequestProperty("User-Agent", mCaptivePortalUserAgent); @@ -1485,10 +1480,6 @@ public class NetworkMonitor extends StateMachine { */ private void sendNetworkConditionsBroadcast(boolean responseReceived, boolean isCaptivePortal, long requestTimestampMs, long responseTimestampMs) { - if (!mWifiManager.isScanAlwaysAvailable()) { - return; - } - if (!mSystemReady) { return; } @@ -1496,6 +1487,10 @@ public class NetworkMonitor extends StateMachine { Intent latencyBroadcast = new Intent(NetworkMonitorUtils.ACTION_NETWORK_CONDITIONS_MEASURED); if (mNetworkCapabilities.hasTransport(TRANSPORT_WIFI)) { + if (!mWifiManager.isScanAlwaysAvailable()) { + return; + } + WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo(); if (currentWifiInfo != null) { // NOTE: getSSID()'s behavior changed in API 17; before that, SSIDs were not @@ -1515,39 +1510,21 @@ public class NetworkMonitor extends StateMachine { } latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_WIFI); } else if (mNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { + // TODO(b/123893112): Support multi-sim. latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_NETWORK_TYPE, mTelephonyManager.getNetworkType()); - List<CellInfo> info = mTelephonyManager.getAllCellInfo(); - if (info == null) return; - int numRegisteredCellInfo = 0; - for (CellInfo cellInfo : info) { - if (cellInfo.isRegistered()) { - numRegisteredCellInfo++; - if (numRegisteredCellInfo > 1) { - if (VDBG) { - logw("more than one registered CellInfo." - + " Can't tell which is active. Bailing."); - } - return; - } - if (cellInfo instanceof CellInfoCdma) { - CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity(); - latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId); - } else if (cellInfo instanceof CellInfoGsm) { - CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity(); - latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId); - } else if (cellInfo instanceof CellInfoLte) { - CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity(); - latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId); - } else if (cellInfo instanceof CellInfoWcdma) { - CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity(); - latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId); - } else { - if (VDBG) logw("Registered cellinfo is unrecognized"); - return; - } - } + final ServiceState dataSs = mTelephonyManager.getServiceState(); + if (dataSs == null) { + logw("failed to retrieve ServiceState"); + return; } + // See if the data sub is registered for PS services on cell. + final NetworkRegistrationState nrs = dataSs.getNetworkRegistrationState( + NetworkRegistrationState.DOMAIN_PS, + AccessNetworkConstants.TransportType.WWAN); + latencyBroadcast.putExtra( + NetworkMonitorUtils.EXTRA_CELL_ID, + nrs == null ? null : nrs.getCellIdentity()); latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_MOBILE); } else { return; diff --git a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java index 7e57d1eb00b0..aaaff0279fed 100644 --- a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java +++ b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java @@ -104,8 +104,8 @@ public class IpClientTest { when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm); when(mContext.getSystemService(eq(ConnectivityManager.class))).thenReturn(mCm); - when(mContext.getSystemService(INetd.class)).thenReturn(mNetd); when(mContext.getResources()).thenReturn(mResources); + when(mDependencies.getNetd(any())).thenReturn(mNetd); when(mResources.getInteger(R.integer.config_networkAvoidBadWifi)) .thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE); diff --git a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml index 6d35550f1b77..b198f5a35630 100644 --- a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml +++ b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml @@ -52,7 +52,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" - android:textAppearance="@android:style/TextAppearance.Material.Subhead" + android:textAppearance="?android:attr/textAppearanceListItem" android:ellipsize="marquee" android:fadingEdge="horizontal"/> @@ -65,7 +65,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" - android:textAppearance="@android:style/TextAppearance.Material.Small" + android:textAppearance="?android:attr/textAppearanceSmall" android:textAlignment="viewStart" android:textColor="?android:attr/textColorSecondary"/> @@ -73,7 +73,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" - android:textAppearance="@android:style/TextAppearance.Material.Small" + android:textAppearance="?android:attr/textAppearanceSmall" android:textAlignment="viewEnd" android:textColor="?android:attr/textColorSecondary" android:maxLines="1" diff --git a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml index 96045120fec4..013d2d004471 100644 --- a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml +++ b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml @@ -23,7 +23,7 @@ android:layout_marginEnd="16dp" android:gravity="center" android:clickable="true" - android:background="?android:attr/selectableItemBackground" + android:background="@*android:drawable/btn_borderless_material" android:orientation="vertical"> <ImageView diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk index 8d24eaba5cc4..8c309ff97370 100644 --- a/packages/SettingsLib/common.mk +++ b/packages/SettingsLib/common.mk @@ -31,4 +31,3 @@ LOCAL_STATIC_ANDROID_LIBRARIES += \ androidx.legacy_legacy-preference-v14 \ SettingsLib -LOCAL_RESOURCE_DIR += $(call my-dir)/res diff --git a/packages/SettingsLib/res/drawable/ic_info_outline_24.xml b/packages/SettingsLib/res/drawable/ic_info_outline_24.xml new file mode 100644 index 000000000000..317e43b95cdd --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_info_outline_24.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?android:attr/colorControlNormal"> + <path + android:fillColor="#FF000000" + android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/> +</vector> diff --git a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java index a106846fac26..2a1281027169 100644 --- a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java @@ -56,7 +56,7 @@ public class FooterPreference extends Preference { } private void init() { - setIcon(com.android.internal.R.drawable.ic_info_outline_24); + setIcon(R.drawable.ic_info_outline_24); setKey(KEY_FOOTER); setOrder(ORDER_FOOTER); setSelectable(false); diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml index 51f6a4b92413..dc45b4b51925 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml @@ -28,11 +28,20 @@ android:clipToPadding="false" android:orientation="vertical" android:layout_centerHorizontal="true"> + <TextView + android:id="@+id/title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingStart="64dp" + android:paddingEnd="64dp" + android:visibility="gone" + android:textColor="?attr/wallpaperTextColor" + android:theme="@style/TextAppearance.Keyguard" + /> <view class="com.android.keyguard.KeyguardSliceView$Row" android:id="@+id/row" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/subtitle_clock_padding" android:orientation="horizontal" android:gravity="center" /> diff --git a/packages/SystemUI/res-keyguard/layout/text_clock.xml b/packages/SystemUI/res-keyguard/layout/text_clock.xml index b61ad9c4fc11..9f7ea0d44357 100644 --- a/packages/SystemUI/res-keyguard/layout/text_clock.xml +++ b/packages/SystemUI/res-keyguard/layout/text_clock.xml @@ -16,9 +16,10 @@ --> <TextClock xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" + android:gravity="center_horizontal" android:letterSpacing="0.03" android:textColor="?attr/wallpaperTextColor" android:singleLine="true" diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index e2ba23ec898c..b6a41c19ec32 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -42,18 +42,18 @@ <dimen name="eca_overlap">-10dip</dimen> <!-- Slice header --> - <dimen name="widget_title_font_size">24dp</dimen> - <dimen name="widget_title_bottom_margin">14dp</dimen> - <dimen name="bottom_text_spacing_digital">0dp</dimen> + <dimen name="widget_title_font_size">22dp</dimen> + <dimen name="header_subtitle_padding">4dp</dimen> + <dimen name="header_icon_size">20dp</dimen> <!-- Slice subtitle --> <dimen name="widget_label_font_size">16dp</dimen> <!-- Clock without header --> <dimen name="widget_big_font_size">64dp</dimen> + <dimen name="bottom_text_spacing_digital">0dp</dimen> <!-- Clock with header --> - <dimen name="widget_small_clock_padding">-25dp</dimen> - <dimen name="widget_small_font_size">24dp</dimen> - <dimen name="widget_small_font_stroke">0.6dp</dimen> + <dimen name="widget_small_font_size">22dp</dimen> <dimen name="widget_vertical_padding">32dp</dimen> + <dimen name="widget_vertical_padding_clock">30dp</dimen> <!-- Subtitle paddings --> <dimen name="widget_horizontal_padding">8dp</dimen> <dimen name="widget_icon_size">16dp</dimen> diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml index b673e4f3b081..dd124b713a72 100644 --- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml +++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml @@ -14,12 +14,19 @@ Copyright (C) 2017 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0" - android:tint="?android:attr/colorControlNormal" > + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?android:attr/colorControlNormal"> + + <path + android:fillColor="#FFFFFFFF" + android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L11,14.41V22h1l5.71-5.71L13.41,12L17.71,7.71z M13,5.83 l1.88,1.88L13,9.59V5.83z M14.88,16.29L13,18.17v-3.76L14.88,16.29z" /> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M 5 10.5 C 5.82842712475 10.5 6.5 11.1715728753 6.5 12 C 6.5 12.8284271247 5.82842712475 13.5 5 13.5 C 4.17157287525 13.5 3.5 12.8284271247 3.5 12 C 3.5 11.1715728753 4.17157287525 10.5 5 10.5 Z" /> <path - android:pathData="M13.51,12l3.75,-3.74c0.41,-0.41 0.41,-1.07 0,-1.48l-4.47,-4.47 -0.03,-0.03a1.046,1.046 0,0 0,-1.76 0.76v6.44L6.95,5.43c-0.41,-0.41 -1.06,-0.41 -1.47,0s-0.41,1.06 0,1.47l5.09,5.1 -5.09,5.09c-0.41,0.41 -0.41,1.06 0,1.47s1.06,0.41 1.47,0L11,14.51v6.45a1.04,1.04 0,0 0,1.75 0.76l0.05,-0.05 4.46,-4.46c0.41,-0.41 0.41,-1.07 0,-1.48L13.51,12zM12.99,9.67v-4.3l2.15,2.15 -2.15,2.15zM12.99,18.62v-4.3l2.15,2.15 -2.15,2.15zM6.06,13.06c-0.59,0.59 -1.54,0.59 -2.12,0a1.49,1.49 0,0 1,0 -2.12,1.49 1.49,0 0,1 2.12,0c0.59,0.59 0.59,1.53 0,2.12zM20.06,10.94c0.59,0.59 0.59,1.54 0,2.12 -0.59,0.59 -1.54,0.59 -2.12,0a1.49,1.49 0,0 1,0 -2.12,1.49 1.49,0 0,1 2.12,0z" - android:fillColor="#FFFFFFFF" /> + android:fillColor="#FFFFFFFF" + android:pathData="M 19 10.5 C 19.8284271247 10.5 20.5 11.1715728753 20.5 12 C 20.5 12.8284271247 19.8284271247 13.5 19 13.5 C 18.1715728753 13.5 17.5 12.8284271247 17.5 12 C 17.5 11.1715728753 18.1715728753 10.5 19 10.5 Z" /> </vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml index 8cc6caa8abc8..220c63ccca6d 100644 --- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml +++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml @@ -14,12 +14,13 @@ Copyright (C) 2017 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="64dp" - android:height="64dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0" - android:tint="?android:attr/colorControlNormal"> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?android:attr/colorControlNormal"> + <path android:fillColor="#FFFFFFFF" - android:pathData="M13.5,12l3.8,-3.7c0.4,-0.4 0.4,-1.1 0,-1.5l-4.5,-4.5c-0.4,-0.4 -1.1,-0.4 -1.5,0.1C11.1,2.5 11,2.8 11,3v6.4L6.9,5.4C6.5,5 5.9,5 5.5,5.4s-0.4,1.1 0,1.5l5.1,5.1l-5.1,5.1c-0.4,0.4 -0.4,1.1 0,1.5s1.1,0.4 1.5,0l4.1,-4V21c0,0.6 0.5,1 1,1c0.3,0 0.5,-0.1 0.7,-0.3l0.1,0l4.5,-4.5c0.4,-0.4 0.4,-1.1 0,-1.5L13.5,12zM13,9.7V5.4l2.1,2.2L13,9.7zM13,18.6v-4.3l2.1,2.2L13,18.6z"/> + android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L11,14.41V22h1l5.71-5.71L13.41,12L17.71,7.71z M13,5.83 l1.88,1.88L13,9.59V5.83z M14.88,16.29L13,18.17v-3.76L14.88,16.29z" /> </vector> diff --git a/packages/SystemUI/res/drawable/ic_signal_airplane.xml b/packages/SystemUI/res/drawable/ic_signal_airplane.xml index 0a4d7526a55d..f708ed9cb8a6 100644 --- a/packages/SystemUI/res/drawable/ic_signal_airplane.xml +++ b/packages/SystemUI/res/drawable/ic_signal_airplane.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2017 The Android Open Source Project + Copyright (C) 2019 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,16 +15,12 @@ limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="48dp" - android:height="48dp" - android:viewportWidth="20.5" - android:viewportHeight="20.5"> - <group - android:translateX="1.75" - android:translateY="1.4"> - <path - android:pathData="M16.01,9.87l-6.24,-3.9v-4.7C9.77,0.57 9.21,0 8.5,0S7.23,0.57 7.23,1.28v4.7L0.99,9.88c-0.37,0.23 -0.6,0.64 -0.6,1.08v0.41c0,0.29 0.29,0.5 0.55,0.41l6.27,-1.97v4.7l-1.37,1.02c-0.21,0.16 -0.34,0.41 -0.34,0.68v0.57c0,0.15 0.12,0.23 0.27,0.2 1.67,-0.47 1.12,-0.31 2.73,-0.78 1.03,0.3 1.7,0.49 2.72,0.78 0.15,0.03 0.27,-0.06 0.27,-0.2v-0.57c0,-0.27 -0.13,-0.52 -0.34,-0.68l-1.37,-1.02v-4.7l6.27,1.97c0.28,0.09 0.55,-0.12 0.55,-0.41v-0.41c0.01,-0.45 -0.23,-0.87 -0.59,-1.09z" - android:fillColor="#FFF"/> - </group> -</vector> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> +<path + android:fillColor="#FFFFFF" + android:pathData="M21,16v-2l-8-5V3.5C13,2.67,12.33,2,11.5,2S10,2.67,10,3.5V9l-8,5v2l8-2.5V19l-2,1.5V22l3.5-1l3.5,1v-1.5L13,19v-5.5L21,16z" /> +</vector> diff --git a/packages/SystemUI/res/drawable/rounded_bg.xml b/packages/SystemUI/res/drawable/rounded_bg.xml index c23a87fbfb79..3de67de95f31 100644 --- a/packages/SystemUI/res/drawable/rounded_bg.xml +++ b/packages/SystemUI/res/drawable/rounded_bg.xml @@ -3,8 +3,8 @@ android:shape="rectangle"> <solid android:color="?android:attr/colorPrimary" /> <corners - android:bottomLeftRadius="@dimen/corner_size" - android:topLeftRadius="@dimen/corner_size" + android:bottomLeftRadius="?android:attr/dialogCornerRadius" + android:topLeftRadius="?android:attr/dialogCornerRadius" android:bottomRightRadius="0dp" android:topRightRadius="0dp" /> diff --git a/packages/SystemUI/res/drawable/rounded_bg_bottom.xml b/packages/SystemUI/res/drawable/rounded_bg_bottom.xml index b3bea635f953..7db59e9bad7c 100644 --- a/packages/SystemUI/res/drawable/rounded_bg_bottom.xml +++ b/packages/SystemUI/res/drawable/rounded_bg_bottom.xml @@ -3,7 +3,7 @@ android:shape="rectangle"> <solid android:color="?android:attr/colorPrimaryDark" /> <corners - android:bottomLeftRadius="@dimen/corner_size" + android:bottomLeftRadius="?android:attr/dialogCornerRadius" android:topLeftRadius="0dp" android:bottomRightRadius="0dp" android:topRightRadius="0dp" diff --git a/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml b/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml index 622226f25de3..382ca20268b7 100644 --- a/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml +++ b/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml @@ -3,9 +3,9 @@ android:shape="rectangle"> <solid android:color="?android:attr/panelColorBackground" /> <corners - android:bottomLeftRadius="@dimen/corner_size" + android:bottomLeftRadius="?android:attr/dialogCornerRadius" android:topLeftRadius="0dp" - android:bottomRightRadius="@dimen/corner_size" + android:bottomRightRadius="?android:attr/dialogCornerRadius" android:topRightRadius="0dp" /> </shape> diff --git a/packages/SystemUI/res/drawable/rounded_bg_full.xml b/packages/SystemUI/res/drawable/rounded_bg_full.xml index 03f18bb5d680..e0d3f63e8f40 100644 --- a/packages/SystemUI/res/drawable/rounded_bg_full.xml +++ b/packages/SystemUI/res/drawable/rounded_bg_full.xml @@ -3,9 +3,9 @@ android:shape="rectangle"> <solid android:color="?android:attr/colorBackgroundFloating" /> <corners - android:bottomLeftRadius="@dimen/corner_size" - android:topLeftRadius="@dimen/corner_size" - android:bottomRightRadius="@dimen/corner_size" - android:topRightRadius="@dimen/corner_size" + android:bottomLeftRadius="?android:attr/dialogCornerRadius" + android:topLeftRadius="?android:attr/dialogCornerRadius" + android:bottomRightRadius="?android:attr/dialogCornerRadius" + android:topRightRadius="?android:attr/dialogCornerRadius" /> </shape> diff --git a/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml b/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml index a4b3c99f7ec6..a62657d14afc 100644 --- a/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml +++ b/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml @@ -17,9 +17,9 @@ android:shape="rectangle"> <solid android:color="?android:attr/colorPrimaryDark" /> <corners - android:bottomLeftRadius="@dimen/corner_size" + android:bottomLeftRadius="?android:attr/dialogCornerRadius" android:topLeftRadius="0dp" - android:bottomRightRadius="@dimen/corner_size" + android:bottomRightRadius="?android:attr/dialogCornerRadius" android:topRightRadius="0dp" /> </shape> diff --git a/packages/SystemUI/res/layout-land/global_actions_grid.xml b/packages/SystemUI/res/layout-land/global_actions_grid.xml new file mode 100644 index 000000000000..911b661d48eb --- /dev/null +++ b/packages/SystemUI/res/layout-land/global_actions_grid.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="utf-8"?> +<com.android.systemui.globalactions.GlobalActionsGridLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@id/global_actions_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal" + android:clipToPadding="false" + android:theme="@style/qs_theme" + android:gravity="top|right" + android:clipChildren="false" +> + + <LinearLayout + android:layout_height="match_parent" + android:layout_width="wrap_content" + android:gravity="top|right" + android:padding="0dp" + android:orientation="vertical" + android:layoutDirection="ltr" + android:layout_marginRight="@dimen/global_actions_grid_container_bottom_margin" + > + <!-- Grid of action items --> + <com.android.systemui.globalactions.ListGridLayout + android:id="@android:id/list" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layoutDirection="ltr" + android:layout_marginTop="@dimen/global_actions_grid_side_margin" + android:translationZ="@dimen/global_actions_translate" + android:paddingLeft="@dimen/global_actions_grid_top_padding" + android:paddingRight="@dimen/global_actions_grid_bottom_padding" + android:paddingTop="@dimen/global_actions_grid_left_padding" + android:paddingBottom="@dimen/global_actions_grid_right_padding" + android:background="?android:attr/colorBackgroundFloating" + > + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:layoutDirection="ltr" + android:orientation="horizontal" + /> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:layoutDirection="ltr" + android:orientation="horizontal" + /> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:layoutDirection="ltr" + android:orientation="horizontal" + /> + </com.android.systemui.globalactions.ListGridLayout> + + <!-- For separated items--> + <LinearLayout + android:id="@+id/separated_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/global_actions_grid_side_margin" + android:layout_marginBottom="@dimen/global_actions_grid_side_margin" + android:paddingTop="@dimen/global_actions_grid_left_padding" + android:paddingLeft="@dimen/global_actions_grid_top_padding" + android:paddingBottom="@dimen/global_actions_grid_right_padding" + android:paddingRight="@dimen/global_actions_grid_bottom_padding" + android:orientation="horizontal" + android:layoutDirection="ltr" + android:background="?android:attr/colorBackgroundFloating" + android:translationZ="@dimen/global_actions_translate" + /> + + </LinearLayout> + +</com.android.systemui.globalactions.GlobalActionsGridLayout> diff --git a/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml b/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml new file mode 100644 index 000000000000..669be1b40567 --- /dev/null +++ b/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="utf-8"?> +<com.android.systemui.globalactions.GlobalActionsGridLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@id/global_actions_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal" + android:clipToPadding="false" + android:theme="@style/qs_theme" + android:gravity="top|left" + android:clipChildren="false" +> + + <LinearLayout + android:layout_height="match_parent" + android:layout_width="wrap_content" + android:gravity="bottom|left" + android:padding="0dp" + android:orientation="vertical" + android:layout_marginLeft="@dimen/global_actions_grid_container_bottom_margin" + > + <!-- For separated items--> + <LinearLayout + android:id="@+id/separated_button" + android:layout_gravity="top|left" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/global_actions_grid_side_margin" + android:layout_marginBottom="@dimen/global_actions_grid_side_margin" + android:paddingTop="@dimen/global_actions_grid_left_padding" + android:paddingLeft="@dimen/global_actions_grid_top_padding" + android:paddingBottom="@dimen/global_actions_grid_right_padding" + android:paddingRight="@dimen/global_actions_grid_bottom_padding" + android:orientation="horizontal" + android:layoutDirection="rtl" + android:background="?android:attr/colorBackgroundFloating" + android:translationZ="@dimen/global_actions_translate" + /> + + <!-- Grid of action items --> + <com.android.systemui.globalactions.ListGridLayout + android:id="@android:id/list" + android:layout_gravity="bottom|left" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_marginTop="@dimen/global_actions_grid_side_margin" + android:translationZ="@dimen/global_actions_translate" + android:paddingLeft="@dimen/global_actions_grid_top_padding" + android:paddingRight="@dimen/global_actions_grid_bottom_padding" + android:paddingTop="@dimen/global_actions_grid_left_padding" + android:paddingBottom="@dimen/global_actions_grid_right_padding" + android:background="?android:attr/colorBackgroundFloating" + > + <LinearLayout + android:layout_gravity="bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:layoutDirection="rtl" + android:orientation="horizontal" + /> + <LinearLayout + android:layout_gravity="bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:layoutDirection="rtl" + android:orientation="horizontal" + /> + <LinearLayout + android:layout_gravity="bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:layoutDirection="rtl" + android:orientation="horizontal" + /> + </com.android.systemui.globalactions.ListGridLayout> + </LinearLayout> + +</com.android.systemui.globalactions.GlobalActionsGridLayout> diff --git a/packages/SystemUI/res/layout/global_actions_grid.xml b/packages/SystemUI/res/layout/global_actions_grid.xml index e6f2376ae76b..1b56fa089281 100644 --- a/packages/SystemUI/res/layout/global_actions_grid.xml +++ b/packages/SystemUI/res/layout/global_actions_grid.xml @@ -12,10 +12,11 @@ > <LinearLayout - android:layout_height="290dp" - android:layout_width="412dp" - android:gravity="bottom" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:gravity="bottom | right" android:padding="0dp" + android:layoutDirection="ltr" android:layout_marginBottom="@dimen/global_actions_grid_container_bottom_margin" > <!-- For separated items--> @@ -34,15 +35,11 @@ android:translationZ="@dimen/global_actions_translate" /> - <Space android:layout_width="match_parent" android:layout_height="2dp" - android:layout_weight="1" /> - <!-- Grid of action items --> <com.android.systemui.globalactions.ListGridLayout android:id="@android:id/list" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:gravity="right" android:orientation="horizontal" android:layoutDirection="rtl" android:layout_marginRight="@dimen/global_actions_grid_side_margin" @@ -56,25 +53,19 @@ <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="bottom|right" android:visibility="gone" - android:gravity="bottom" android:orientation="vertical" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="bottom|right" android:visibility="gone" - android:gravity="bottom" android:orientation="vertical" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="bottom|right" android:visibility="gone" - android:gravity="bottom" android:orientation="vertical" /> </com.android.systemui.globalactions.ListGridLayout> diff --git a/packages/SystemUI/res/layout/global_actions_grid_item.xml b/packages/SystemUI/res/layout/global_actions_grid_item.xml index 0c11cd977256..a8938390690f 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_item.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_item.xml @@ -47,6 +47,7 @@ android:gravity="center" android:textSize="12sp" android:textAppearance="?android:attr/textAppearanceSmall" + android:singleLine="true" /> <TextView @@ -57,5 +58,6 @@ android:gravity="center" android:textColor="?android:attr/textColorTertiary" android:textAppearance="?android:attr/textAppearanceSmall" + android:singleLine="true" /> </LinearLayout> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 7d009b5a9f18..b4131d738d13 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2323,7 +2323,7 @@ <!-- Action for accepting the Ongoing privacy dialog [CHAR LIMIT=10]--> <string name="ongoing_privacy_dialog_ok">Got it</string> - <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=20]--> + <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=23]--> <string name="ongoing_privacy_dialog_open_settings">Privacy settings</string> <!-- Text for item in Ongoing Privacy Dialog title when only one app is using app ops [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 822920e63460..8de84bf4e2af 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -1,10 +1,19 @@ package com.android.keyguard; +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ValueAnimator; import android.app.WallpaperManager; import android.content.Context; import android.graphics.Paint; import android.graphics.Paint.Style; +import android.transition.ChangeBounds; +import android.transition.Transition; +import android.transition.TransitionManager; +import android.transition.TransitionValues; import android.util.AttributeSet; +import android.util.MathUtils; +import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; @@ -16,6 +25,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; import com.android.keyguard.clock.ClockManager; import com.android.systemui.Dependency; +import com.android.systemui.Interpolators; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -28,6 +38,7 @@ import java.util.TimeZone; */ public class KeyguardClockSwitch extends RelativeLayout { + private final Transition mTransition; /** * Optional/alternative clock injected via plugin. */ @@ -53,6 +64,10 @@ public class KeyguardClockSwitch extends RelativeLayout { * Maintain state so that a newly connected plugin can be initialized. */ private float mDarkAmount; + /** + * If the Keyguard Slice has a header (big center-aligned text.) + */ + private boolean mShowingHeader; private boolean mSupportsDarkText; private int[] mColorPalette; @@ -98,6 +113,7 @@ public class KeyguardClockSwitch extends RelativeLayout { public KeyguardClockSwitch(Context context, AttributeSet attrs) { super(context, attrs); + mTransition = new ClockBoundsTransition(); } /** @@ -286,6 +302,26 @@ public class KeyguardClockSwitch extends RelativeLayout { } } + /** + * Sets if the keyguard slice is showing a center-aligned header. We need a smaller clock + * in these cases. + */ + public void setKeyguardShowingHeader(boolean hasHeader) { + if (mShowingHeader == hasHeader || hasCustomClock()) { + return; + } + mShowingHeader = hasHeader; + + TransitionManager.beginDelayedTransition((ViewGroup) mClockView.getParent(), mTransition); + int fontSize = mContext.getResources().getDimensionPixelSize(mShowingHeader + ? R.dimen.widget_small_font_size : R.dimen.widget_big_font_size); + int paddingBottom = mContext.getResources().getDimensionPixelSize(mShowingHeader + ? R.dimen.widget_vertical_padding_clock : R.dimen.header_subtitle_padding); + mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize); + mClockView.setPadding(mClockView.getPaddingLeft(), mClockView.getPaddingTop(), + mClockView.getPaddingRight(), paddingBottom); + } + @VisibleForTesting (otherwise = VisibleForTesting.NONE) ClockManager.ClockChangedListener getClockChangedListener() { return mClockChangedListener; @@ -295,4 +331,54 @@ public class KeyguardClockSwitch extends RelativeLayout { StatusBarStateController.StateListener getStateListener() { return mStateListener; } + + /** + * Special layout transition that scales the clock view as its bounds change, to make it look + * like the text is shrinking. + */ + private class ClockBoundsTransition extends ChangeBounds { + + ClockBoundsTransition() { + setDuration(KeyguardSliceView.DEFAULT_ANIM_DURATION / 2); + setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + } + + @Override + public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, + TransitionValues endValues) { + Animator animator = super.createAnimator(sceneRoot, startValues, endValues); + if (animator == null || startValues.view != mClockView) { + return animator; + } + + ValueAnimator boundsAnimator = null; + if (animator instanceof AnimatorSet) { + Animator first = ((AnimatorSet) animator).getChildAnimations().get(0); + if (first instanceof ValueAnimator) { + boundsAnimator = (ValueAnimator) first; + } + } else if (animator instanceof ValueAnimator) { + boundsAnimator = (ValueAnimator) animator; + } + + if (boundsAnimator != null) { + float bigFontSize = mContext.getResources() + .getDimensionPixelSize(R.dimen.widget_big_font_size); + float smallFontSize = mContext.getResources() + .getDimensionPixelSize(R.dimen.widget_small_font_size); + float startScale = mShowingHeader + ? bigFontSize / smallFontSize : smallFontSize / bigFontSize; + boundsAnimator.addUpdateListener(animation -> { + float scale = MathUtils.lerp(startScale, 1f /* stop */, + animation.getAnimatedFraction()); + mClockView.setPivotX(mClockView.getWidth() / 2); + mClockView.setPivotY(0); + mClockView.setScaleX(scale); + mClockView.setScaleY(scale); + }); + } + + return animator; + } + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index bac7844c024b..2040a76b61d3 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -37,10 +37,10 @@ import android.text.TextUtils.TruncateAt; import android.util.AttributeSet; import android.util.Log; import android.view.View; -import android.view.ViewGroup; import android.view.animation.Animation; import android.widget.Button; import android.widget.LinearLayout; +import android.widget.TextView; import androidx.lifecycle.LiveData; import androidx.lifecycle.Observer; @@ -58,6 +58,7 @@ import com.android.internal.graphics.ColorUtils; import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; +import com.android.systemui.R; import com.android.systemui.keyguard.KeyguardSliceProvider; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; @@ -78,6 +79,8 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe private final HashMap<View, PendingIntent> mClickActions; private Uri mKeyguardSliceUri; + @VisibleForTesting + TextView mTitle; private Row mRow; private int mTextColor; private float mDarkAmount = 0; @@ -91,6 +94,8 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe private Runnable mContentChangeListener; private Slice mSlice; private boolean mHasHeader; + private final int mRowWithHeaderPadding; + private final int mRowPadding; public KeyguardSliceView(Context context) { this(context, null, 0); @@ -107,6 +112,9 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe tunerService.addTunable(this, Settings.Secure.KEYGUARD_SLICE_URI); mClickActions = new HashMap<>(); + mRowPadding = context.getResources().getDimensionPixelSize(R.dimen.subtitle_clock_padding); + mRowWithHeaderPadding = context.getResources() + .getDimensionPixelSize(R.dimen.header_subtitle_padding); LayoutTransition transition = new LayoutTransition(); transition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2); @@ -117,13 +125,13 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe transition.setInterpolator(LayoutTransition.APPEARING, Interpolators.FAST_OUT_SLOW_IN); transition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT); transition.setAnimateParentHierarchy(false); - transition.addTransitionListener(new SliceViewTransitionListener()); setLayoutTransition(transition); } @Override protected void onFinishInflate() { super.onFinishInflate(); + mTitle = findViewById(R.id.title); mRow = findViewById(R.id.row); mTextColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor); mIconSize = (int) mContext.getResources().getDimension(R.dimen.widget_icon_size); @@ -160,6 +168,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe private void showSlice() { Trace.beginSection("KeyguardSliceView#showSlice"); if (mSlice == null) { + mTitle.setVisibility(GONE); mRow.setVisibility(GONE); mHasHeader = false; if (mContentChangeListener != null) { @@ -170,8 +179,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe ListContent lc = new ListContent(getContext(), mSlice); SliceContent headerContent = lc.getHeader(); - mHasHeader = headerContent != null - && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM); + mHasHeader = headerContent != null && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM); List<SliceContent> subItems = new ArrayList<>(); for (int i = 0; i < lc.getRowItems().size(); i++) { SliceContent subItem = lc.getRowItems().get(i); @@ -181,12 +189,26 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe subItems.add(subItem); } } + if (!mHasHeader) { + mTitle.setVisibility(GONE); + } else { + mTitle.setVisibility(VISIBLE); + + RowContent header = lc.getHeader(); + SliceItem mainTitle = header.getTitleItem(); + CharSequence title = mainTitle != null ? mainTitle.getText() : null; + mTitle.setText(title); + } mClickActions.clear(); final int subItemsCount = subItems.size(); final int blendedColor = getTextColor(); final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it mRow.setVisibility(subItemsCount > 0 ? VISIBLE : GONE); + LinearLayout.LayoutParams layoutParams = (LayoutParams) mRow.getLayoutParams(); + layoutParams.topMargin = mHasHeader ? mRowWithHeaderPadding : mRowPadding; + mRow.setLayoutParams(layoutParams); + for (int i = startIndex; i < subItemsCount; i++) { RowContent rc = (RowContent) subItems.get(i); SliceItem item = rc.getSliceItem(); @@ -250,6 +272,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe private void updateTextColors() { final int blendedColor = getTextColor(); + mTitle.setTextColor(blendedColor); int childCount = mRow.getChildCount(); for (int i = 0; i < childCount; i++) { View v = mRow.getChildAt(i); @@ -294,7 +317,10 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe setupUri(newValue); } - private void setupUri(String uriString) { + /** + * Sets the slice provider Uri. + */ + public void setupUri(String uriString) { if (uriString == null) { uriString = KeyguardSliceProvider.KEYGUARD_SLICE_URI; } @@ -512,29 +538,4 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe } } } - - private class SliceViewTransitionListener implements LayoutTransition.TransitionListener { - @Override - public void startTransition(LayoutTransition transition, ViewGroup container, View view, - int transitionType) { - switch (transitionType) { - case LayoutTransition.APPEARING: - int translation = getResources().getDimensionPixelSize( - R.dimen.pulsing_notification_appear_translation); - view.setTranslationY(translation); - view.animate() - .translationY(0) - .setDuration(DEFAULT_ANIM_DURATION) - .setInterpolator(Interpolators.ALPHA_IN) - .start(); - break; - } - } - - @Override - public void endTransition(LayoutTransition transition, ViewGroup container, View view, - int transitionType) { - - } - } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index bb549ada6830..b0670fd0a91a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -16,14 +16,11 @@ package com.android.keyguard; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.app.ActivityManager; import android.app.IActivityManager; import android.content.Context; import android.content.res.Resources; import android.graphics.Color; -import android.graphics.Paint; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; @@ -37,15 +34,12 @@ import android.util.Slog; import android.util.TypedValue; import android.view.View; import android.widget.GridLayout; -import android.widget.LinearLayout; import android.widget.TextView; import androidx.core.graphics.ColorUtils; import com.android.internal.widget.LockPatternUtils; -import com.android.internal.widget.ViewClippingUtil; import com.android.systemui.Dependency; -import com.android.systemui.Interpolators; import com.android.systemui.statusbar.policy.ConfigurationController; import com.google.android.collect.Sets; @@ -54,14 +48,13 @@ import java.util.Locale; import java.util.TimeZone; public class KeyguardStatusView extends GridLayout implements - ConfigurationController.ConfigurationListener, View.OnLayoutChangeListener { + ConfigurationController.ConfigurationListener { private static final boolean DEBUG = KeyguardConstants.DEBUG; private static final String TAG = "KeyguardStatusView"; private static final int MARQUEE_DELAY_MS = 2000; private final LockPatternUtils mLockPatternUtils; private final IActivityManager mIActivityManager; - private final float mSmallClockScale; private TextView mLogoutView; private KeyguardClockSwitch mClockView; @@ -74,8 +67,6 @@ public class KeyguardStatusView extends GridLayout implements private boolean mPulsing; private float mDarkAmount = 0; private int mTextColor; - private int mLastLayoutHeight; - private int mSmallClockPadding; private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { @@ -135,8 +126,6 @@ public class KeyguardStatusView extends GridLayout implements mIActivityManager = ActivityManager.getService(); mLockPatternUtils = new LockPatternUtils(getContext()); mHandler = new Handler(Looper.myLooper()); - mSmallClockScale = getResources().getDimension(R.dimen.widget_small_font_size) - / getResources().getDimension(R.dimen.widget_big_font_size); onDensityOrFontScaleChanged(); } @@ -189,9 +178,6 @@ public class KeyguardStatusView extends GridLayout implements mVisibleInDoze = Sets.newArraySet(mClockView, mKeyguardSlice); mTextColor = mClockView.getCurrentTextColor(); - int clockStroke = getResources().getDimensionPixelSize(R.dimen.widget_small_font_stroke); - mClockView.getPaint().setStrokeWidth(clockStroke); - mClockView.addOnLayoutChangeListener(this); mKeyguardSlice.setContentChangeListener(this::onSliceContentChanged); onSliceContentChanged(); @@ -207,72 +193,20 @@ public class KeyguardStatusView extends GridLayout implements * Moves clock, adjusting margins when slice content changes. */ private void onSliceContentChanged() { - LinearLayout.LayoutParams layoutParams = - (LinearLayout.LayoutParams) mClockView.getLayoutParams(); - layoutParams.bottomMargin = mKeyguardSlice.hasHeader() ? mSmallClockPadding : 0; - mClockView.setLayoutParams(layoutParams); - } - - /** - * Animate clock when necessary. - */ - @Override - public void onLayoutChange(View view, int left, int top, int right, int bottom, - int oldLeft, int oldTop, int oldRight, int oldBottom) { - boolean smallClock = mKeyguardSlice.hasHeader(); - int heightOffset = smallClock ? 0 : getHeight() - mLastLayoutHeight; - long duration = KeyguardSliceView.DEFAULT_ANIM_DURATION; - long delay = smallClock ? 0 : duration / 4; - - boolean shouldAnimate = mKeyguardSlice.getLayoutTransition() != null - && mKeyguardSlice.getLayoutTransition().isRunning(); - if (view == mClockView) { - float clockScale = smallClock ? mSmallClockScale : 1; - Paint.Style style = smallClock ? Paint.Style.FILL_AND_STROKE : Paint.Style.FILL; - mClockView.animate().cancel(); - if (shouldAnimate) { - mClockView.setY(oldTop + heightOffset); - mClockView.animate() - .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) - .setDuration(duration) - .setListener(new ClipChildrenAnimationListener()) - .setStartDelay(delay) - .y(top) - .scaleX(clockScale) - .scaleY(clockScale) - .withEndAction(() -> { - mClockView.setStyle(style); - mClockView.invalidate(); - }) - .start(); - } else { - mClockView.setY(top); - mClockView.setScaleX(clockScale); - mClockView.setScaleY(clockScale); - mClockView.setStyle(style); - mClockView.invalidate(); - } - } + mClockView.setKeyguardShowingHeader(mKeyguardSlice.hasHeader()); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - mClockView.setPivotX(mClockView.getWidth() / 2); - mClockView.setPivotY(0); - mLastLayoutHeight = getHeight(); layoutOwnerInfo(); } @Override public void onDensityOrFontScaleChanged() { - mSmallClockPadding = getResources() - .getDimensionPixelSize(R.dimen.widget_small_clock_padding); if (mClockView != null) { mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(R.dimen.widget_big_font_size)); - mClockView.getPaint().setStrokeWidth( - getResources().getDimensionPixelSize(R.dimen.widget_small_font_stroke)); } if (mOwnerInfo != null) { mOwnerInfo.setTextSize(TypedValue.COMPLEX_UNIT_PX, @@ -461,24 +395,4 @@ public class KeyguardStatusView extends GridLayout implements Log.e(TAG, "Failed to logout user", re); } } - - private class ClipChildrenAnimationListener extends AnimatorListenerAdapter implements - ViewClippingUtil.ClippingParameters { - - ClipChildrenAnimationListener() { - ViewClippingUtil.setClippingDeactivated(mClockView, true /* deactivated */, - this /* clippingParams */); - } - - @Override - public void onAnimationEnd(Animator animation) { - ViewClippingUtil.setClippingDeactivated(mClockView, false /* deactivated */, - this /* clippingParams */); - } - - @Override - public boolean shouldFinish(View view) { - return view == getParent(); - } - } } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index 95981427b642..078108d658ee 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -15,6 +15,7 @@ */ package com.android.keyguard.clock; +import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; @@ -25,16 +26,18 @@ import android.os.Looper; import android.provider.Settings; import android.view.LayoutInflater; +import androidx.annotation.VisibleForTesting; + import com.android.keyguard.R; +import com.android.systemui.dock.DockManager; +import com.android.systemui.dock.DockManager.DockEventListener; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.ExtensionController.Extension; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.function.Consumer; -import java.util.function.Supplier; import javax.inject.Inject; import javax.inject.Singleton; @@ -45,7 +48,6 @@ import javax.inject.Singleton; @Singleton public final class ClockManager { - private final LayoutInflater mLayoutInflater; private final ContentResolver mContentResolver; private final List<ClockInfo> mClockInfos = new ArrayList<>(); @@ -62,7 +64,6 @@ public final class ClockManager { } } }; - private final ExtensionController mExtensionController; /** * Used to select between plugin or default implementations of ClockPlugin interface. @@ -72,13 +73,35 @@ public final class ClockManager { * Consumer that accepts the a new ClockPlugin implementation when the Extension reloads. */ private final Consumer<ClockPlugin> mClockPluginConsumer = this::setClockPlugin; + /** + * Supplier of default ClockPlugin implementation. + */ + private final DefaultClockSupplier mDefaultClockSupplier; + /** + * Observe changes to dock state to know when to switch the clock face. + */ + private final DockEventListener mDockEventListener = + new DockEventListener() { + @Override + public void onEvent(int event) { + final boolean isDocked = (event == DockManager.STATE_DOCKED + || event == DockManager.STATE_DOCKED_HIDE); + mDefaultClockSupplier.setDocked(isDocked); + if (mClockExtension != null) { + mClockExtension.reload(); + } + } + }; + @Nullable + private final DockManager mDockManager; private final List<ClockChangedListener> mListeners = new ArrayList<>(); @Inject - public ClockManager(Context context, ExtensionController extensionController) { + public ClockManager(Context context, ExtensionController extensionController, + @Nullable DockManager dockManager) { mExtensionController = extensionController; - mLayoutInflater = LayoutInflater.from(context); + mDockManager = dockManager; mContentResolver = context.getContentResolver(); Resources res = context.getResources(); @@ -110,6 +133,9 @@ public final class ClockManager { .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.type_thumbnail)) .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.type_preview)) .build()); + + mDefaultClockSupplier = new DefaultClockSupplier(new SettingsWrapper(mContentResolver), + LayoutInflater.from(context)); } /** @@ -154,41 +180,32 @@ public final class ClockManager { mContentResolver.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE), false, mContentObserver); + mContentResolver.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE), + false, mContentObserver); + if (mDockManager != null) { + mDockManager.addListener(mDockEventListener); + } mClockExtension = mExtensionController.newExtension(ClockPlugin.class) .withPlugin(ClockPlugin.class) .withCallback(mClockPluginConsumer) - // Using withDefault even though this isn't the default as a workaround. - // ExtensionBuilder doesn't provide the ability to supply a ClockPlugin - // instance based off of the value of a setting. Since multiple "default" - // can be provided, using a supplier that changes the settings value. - // A null return will cause Extension#reload to look at the next "default" - // supplier. - .withDefault( - new SettingsGattedSupplier( - mContentResolver, - Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, - BubbleClockController.class.getName(), - () -> BubbleClockController.build(mLayoutInflater))) - .withDefault( - new SettingsGattedSupplier( - mContentResolver, - Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, - StretchAnalogClockController.class.getName(), - () -> StretchAnalogClockController.build(mLayoutInflater))) - .withDefault( - new SettingsGattedSupplier( - mContentResolver, - Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, - TypeClockController.class.getName(), - () -> TypeClockController.build(mLayoutInflater))) + .withDefault(mDefaultClockSupplier) .build(); } private void unregister() { mContentResolver.unregisterContentObserver(mContentObserver); + if (mDockManager != null) { + mDockManager.removeListener(mDockEventListener); + } mClockExtension.destroy(); } + @VisibleForTesting + boolean isDocked() { + return mDefaultClockSupplier.isDocked(); + } + /** * Listener for events that should cause the custom clock face to change. */ @@ -200,44 +217,4 @@ public final class ClockManager { */ void onClockChanged(ClockPlugin clock); } - - /** - * Supplier that only gets an instance when a settings value matches expected value. - */ - private static class SettingsGattedSupplier implements Supplier<ClockPlugin> { - - private final ContentResolver mContentResolver; - private final String mKey; - private final String mValue; - private final Supplier<ClockPlugin> mSupplier; - - /** - * Constructs a supplier that changes secure setting key against value. - * - * @param contentResolver Used to look up settings value. - * @param key Settings key. - * @param value If the setting matches this values that get supplies a ClockPlugin - * instance. - * @param supplier Supplier of ClockPlugin instance, only used if the setting - * matches value. - */ - SettingsGattedSupplier(ContentResolver contentResolver, String key, String value, - Supplier<ClockPlugin> supplier) { - mContentResolver = contentResolver; - mKey = key; - mValue = value; - mSupplier = supplier; - } - - /** - * Returns null if the settings value doesn't match the expected value. - * - * A null return causes Extension#reload to skip this supplier and move to the next. - */ - @Override - public ClockPlugin get() { - final String currentValue = Settings.Secure.getString(mContentResolver, mKey); - return Objects.equals(currentValue, mValue) ? mSupplier.get() : null; - } - } } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockSupplier.java b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockSupplier.java new file mode 100644 index 000000000000..7fdd2357bc8e --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockSupplier.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import android.util.ArrayMap; +import android.view.LayoutInflater; + +import com.android.systemui.plugins.ClockPlugin; + +import java.util.Map; +import java.util.function.Supplier; + +/** + * Supplier that only gets an instance when a settings value matches expected value. + */ +public class DefaultClockSupplier implements Supplier<ClockPlugin> { + + private final SettingsWrapper mSettingsWrapper; + /** + * Map from expected value stored in settings to supplier of custom clock face. + */ + private final Map<String, Supplier<ClockPlugin>> mClocks = new ArrayMap<>(); + /** + * When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face + * to show. + */ + private boolean mIsDocked; + + /** + * Constructs a supplier that changes secure setting key against value. + * + * @param settingsWrapper Wrapper around settings used to look up the custom clock face. + * @param layoutInflater Provided to clocks as dependency to inflate clock views. + */ + public DefaultClockSupplier(SettingsWrapper settingsWrapper, LayoutInflater layoutInflater) { + mSettingsWrapper = settingsWrapper; + + mClocks.put(BubbleClockController.class.getName(), + () -> BubbleClockController.build(layoutInflater)); + mClocks.put(StretchAnalogClockController.class.getName(), + () -> StretchAnalogClockController.build(layoutInflater)); + mClocks.put(TypeClockController.class.getName(), + () -> TypeClockController.build(layoutInflater)); + } + + /** + * Sets the dock state. + * + * @param isDocked True when docked, false otherwise. + */ + public void setDocked(boolean isDocked) { + mIsDocked = isDocked; + } + + boolean isDocked() { + return mIsDocked; + } + + /** + * Get the custom clock face based on values in settings. + * + * @return Custom clock face, null if the settings value doesn't match a custom clock. + */ + @Override + public ClockPlugin get() { + ClockPlugin plugin = null; + if (mIsDocked) { + final String name = mSettingsWrapper.getDockedClockFace(); + if (name != null) { + Supplier<ClockPlugin> supplier = mClocks.get(name); + if (supplier != null) { + plugin = supplier.get(); + if (plugin != null) { + return plugin; + } + } + } + } + final String name = mSettingsWrapper.getLockScreenCustomClockFace(); + if (name != null) { + Supplier<ClockPlugin> supplier = mClocks.get(name); + if (supplier != null) { + plugin = supplier.get(); + } + } + return plugin; + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java b/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java new file mode 100644 index 000000000000..58e11553af9d --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import android.content.ContentResolver; +import android.provider.Settings; + +/** + * Wrapper around Settings used for testing. + */ +public class SettingsWrapper { + + private static final String CUSTOM_CLOCK_FACE = Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE; + private static final String DOCKED_CLOCK_FACE = Settings.Secure.DOCKED_CLOCK_FACE; + + private ContentResolver mContentResolver; + + public SettingsWrapper(ContentResolver contentResolver) { + mContentResolver = contentResolver; + } + + /** + * Gets the value stored in settings for the custom clock face. + */ + public String getLockScreenCustomClockFace() { + return Settings.Secure.getString(mContentResolver, CUSTOM_CLOCK_FACE); + } + + /** + * Gets the value stored in settings for the clock face to use when docked. + */ + public String getDockedClockFace() { + return Settings.Secure.getString(mContentResolver, DOCKED_CLOCK_FACE); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java index ce9c637cebf6..3c6f081d0373 100644 --- a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java +++ b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java @@ -21,11 +21,13 @@ import com.android.systemui.appops.AppOpsControllerImpl; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.VolumeDialogController; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.PowerNotificationWarnings; import com.android.systemui.power.PowerUI; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QSTileHost; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl; import com.android.systemui.statusbar.phone.ManagedProfileController; import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl; @@ -194,6 +196,12 @@ public abstract class DependencyBinder { /** */ @Binds + public abstract StatusBarStateController provideStatusBarStateController( + StatusBarStateControllerImpl controllerImpl); + + /** + */ + @Binds public abstract StatusBarIconController provideStatusBarIconController( StatusBarIconControllerImpl controllerImpl); diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java index e28aa9d369cb..2a1d066d356e 100644 --- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java +++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java @@ -23,7 +23,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; -import android.content.res.Configuration; import android.provider.Settings; import android.util.AttributeSet; import android.view.Gravity; @@ -59,7 +58,6 @@ public class HardwareUiLayout extends MultiListLayout implements Tunable { private int mEndPoint; private boolean mEdgeBleed; private boolean mRoundedDivider; - private int mRotation = ROTATION_NONE; private boolean mRotatedBackground; private boolean mSwapOrientation = true; @@ -89,7 +87,7 @@ public class HardwareUiLayout extends MultiListLayout implements Tunable { } @Override - public ViewGroup getParentView(boolean separated, int index) { + public ViewGroup getParentView(boolean separated, int index, boolean reverse) { if (separated) { return getSeparatedView(); } else { @@ -174,7 +172,6 @@ public class HardwareUiLayout extends MultiListLayout implements Tunable { mSeparatedView.setBackground(mSeparatedViewBackground); updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding()); mOldHeight = mList.getMeasuredHeight(); - updateRotation(); } else { return; } @@ -188,25 +185,13 @@ public class HardwareUiLayout extends MultiListLayout implements Tunable { post(() -> updatePosition()); } - @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - updateRotation(); - } - public void setSwapOrientation(boolean swapOrientation) { mSwapOrientation = swapOrientation; } - private void updateRotation() { - int rotation = RotationUtils.getRotation(getContext()); - if (rotation != mRotation) { - rotate(mRotation, rotation); - mRotation = rotation; - } - } - - private void rotate(int from, int to) { + @Override + protected void rotate(int from, int to) { + super.rotate(from, to); if (from != ROTATION_NONE && to != ROTATION_NONE) { // Rather than handling this confusing case, just do 2 rotations. rotate(from, ROTATION_NONE); diff --git a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java index 85265f458370..00ff518ce212 100644 --- a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java +++ b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java @@ -17,11 +17,14 @@ package com.android.systemui; import android.content.Context; +import android.content.res.Configuration; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; +import com.android.systemui.util.leak.RotationUtils; + /** * Layout class representing the Global Actions menu which appears when the power button is held. */ @@ -32,8 +35,12 @@ public abstract class MultiListLayout extends LinearLayout { protected int mExpectedSeparatedItemCount; protected int mExpectedListItemCount; + protected int mRotation; + protected RotationListener mRotationListener; + public MultiListLayout(Context context, AttributeSet attrs) { super(context, attrs); + mRotation = RotationUtils.getRotation(context); } protected abstract ViewGroup getSeparatedView(); @@ -50,10 +57,12 @@ public abstract class MultiListLayout extends LinearLayout { * @param separated Whether or not this index refers to a position in the separated or list * container. * @param index The index of the item within the container. + * @param reverse If the MultiListLayout contains sub-lists within the list container, reverse + * the order that they are filled. * @return The parent ViewGroup which will be used to contain the specified item * after it has been added to the layout. */ - public abstract ViewGroup getParentView(boolean separated, int index); + public abstract ViewGroup getParentView(boolean separated, int index, boolean reverse); /** * Sets the divided view, which may have a differently-colored background. @@ -111,6 +120,26 @@ public abstract class MultiListLayout extends LinearLayout { setFocusable(true); } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + int newRotation = RotationUtils.getRotation(mContext); + if (newRotation != mRotation) { + rotate(mRotation, newRotation); + mRotation = newRotation; + } + } + + protected void rotate(int from, int to) { + if (mRotationListener != null) { + mRotationListener.onRotate(from, to); + } + } + + public void setRotationListener(RotationListener listener) { + mRotationListener = listener; + } + /** * Retrieve the MultiListLayout associated with the given view. */ @@ -121,4 +150,8 @@ public abstract class MultiListLayout extends LinearLayout { } return null; } + + interface RotationListener { + void onRotate(int from, int to); + } } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 755d6fcffb43..6d583df6082b 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -31,6 +31,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.assist.AssistManager; import com.android.systemui.classifier.FalsingManager; +import com.android.systemui.dock.DockManager; import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -41,7 +42,6 @@ import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; import com.android.systemui.statusbar.ScrimView; -import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotificationData; @@ -153,15 +153,6 @@ public class SystemUIFactory { return new VolumeDialogComponent(systemUi, context); } - /** - * Provides status bar state controller implementation - */ - @Singleton - @Provides - public StatusBarStateController provideStatusBarStateController(Context context) { - return new StatusBarStateControllerImpl(); - } - @Singleton @Provides public NotificationData.KeyguardEnvironment provideKeyguardEnvironment(Context context) { @@ -228,6 +219,16 @@ public class SystemUIFactory { return SysUiServiceProvider.getComponent(context, StatusBar.class); } + /** + * Provides DockManager. + */ + @Singleton + @Provides + @Nullable + public DockManager providesDockManager(Context context) { + return SysUiServiceProvider.getComponent(context, DockManager.class); + } + @Module protected static class ContextHolder { private Context mContext; diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index 038f4912e40f..83398cf6a88f 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -122,23 +122,9 @@ public class AssistManager implements ConfigurationChangedReceiver { } @Override - public void onTranscriptionUpdate(String transcription) { + public void onSetUiHints(Bundle hints) { if (VERBOSE) { - Log.v(TAG, "Transcription Updated: \"" + transcription + "\""); - } - } - - @Override - public void onTranscriptionComplete(boolean immediate) { - if (VERBOSE) { - Log.v(TAG, "Transcription complete (immediate=" + immediate + ")"); - } - } - - @Override - public void onVoiceStateChange(int state) { - if (VERBOSE) { - Log.v(TAG, "Voice state is now " + state); + Log.v(TAG, "UI hints received"); } } }); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java new file mode 100644 index 000000000000..4778434e2052 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.bubbles; + + +import android.view.LayoutInflater; + +import com.android.systemui.R; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + +/** + * Encapsulates the data and UI elements of a bubble. + */ +class Bubble { + + public BubbleView iconView; + public BubbleExpandedView expandedView; + public String key; + public NotificationEntry entry; + + Bubble(NotificationEntry e, LayoutInflater inflater, BubbleStackView stackView, + BubbleExpandedView.OnBubbleBlockedListener listener) { + entry = e; + key = entry.key; + + iconView = (BubbleView) inflater.inflate( + R.layout.bubble_view, stackView, false /* attachToRoot */); + iconView.setNotif(entry); + + expandedView = (BubbleExpandedView) inflater.inflate( + R.layout.bubble_expanded_view, stackView, false /* attachToRoot */); + expandedView.setEntry(entry, stackView); + + expandedView.setOnBlockedListener(listener); + } + + public void setEntry(NotificationEntry entry) { + key = entry.key; + iconView.update(entry); + // TODO: should also update the expanded view here (e.g. height change) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 41bc1b29075c..be2dd679bba5 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -16,10 +16,6 @@ package com.android.systemui.bubbles; -import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS; -import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING; -import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE; -import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; @@ -33,21 +29,14 @@ import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; import android.app.INotificationManager; import android.app.Notification; -import android.app.PendingIntent; import android.content.Context; -import android.content.pm.ActivityInfo; -import android.graphics.Point; import android.graphics.Rect; import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; import android.service.notification.StatusBarNotification; -import android.util.Log; -import android.util.StatsLog; import android.view.Display; -import android.view.LayoutInflater; import android.view.ViewGroup; -import android.view.WindowManager; import android.widget.FrameLayout; import androidx.annotation.MainThread; @@ -66,10 +55,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationInflater; import com.android.systemui.statusbar.phone.StatusBarWindowController; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; @@ -105,11 +91,9 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe private final BubbleTaskStackListener mTaskStackListener; private BubbleStateChangeListener mStateChangeListener; private BubbleExpandListener mExpandListener; - private LayoutInflater mInflater; - private final Map<String, BubbleView> mBubbles = new HashMap<>(); + private BubbleData mBubbleData; private BubbleStackView mStackView; - private final Point mDisplaySize; // Bubbles get added to the status bar view private final StatusBarWindowController mStatusBarWindowController; @@ -166,12 +150,9 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe } @Inject - public BubbleController(Context context, StatusBarWindowController statusBarWindowController) { + public BubbleController(Context context, StatusBarWindowController statusBarWindowController, + BubbleData data) { mContext = context; - WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - mDisplaySize = new Point(); - wm.getDefaultDisplay().getSize(mDisplaySize); - mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mNotificationEntryManager = Dependency.get(NotificationEntryManager.class); mNotificationEntryManager.addNotificationEntryListener(mEntryListener); @@ -190,6 +171,8 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe mActivityTaskManager = ActivityTaskManager.getService(); mTaskStackListener = new BubbleTaskStackListener(); ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); + + mBubbleData = data; } /** @@ -219,8 +202,11 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe * screen (e.g. if on AOD). */ public boolean hasBubbles() { - for (BubbleView bv : mBubbles.values()) { - if (!bv.getEntry().isBubbleDismissed()) { + if (mStackView == null) { + return false; + } + for (Bubble bubble : mBubbleData.getBubbles()) { + if (!bubble.entry.isBubbleDismissed()) { return true; } } @@ -250,10 +236,6 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe if (mStackView == null) { return; } - Set<String> keys = mBubbles.keySet(); - for (String key: keys) { - mBubbles.get(key).getEntry().setBubbleDismissed(true); - } mStackView.stackDismissed(); updateVisibility(); @@ -267,13 +249,12 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe * @param updatePosition whether this update should promote the bubble to the top of the stack. */ public void updateBubble(NotificationEntry notif, boolean updatePosition) { - if (mBubbles.containsKey(notif.key)) { + if (mStackView != null && mBubbleData.getBubble(notif.key) != null) { // It's an update - BubbleView bubble = mBubbles.get(notif.key); - mStackView.updateBubble(bubble, notif, updatePosition); + mStackView.updateBubble(notif, updatePosition); } else { if (mStackView == null) { - mStackView = new BubbleStackView(mContext); + mStackView = new BubbleStackView(mContext, mBubbleData); ViewGroup sbv = mStatusBarWindowController.getStatusBarView(); // XXX: Bug when you expand the shade on top of expanded bubble, there is no scrim // between bubble and the shade @@ -286,15 +267,7 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe mStackView.setOnBlockedListener(this); } // It's new - BubbleView bubble = (BubbleView) mInflater.inflate( - R.layout.bubble_view, mStackView, false /* attachToRoot */); - bubble.setNotif(notif); - PendingIntent bubbleIntent = getValidBubbleIntent(notif); - if (bubbleIntent != null) { - bubble.setBubbleIntent(bubbleIntent); - } - mBubbles.put(bubble.getKey(), bubble); - mStackView.addBubble(bubble); + mStackView.addBubble(notif); } updateVisibility(); } @@ -306,25 +279,18 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe */ @MainThread void removeBubble(String key) { - BubbleView bv = mBubbles.remove(key); - if (mStackView != null && bv != null) { - mStackView.removeBubble(bv); - bv.destroyActivityView(mStackView); - } - - NotificationEntry entry = bv != null ? bv.getEntry() : null; - if (entry != null) { - entry.setBubbleDismissed(true); - mNotificationEntryManager.updateNotifications(); + if (mStackView != null) { + mStackView.removeBubble(key); } + mNotificationEntryManager.updateNotifications(); updateVisibility(); } @Override public void onBubbleBlocked(NotificationEntry entry) { - Object[] bubbles = mBubbles.values().toArray(); + Object[] bubbles = mBubbleData.getBubbles().toArray(); for (int i = 0; i < bubbles.length; i++) { - NotificationEntry e = ((BubbleView) bubbles[i]).getEntry(); + NotificationEntry e = ((Bubble) bubbles[i]).entry; boolean samePackage = entry.notification.getPackageName().equals( e.notification.getPackageName()); if (samePackage) { @@ -368,10 +334,8 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe && alertAgain(entry, entry.notification.getNotification())) { entry.setShowInShadeWhenBubble(true); entry.setBubbleDismissed(false); // updates come back as bubbles even if dismissed - if (mBubbles.containsKey(entry.key)) { - mBubbles.get(entry.key).updateDotVisibility(); - } updateBubble(entry, true /* updatePosition */); + mStackView.updateDotVisibility(entry.key); } } @@ -383,8 +347,8 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe return; } entry.setShowInShadeWhenBubble(false); - if (mBubbles.containsKey(entry.key)) { - mBubbles.get(entry.key).updateDotVisibility(); + if (mStackView != null) { + mStackView.updateDotVisibility(entry.key); } if (!removedByUser) { // This was a cancel so we should remove the bubble @@ -441,65 +405,6 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe return mStackView; } - @Nullable - private PendingIntent getValidBubbleIntent(NotificationEntry notif) { - Notification notification = notif.notification.getNotification(); - String packageName = notif.notification.getPackageName(); - Notification.BubbleMetadata data = notif.getBubbleMetadata(); - if (data != null && canLaunchInActivityView(data.getIntent(), - true /* enable logging for bubbles */, packageName)) { - return data.getIntent(); - } - if (shouldUseContentIntent(mContext) - && canLaunchInActivityView(notification.contentIntent, - false /* disable logging for notifications */, packageName)) { - Log.d(TAG, "[addBubble " + notif.key - + "]: No appOverlayIntent, using contentIntent."); - return notification.contentIntent; - } - Log.d(TAG, "[addBubble " + notif.key + "]: No supported intent for ActivityView."); - return null; - } - - /** - * Whether an intent is properly configured to display in an {@link android.app.ActivityView}. - * - * @param intent the pending intent of the bubble. - * @param enableLogging whether bubble developer error should be logged. - * @param packageName the notification package name for this bubble. - * @return - */ - private boolean canLaunchInActivityView(PendingIntent intent, boolean enableLogging, - String packageName) { - if (intent == null) { - return false; - } - ActivityInfo info = - intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0); - if (info == null) { - if (enableLogging) { - StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName, - BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING); - } - return false; - } - if (!ActivityInfo.isResizeableMode(info.resizeMode)) { - if (enableLogging) { - StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName, - BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE); - } - return false; - } - if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) { - if (enableLogging) { - StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName, - BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS); - } - return false; - } - return (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0; - } - /** * Whether the notification has been developer configured to bubble and is allowed by the user. */ @@ -620,7 +525,7 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe ENABLE_AUTO_BUBBLE_ALL, 0) != 0; } - private static boolean shouldUseContentIntent(Context context) { + static boolean shouldUseContentIntent(Context context) { return Settings.Secure.getInt(context.getContentResolver(), ENABLE_BUBBLE_CONTENT_INTENT, 0) != 0; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java new file mode 100644 index 000000000000..5002f5cce751 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.bubbles; + +import androidx.annotation.Nullable; + +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + +import java.util.Collection; +import java.util.HashMap; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Keeps track of active bubbles. + */ +@Singleton +class BubbleData { + + private HashMap<String, Bubble> mBubbles = new HashMap<>(); + + @Inject + BubbleData() {} + + /** + * The set of bubbles. + */ + public Collection<Bubble> getBubbles() { + return mBubbles.values(); + } + + @Nullable + public Bubble getBubble(String key) { + return mBubbles.get(key); + } + + public void addBubble(Bubble b) { + mBubbles.put(b.key, b); + } + + @Nullable + public Bubble removeBubble(String key) { + return mBubbles.remove(key); + } + + public void updateBubble(String key, NotificationEntry newEntry) { + Bubble oldBubble = mBubbles.get(key); + if (oldBubble != null) { + oldBubble.setEntry(newEntry); + } + } + + public void clear() { + mBubbles.clear(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 976a766dcc09..492eadd240ed 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -16,19 +16,28 @@ package com.android.systemui.bubbles; +import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS; +import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING; +import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE; +import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS; + import android.animation.LayoutTransition; import android.animation.ObjectAnimator; import android.annotation.Nullable; +import android.app.ActivityView; import android.app.INotificationManager; import android.app.Notification; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; +import android.graphics.Insets; +import android.graphics.Point; import android.graphics.drawable.Drawable; import android.graphics.drawable.ShapeDrawable; import android.os.RemoteException; @@ -39,16 +48,21 @@ import android.util.AttributeSet; import android.util.Log; import android.util.StatsLog; import android.view.View; +import android.view.ViewGroup; +import android.view.WindowInsets; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.recents.TriangleShape; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.stack.ExpandableViewState; /** * Container for the expanded bubble view, handles rendering the caret and header of the view. @@ -68,8 +82,15 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList // Permission view private View mPermissionView; - // The view that is being displayed for the expanded state - private View mExpandedView; + // Views for expanded state + private ExpandableNotificationRow mNotifRow; + private ActivityView mActivityView; + + private boolean mActivityViewReady = false; + private PendingIntent mBubbleIntent; + + private int mBubbleHeight; + private int mDefaultHeight; private NotificationEntry mEntry; private PackageManager mPm; @@ -77,11 +98,40 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList private Drawable mAppIcon; private INotificationManager mNotificationManagerService; + private BubbleController mBubbleController = Dependency.get(BubbleController.class); - // Need reference to let it know to collapse when new task is launched private BubbleStackView mStackView; - private OnBubbleBlockedListener mOnBubbleBlockedListener; + private BubbleExpandedView.OnBubbleBlockedListener mOnBubbleBlockedListener; + + private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() { + @Override + public void onActivityViewReady(ActivityView view) { + if (!mActivityViewReady) { + mActivityViewReady = true; + mActivityView.startActivity(mBubbleIntent); + } + } + + @Override + public void onActivityViewDestroyed(ActivityView view) { + mActivityViewReady = false; + } + + /** + * This is only called for tasks on this ActivityView, which is also set to + * single-task mode -- meaning never more than one task on this display. If a task + * is being removed, it's the top Activity finishing and this bubble should + * be removed or collapsed. + */ + @Override + public void onTaskRemovalStarted(int taskId) { + if (mEntry != null) { + // Must post because this is called from a binder thread. + post(() -> mBubbleController.removeBubble(mEntry.key)); + } + } + }; public BubbleExpandedView(Context context) { this(context, null); @@ -99,6 +149,8 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); mPm = context.getPackageManager(); + mDefaultHeight = getResources().getDimensionPixelSize( + R.dimen.bubble_expanded_default_height); try { mNotificationManagerService = INotificationManager.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.NOTIFICATION_SERVICE)); @@ -152,6 +204,28 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList mPermissionView = findViewById(R.id.permission_layout); findViewById(R.id.no_bubbles_button).setOnClickListener(this); findViewById(R.id.yes_bubbles_button).setOnClickListener(this); + + mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */, + true /* singleTaskInstance */); + addView(mActivityView); + + mActivityView.setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> { + ActivityView activityView = (ActivityView) view; + // Here we assume that the position of the ActivityView on the screen + // remains regardless of IME status. When we move ActivityView, the + // forwardedInsets should be computed not against the current location + // and size, but against the post-moved location and size. + Point displaySize = new Point(); + view.getContext().getDisplay().getSize(displaySize); + int[] windowLocation = view.getLocationOnScreen(); + final int windowBottom = windowLocation[1] + view.getHeight(); + final int keyboardHeight = insets.getSystemWindowInsetBottom() + - insets.getStableInsetBottom(); + final int insetsBottom = Math.max(0, + windowBottom + keyboardHeight - displaySize.y); + activityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom)); + return view.onApplyWindowInsets(insets); + }); } /** @@ -189,6 +263,14 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList } updateHeaderView(); updatePermissionView(); + updateExpandedView(); + } + + /** + * Lets activity view know it should be shown / populated. + */ + public void populateActivityView() { + mActivityView.setCallback(mStateCallback); } private void updateHeaderView() { @@ -225,6 +307,34 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList } } + private void updateExpandedView() { + mBubbleIntent = getBubbleIntent(mEntry); + if (mBubbleIntent != null) { + if (mNotifRow != null) { + // Clear out the row if we had it previously + removeView(mNotifRow); + mNotifRow = null; + } + Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); + mBubbleHeight = data != null && data.getDesiredHeight() > 0 + ? data.getDesiredHeight() + : mDefaultHeight; + // XXX: enforce max / min height + LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams(); + lp.height = mBubbleHeight; + mActivityView.setLayoutParams(lp); + mActivityView.setVisibility(VISIBLE); + } else { + // Hide activity view if we had it previously + mActivityView.setVisibility(GONE); + + // Use notification view + mNotifRow = mEntry.getRow(); + addView(mNotifRow); + } + updateView(); + } + @Override public void onClick(View view) { if (mEntry == null) { @@ -279,36 +389,87 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList } /** + * Update appearance of the expanded view being displayed. + */ + public void updateView() { + if (usingActivityView() + && mActivityView.getVisibility() == VISIBLE + && mActivityView.isAttachedToWindow()) { + mActivityView.onLocationChanged(); + } else if (mNotifRow != null) { + applyRowState(mNotifRow); + } + } + + /** * Set the x position that the tip of the triangle should point to. */ - public void setPointerPosition(int x) { + public void setPointerPosition(float x) { // Adjust for the pointer size - x -= (mPointerView.getWidth() / 2); + x -= (mPointerView.getWidth() / 2f); mPointerView.setTranslationX(x); } /** - * Set the view to display for the expanded state. Passing null will clear the view. + * Removes and releases an ActivityView if one was previously created for this bubble. */ - public void setExpandedView(View view) { - if (mExpandedView == view) { + public void destroyActivityView(ViewGroup tmpParent) { + if (mActivityView == null) { return; } - if (mExpandedView != null) { - removeView(mExpandedView); + if (!mActivityViewReady) { + // release not needed, never initialized? + mActivityView = null; + return; } - mExpandedView = view; - if (mExpandedView != null) { - addView(mExpandedView); + // HACK: release() will crash if the view is not attached. + if (!isAttachedToWindow()) { + mActivityView.setVisibility(View.GONE); + tmpParent.addView(this); } + + mActivityView.release(); + ((ViewGroup) getParent()).removeView(this); + mActivityView = null; + mActivityViewReady = false; } - /** - * @return the view containing the expanded content, can be null. - */ - @Nullable - public View getExpandedView() { - return mExpandedView; + private boolean usingActivityView() { + return mBubbleIntent != null; + } + + private void applyRowState(ExpandableNotificationRow view) { + view.reset(); + view.setHeadsUp(false); + view.resetTranslation(); + view.setOnKeyguard(false); + view.setOnAmbient(false); + view.setClipBottomAmount(0); + view.setClipTopAmount(0); + view.setContentTransformationAmount(0, false); + view.setIconsVisible(true); + + // TODO - Need to reset this (and others) when view goes back in shade, leave for now + // view.setTopRoundness(1, false); + // view.setBottomRoundness(1, false); + + ExpandableViewState viewState = view.getViewState(); + viewState = viewState == null ? new ExpandableViewState() : viewState; + viewState.height = view.getIntrinsicHeight(); + viewState.gone = false; + viewState.hidden = false; + viewState.dimmed = false; + viewState.dark = false; + viewState.alpha = 1f; + viewState.notGoneIndex = -1; + viewState.xTranslation = 0; + viewState.yTranslation = 0; + viewState.zTranslation = 0; + viewState.scaleX = 1; + viewState.scaleY = 1; + viewState.inShelf = true; + viewState.headsUpIsVisible = false; + viewState.applyToView(view); } private Intent getSettingsIntent(String packageName, final int appUid) { @@ -320,6 +481,61 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList return intent; } + @Nullable + private PendingIntent getBubbleIntent(NotificationEntry entry) { + Notification notif = entry.notification.getNotification(); + String packageName = entry.notification.getPackageName(); + Notification.BubbleMetadata data = notif.getBubbleMetadata(); + if (data != null && canLaunchInActivityView(data.getIntent(), true /* enableLogging */, + packageName)) { + return data.getIntent(); + } else if (BubbleController.shouldUseContentIntent(mContext) + && canLaunchInActivityView(notif.contentIntent, false /* enableLogging */, + packageName)) { + return notif.contentIntent; + } + return null; + } + + /** + * Whether an intent is properly configured to display in an {@link android.app.ActivityView}. + * + * @param intent the pending intent of the bubble. + * @param enableLogging whether bubble developer error should be logged. + * @param packageName the notification package name for this bubble. + * @return + */ + private boolean canLaunchInActivityView(PendingIntent intent, boolean enableLogging, + String packageName) { + if (intent == null) { + return false; + } + ActivityInfo info = + intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0); + if (info == null) { + if (enableLogging) { + StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName, + BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING); + } + return false; + } + if (!ActivityInfo.isResizeableMode(info.resizeMode)) { + if (enableLogging) { + StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName, + BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE); + } + return false; + } + if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) { + if (enableLogging) { + StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName, + BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS); + } + return false; + } + return (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0; + } + /** * Listener that is notified when a bubble is blocked. */ diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 2b344f6cf195..62cc8895ac73 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -19,7 +19,6 @@ package com.android.systemui.bubbles; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import android.app.ActivityView; import android.content.Context; import android.content.res.Resources; import android.graphics.Outline; @@ -33,13 +32,11 @@ import android.util.StatsLog; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; import android.view.ViewOutlineProvider; import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.WindowManager; import android.widget.FrameLayout; -import android.widget.LinearLayout; import androidx.annotation.MainThread; import androidx.annotation.Nullable; @@ -54,8 +51,6 @@ import com.android.systemui.bubbles.animation.ExpandedAnimationController; import com.android.systemui.bubbles.animation.PhysicsAnimationLayout; import com.android.systemui.bubbles.animation.StackAnimationController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.notification.stack.ExpandableViewState; import java.math.BigDecimal; import java.math.RoundingMode; @@ -90,27 +85,33 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F private final SpringAnimation mExpandedViewXAnim; private final SpringAnimation mExpandedViewYAnim; + private final BubbleData mBubbleData; private PhysicsAnimationLayout mBubbleContainer; private StackAnimationController mStackAnimationController; private ExpandedAnimationController mExpandedAnimationController; - private BubbleExpandedView mExpandedViewContainer; + private FrameLayout mExpandedViewContainer; + private int mBubbleSize; private int mBubblePadding; private int mExpandedAnimateXDistance; private int mExpandedAnimateYDistance; + private int mStatusBarHeight; + private Bubble mExpandedBubble; private boolean mIsExpanded; - private int mExpandedBubbleHeight; + private BubbleTouchHandler mTouchHandler; - private BubbleView mExpandedBubble; private BubbleController.BubbleExpandListener mExpandListener; + private BubbleExpandedView.OnBubbleBlockedListener mBlockedListener; private boolean mViewUpdatedRequested = false; private boolean mIsAnimating = false; + private LayoutInflater mInflater; + // Used for determining view / touch intersection int[] mTempLoc = new int[2]; RectF mTempRect = new RectF(); @@ -140,9 +141,11 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F } }; - public BubbleStackView(Context context) { + public BubbleStackView(Context context, BubbleData data) { super(context); + mBubbleData = data; + mInflater = LayoutInflater.from(context); mTouchHandler = new BubbleTouchHandler(context); setOnTouchListener(mTouchHandler); @@ -153,8 +156,9 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_x_distance); mExpandedAnimateYDistance = res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_y_distance); + mStatusBarHeight = + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); - mExpandedBubbleHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height); mDisplaySize = new Point(); WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); wm.getDefaultDisplay().getSize(mDisplaySize); @@ -174,9 +178,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F mBubbleContainer.setClipChildren(false); addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); - mExpandedViewContainer = (BubbleExpandedView) - LayoutInflater.from(context).inflate(R.layout.bubble_expanded_view, - this /* parent */, false /* attachToRoot */); + mExpandedViewContainer = new FrameLayout(context); mExpandedViewContainer.setElevation(elevation); mExpandedViewContainer.setPadding(padding, padding, padding, padding); mExpandedViewContainer.setClipChildren(false); @@ -218,6 +220,17 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F } /** + * Updates the visibility of the 'dot' indicating an update on the bubble. + * @param key the {@link NotificationEntry#key} associated with the bubble. + */ + public void updateDotVisibility(String key) { + Bubble b = mBubbleData.getBubble(key); + if (b != null) { + b.iconView.updateDotVisibility(); + } + } + + /** * Sets the listener to notify when the bubble stack is expanded. */ public void setExpandListener(BubbleController.BubbleExpandListener listener) { @@ -228,7 +241,10 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F * Sets the listener to notify when a bubble is blocked. */ public void setOnBlockedListener(BubbleExpandedView.OnBubbleBlockedListener listener) { - mExpandedViewContainer.setOnBlockedListener(listener); + mBlockedListener = listener; + for (Bubble b : mBubbleData.getBubbles()) { + b.expandedView.setOnBlockedListener(mBlockedListener); + } } /** @@ -241,19 +257,29 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F /** * The {@link BubbleView} that is expanded, null if one does not exist. */ - public BubbleView getExpandedBubble() { + BubbleView getExpandedBubbleView() { + return mExpandedBubble != null ? mExpandedBubble.iconView : null; + } + + /** + * The {@link Bubble} that is expanded, null if one does not exist. + */ + Bubble getExpandedBubble() { return mExpandedBubble; } /** * Sets the bubble that should be expanded and expands if needed. + * + * @param key the {@link NotificationEntry#key} associated with the bubble to expand. */ - public void setExpandedBubble(BubbleView bubbleToExpand) { + void setExpandedBubble(String key) { + Bubble bubbleToExpand = mBubbleData.getBubble(key); if (mIsExpanded && !bubbleToExpand.equals(mExpandedBubble)) { // Previously expanded, notify that this bubble is no longer expanded - notifyExpansionChanged(mExpandedBubble, false /* expanded */); + notifyExpansionChanged(mExpandedBubble.entry, false /* expanded */); } - BubbleView prevBubble = mExpandedBubble; + Bubble prevBubble = mExpandedBubble; mExpandedBubble = bubbleToExpand; if (!mIsExpanded) { // If we weren't previously expanded we should animate open. @@ -268,8 +294,8 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F logBubbleEvent(prevBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED); logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED); } - mExpandedBubble.getEntry().setShowInShadeWhenBubble(false); - notifyExpansionChanged(mExpandedBubble, true /* expanded */); + mExpandedBubble.entry.setShowInShadeWhenBubble(false); + notifyExpansionChanged(mExpandedBubble.entry, true /* expanded */); } /** @@ -280,7 +306,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F for (int i = 0; i < mBubbleContainer.getChildCount(); i++) { BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i); if (entry.equals(bv.getEntry())) { - setExpandedBubble(bv); + setExpandedBubble(entry.key); } } } @@ -288,48 +314,67 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F /** * Adds a bubble to the top of the stack. * - * @param bubbleView the view to add to the stack. + * @param entry the notification to add to the stack of bubbles. */ - public void addBubble(BubbleView bubbleView) { - mBubbleContainer.addView(bubbleView, 0, + public void addBubble(NotificationEntry entry) { + Bubble b = new Bubble(entry, mInflater, this /* stackView */, mBlockedListener); + mBubbleData.addBubble(b); + + mBubbleContainer.addView(b.iconView, 0, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); - ViewClippingUtil.setClippingDeactivated(bubbleView, true, mClippingParameters); + ViewClippingUtil.setClippingDeactivated(b.iconView, true, mClippingParameters); + requestUpdate(); - logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED); + logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED); } /** * Remove a bubble from the stack. */ - public void removeBubble(BubbleView bubbleView) { - int removedIndex = mBubbleContainer.indexOfChild(bubbleView); - mBubbleContainer.removeView(bubbleView); + public void removeBubble(String key) { + Bubble b = mBubbleData.removeBubble(key); + if (b == null) { + return; + } + b.entry.setBubbleDismissed(true); + + // Remove it from the views + int removedIndex = mBubbleContainer.indexOfChild(b.iconView); + b.expandedView.destroyActivityView(this /* tmpParent */); + mBubbleContainer.removeView(b.iconView); + int bubbleCount = mBubbleContainer.getChildCount(); if (bubbleCount == 0) { // If no bubbles remain, collapse the entire stack. collapseStack(); return; - } else if (bubbleView.equals(mExpandedBubble)) { + } else if (b.equals(mExpandedBubble)) { // Was the current bubble just removed? // If we have other bubbles and are expanded go to the next one or previous // if the bubble removed was last int nextIndex = bubbleCount > removedIndex ? removedIndex : bubbleCount - 1; BubbleView expandedBubble = (BubbleView) mBubbleContainer.getChildAt(nextIndex); if (mIsExpanded) { - setExpandedBubble(expandedBubble); + setExpandedBubble(expandedBubble.getKey()); } else { mExpandedBubble = null; } } - logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED); + logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED); } /** * Dismiss the stack of bubbles. */ public void stackDismissed() { + for (Bubble bubble : mBubbleData.getBubbles()) { + bubble.entry.setBubbleDismissed(true); + bubble.expandedView.destroyActivityView(this /* tmpParent */); + } + mBubbleData.clear(); collapseStack(); mBubbleContainer.removeAllViews(); + mExpandedViewContainer.removeAllViews(); logBubbleEvent(null /* no bubble associated with bubble stack dismiss */, StatsLog.BUBBLE_UICHANGED__ACTION__STACK_DISMISSED); } @@ -337,25 +382,26 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F /** * Updates a bubble in the stack. * - * @param bubbleView the view to update in the stack. - * @param entry the entry to update it with. + * @param entry the entry to update in the stack. * @param updatePosition whether this bubble should be moved to top of the stack. */ - public void updateBubble(BubbleView bubbleView, NotificationEntry entry, - boolean updatePosition) { - bubbleView.update(entry); + public void updateBubble(NotificationEntry entry, boolean updatePosition) { + Bubble b = mBubbleData.getBubble(entry.key); + b.iconView.update(entry); + // TODO: should also update the expanded view here (e.g. height change) + if (updatePosition && !mIsExpanded) { // If alerting it gets promoted to top of the stack. - if (mBubbleContainer.indexOfChild(bubbleView) != 0) { - mBubbleContainer.moveViewTo(bubbleView, 0); + if (mBubbleContainer.indexOfChild(b.iconView) != 0) { + mBubbleContainer.moveViewTo(b.iconView, 0); } requestUpdate(); } - if (mIsExpanded && bubbleView.equals(mExpandedBubble)) { + if (mIsExpanded && entry.equals(mExpandedBubble.entry)) { entry.setShowInShadeWhenBubble(false); requestUpdate(); } - logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED); + logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED); } /** @@ -393,7 +439,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F if (mIsExpanded) { // TODO: Save opened bubble & move it to top of stack animateExpansion(false /* shouldExpand */); - notifyExpansionChanged(mExpandedBubble, mIsExpanded); + notifyExpansionChanged(mExpandedBubble.entry, mIsExpanded); logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED); } } @@ -412,8 +458,8 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F @MainThread public void expandStack() { if (!mIsExpanded) { - mExpandedBubble = getTopBubble(); - setExpandedBubble(mExpandedBubble); + String expandedBubbleKey = getBubbleAt(0).getKey(); + setExpandedBubble(expandedBubbleKey); logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED); } } @@ -459,7 +505,8 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F final float yStart = Math.min( mStackAnimationController.getStackPosition().y, mExpandedAnimateYDistance); - final float yDest = getStatusBarHeight() + mExpandedBubble.getHeight() + mBubblePadding; + final float yDest = getStatusBarHeight() + + mExpandedBubble.iconView.getHeight() + mBubblePadding; if (shouldExpand) { mExpandedViewContainer.setTranslationX(xStart); @@ -484,17 +531,12 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F + mBubbleContainer.getPaddingStart(); } - private void notifyExpansionChanged(BubbleView bubbleView, boolean expanded) { + private void notifyExpansionChanged(NotificationEntry entry, boolean expanded) { if (mExpandListener != null) { - NotificationEntry entry = bubbleView != null ? bubbleView.getEntry() : null; mExpandListener.onBubbleExpandChanged(expanded, entry != null ? entry.key : null); } } - private BubbleView getTopBubble() { - return getBubbleAt(0); - } - /** Return the BubbleView at the given index from the bubble container. */ public BubbleView getBubbleAt(int i) { return mBubbleContainer.getChildCount() > i @@ -639,7 +681,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F if (getRootWindowInsets() != null) { WindowInsets insets = getRootWindowInsets(); return Math.max( - insets.getSystemWindowInsetTop(), + mStatusBarHeight, insets.getDisplayCutout() != null ? insets.getDisplayCutout().getSafeInsetTop() : 0); @@ -665,31 +707,11 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F } private void updateExpandedBubble() { - if (mExpandedBubble == null) { - return; - } - - mExpandedViewContainer.setEntry(mExpandedBubble.getEntry(), this); - if (mExpandedBubble.hasAppOverlayIntent()) { - // Bubble with activity view expanded state - ActivityView expandedView = mExpandedBubble.getActivityView(); - // XXX: gets added to linear layout - expandedView.setLayoutParams(new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, mExpandedBubbleHeight)); - - mExpandedViewContainer.setExpandedView(expandedView); - } else { - // Bubble with notification view expanded state - ExpandableNotificationRow row = mExpandedBubble.getRowView(); - if (row.getParent() != null) { - // Row might still be in the shade when we expand - ((ViewGroup) row.getParent()).removeView(row); - } - if (mIsExpanded) { - mExpandedViewContainer.setExpandedView(row); - } else { - mExpandedViewContainer.setExpandedView(null); - } + mExpandedViewContainer.removeAllViews(); + if (mExpandedBubble != null && mIsExpanded) { + mExpandedViewContainer.addView(mExpandedBubble.expandedView); + mExpandedBubble.expandedView.populateActivityView(); + mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE); } } @@ -697,18 +719,10 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded); mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE); - if (!mIsExpanded) { - mExpandedViewContainer.setExpandedView(null); - } else { - View expandedView = mExpandedViewContainer.getExpandedView(); - if (expandedView instanceof ActivityView) { - if (expandedView.isAttachedToWindow()) { - ((ActivityView) expandedView).onLocationChanged(); - } - } else { - applyRowState(mExpandedBubble.getRowView()); - } + if (mIsExpanded) { + mExpandedBubble.expandedView.updateView(); } + int bubbsCount = mBubbleContainer.getChildCount(); for (int i = 0; i < bubbsCount; i++) { BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i); @@ -730,46 +744,12 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F private void updatePointerPosition() { if (mExpandedBubble != null) { - float pointerPosition = mExpandedBubble.getPosition().x - + (mExpandedBubble.getWidth() / 2f); - mExpandedViewContainer.setPointerPosition((int) pointerPosition); + float pointerPosition = mExpandedBubble.iconView.getPosition().x + + (mExpandedBubble.iconView.getWidth() / 2f); + mExpandedBubble.expandedView.setPointerPosition(pointerPosition); } } - private void applyRowState(ExpandableNotificationRow view) { - view.reset(); - view.setHeadsUp(false); - view.resetTranslation(); - view.setOnKeyguard(false); - view.setOnAmbient(false); - view.setClipBottomAmount(0); - view.setClipTopAmount(0); - view.setContentTransformationAmount(0, false); - view.setIconsVisible(true); - - // TODO - Need to reset this (and others) when view goes back in shade, leave for now - // view.setTopRoundness(1, false); - // view.setBottomRoundness(1, false); - - ExpandableViewState viewState = view.getViewState(); - viewState = viewState == null ? new ExpandableViewState() : viewState; - viewState.height = view.getIntrinsicHeight(); - viewState.gone = false; - viewState.hidden = false; - viewState.dimmed = false; - viewState.dark = false; - viewState.alpha = 1f; - viewState.notGoneIndex = -1; - viewState.xTranslation = 0; - viewState.yTranslation = 0; - viewState.zTranslation = 0; - viewState.scaleX = 1; - viewState.scaleY = 1; - viewState.inShelf = true; - viewState.headsUpIsVisible = false; - viewState.applyToView(view); - } - /** * @return the number of bubbles in the stack view. */ @@ -780,12 +760,12 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F /** * Finds the bubble index within the stack. * - * @param bubbleView the view of the bubble. + * @param bubble the bubble to look up. * @return the index of the bubble view within the bubble stack. The range of the position * is between 0 and the bubble count minus 1. */ - public int getBubbleIndex(BubbleView bubbleView) { - return mBubbleContainer.indexOfChild(bubbleView); + int getBubbleIndex(Bubble bubble) { + return mBubbleContainer.indexOfChild(bubble.iconView); } /** @@ -813,7 +793,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F * the user interaction is not specific to one bubble. * @param action the user interaction enum. */ - private void logBubbleEvent(@Nullable BubbleView bubble, int action) { + private void logBubbleEvent(@Nullable Bubble bubble, int action) { if (bubble == null) { StatsLog.write(StatsLog.BUBBLE_UI_CHANGED, null /* package name */, @@ -825,7 +805,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F getNormalizedXPosition(), getNormalizedYPosition()); } else { - StatusBarNotification notification = bubble.getEntry().notification; + StatusBarNotification notification = bubble.entry.notification; StatsLog.write(StatsLog.BUBBLE_UI_CHANGED, notification.getPackageName(), notification.getNotification().getChannelId(), diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java index 22cd2fcc3e72..7d3c0f85c0f7 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java @@ -188,7 +188,7 @@ class BubbleTouchHandler implements View.OnTouchListener { } else { stack.onBubbleDragFinish(mBubbleDraggingOut, x, y, velX, velY); } - } else if (floatingView.equals(stack.getExpandedBubble())) { + } else if (floatingView.equals(stack.getExpandedBubbleView())) { stack.collapseStack(); } else if (isBubbleStack) { if (stack.isExpanded()) { @@ -197,7 +197,7 @@ class BubbleTouchHandler implements View.OnTouchListener { stack.expandStack(); } } else { - stack.setExpandedBubble((BubbleView) floatingView); + stack.setExpandedBubble(((BubbleView) floatingView).getKey()); } cleanUpDismissTarget(); mVelocityTracker.recycle(); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java index 74ddc8f6b8fc..1a4b19940e34 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java @@ -17,13 +17,9 @@ package com.android.systemui.bubbles; import android.annotation.Nullable; -import android.app.ActivityView; import android.app.Notification; -import android.app.PendingIntent; import android.content.Context; import android.graphics.Color; -import android.graphics.Insets; -import android.graphics.Point; import android.graphics.PointF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -31,16 +27,10 @@ import android.graphics.drawable.Icon; import android.graphics.drawable.InsetDrawable; import android.graphics.drawable.LayerDrawable; import android.util.AttributeSet; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowInsets; import android.widget.FrameLayout; -import android.widget.LinearLayout; import android.widget.TextView; import com.android.internal.graphics.ColorUtils; -import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -62,11 +52,6 @@ public class BubbleView extends FrameLayout implements BubbleTouchHandler.Floati private int mIconInset; private NotificationEntry mEntry; - private PendingIntent mAppOverlayIntent; - private BubbleController mBubbleController; - private ActivityView mActivityView; - private boolean mActivityViewReady; - private boolean mActivityViewStarted; public BubbleView(Context context) { this(context, null); @@ -86,7 +71,6 @@ public class BubbleView extends FrameLayout implements BubbleTouchHandler.Floati // XXX: can this padding just be on the view and we look it up? mPadding = getResources().getDimensionPixelSize(R.dimen.bubble_view_padding); mIconInset = getResources().getDimensionPixelSize(R.dimen.bubble_icon_inset); - mBubbleController = Dependency.get(BubbleController.class); } @Override @@ -168,7 +152,6 @@ public class BubbleView extends FrameLayout implements BubbleTouchHandler.Floati updateViews(); } - /** * @return the {@link ExpandableNotificationRow} view to display notification content when the * bubble is expanded. @@ -235,88 +218,6 @@ public class BubbleView extends FrameLayout implements BubbleTouchHandler.Floati return ColorUtils.blendARGB(defaultTint, Color.WHITE, WHITE_SCRIM_ALPHA); } - /** - * @return a view used to display app overlay content when expanded. - */ - public ActivityView getActivityView() { - if (mActivityView == null) { - mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */, - true /* singleTaskInstance */); - Log.d(TAG, "[getActivityView] created: " + mActivityView); - mActivityView.setCallback(new ActivityView.StateCallback() { - @Override - public void onActivityViewReady(ActivityView view) { - mActivityViewReady = true; - mActivityView.startActivity(mAppOverlayIntent); - } - - @Override - public void onActivityViewDestroyed(ActivityView view) { - mActivityViewReady = false; - } - - /** - * This is only called for tasks on this ActivityView, which is also set to - * single-task mode -- meaning never more than one task on this display. If a task - * is being removed, it's the top Activity finishing and this bubble should - * be removed or collapsed. - */ - @Override - public void onTaskRemovalStarted(int taskId) { - if (mEntry != null) { - // Must post because this is called from a binder thread. - post(() -> mBubbleController.removeBubble(mEntry.key)); - } - } - }); - mActivityView.setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> { - ActivityView activityView = (ActivityView) view; - // Here we assume that the position of the ActivityView on the screen - // remains regardless of IME status. When we move ActivityView, the - // forwardedInsets should be computed not against the current location - // and size, but against the post-moved location and size. - Point displaySize = new Point(); - view.getContext().getDisplay().getSize(displaySize); - int[] windowLocation = view.getLocationOnScreen(); - final int windowBottom = windowLocation[1] + view.getHeight(); - final int keyboardHeight = insets.getSystemWindowInsetBottom() - - insets.getStableInsetBottom(); - final int insetsBottom = Math.max(0, - windowBottom + keyboardHeight - displaySize.y); - activityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom)); - return view.onApplyWindowInsets(insets); - }); - - } - return mActivityView; - } - - /** - * Removes and releases an ActivityView if one was previously created for this bubble. - */ - public void destroyActivityView(ViewGroup tmpParent) { - if (mActivityView == null) { - return; - } - if (!mActivityViewReady) { - // release not needed, never initialized? - mActivityView = null; - return; - } - // HACK: release() will crash if the view is not attached. - if (!mActivityView.isAttachedToWindow()) { - mActivityView.setVisibility(View.GONE); - tmpParent.addView(mActivityView, new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - } - - mActivityView.release(); - - ((ViewGroup) mActivityView.getParent()).removeView(mActivityView); - mActivityView = null; - } - @Override public void setPosition(float x, float y) { setPositionX(x); @@ -337,20 +238,4 @@ public class BubbleView extends FrameLayout implements BubbleTouchHandler.Floati public PointF getPosition() { return new PointF(getTranslationX(), getTranslationY()); } - - /** - * @return whether an ActivityView should be used to display the content of this Bubble - */ - public boolean hasAppOverlayIntent() { - return mAppOverlayIntent != null; - } - - public PendingIntent getAppOverlayIntent() { - return mAppOverlayIntent; - - } - - public void setBubbleIntent(PendingIntent intent) { - mAppOverlayIntent = intent; - } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java index 164406494250..5b158e906356 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java @@ -16,6 +16,7 @@ package com.android.systemui.bubbles.animation; +import android.content.res.Resources; import android.graphics.PointF; import android.view.View; import android.view.WindowInsets; @@ -55,16 +56,19 @@ public class ExpandedAnimationController private float mBubblePaddingPx; /** Size of each bubble. */ private float mBubbleSizePx; + /** Height of the status bar. */ + private float mStatusBarHeight; @Override protected void setLayout(PhysicsAnimationLayout layout) { super.setLayout(layout); - mStackOffsetPx = layout.getResources().getDimensionPixelSize( - R.dimen.bubble_stack_offset); - mBubblePaddingPx = layout.getResources().getDimensionPixelSize( - R.dimen.bubble_padding); - mBubbleSizePx = layout.getResources().getDimensionPixelSize( - R.dimen.individual_bubble_size); + + final Resources res = layout.getResources(); + mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); + mBubblePaddingPx = res.getDimensionPixelSize(R.dimen.bubble_padding); + mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size); + mStatusBarHeight = + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); } /** @@ -103,7 +107,7 @@ public class ExpandedAnimationController final WindowInsets insets = mLayout.getRootWindowInsets(); if (insets != null) { return mBubblePaddingPx + Math.max( - insets.getSystemWindowInsetTop(), + mStatusBarHeight, insets.getDisplayCutout() != null ? insets.getDisplayCutout().getSafeInsetTop() : 0); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java index 0c089a75aece..7dfb21cf384f 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java @@ -16,15 +16,12 @@ package com.android.systemui.bubbles.animation; -import android.content.Context; import android.content.res.Resources; -import android.graphics.Point; import android.graphics.PointF; import android.graphics.RectF; import android.util.Log; import android.view.View; import android.view.WindowInsets; -import android.view.WindowManager; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.FlingAnimation; @@ -88,9 +85,8 @@ public class StackAnimationController extends private int mBubbleOffscreen; /** How far down the screen the stack starts, when there is no pre-existing location. */ private int mStackStartingVerticalOffset; - - private Point mDisplaySize; - private RectF mAllowableStackPositionRegion; + /** Height of the status bar. */ + private float mStatusBarHeight; @Override protected void setLayout(PhysicsAnimationLayout layout) { @@ -103,11 +99,8 @@ public class StackAnimationController extends mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen); mStackStartingVerticalOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_starting_offset_y); - - mDisplaySize = new Point(); - WindowManager wm = - (WindowManager) layout.getContext().getSystemService(Context.WINDOW_SERVICE); - wm.getDefaultDisplay().getSize(mDisplaySize); + mStatusBarHeight = + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); } /** @@ -203,10 +196,9 @@ public class StackAnimationController extends */ public RectF getAllowableStackPositionRegion() { final WindowInsets insets = mLayout.getRootWindowInsets(); - mAllowableStackPositionRegion = new RectF(); - + final RectF allowableRegion = new RectF(); if (insets != null) { - mAllowableStackPositionRegion.left = + allowableRegion.left = -mBubbleOffscreen - mBubblePadding + Math.max( @@ -214,7 +206,7 @@ public class StackAnimationController extends insets.getDisplayCutout() != null ? insets.getDisplayCutout().getSafeInsetLeft() : 0); - mAllowableStackPositionRegion.right = + allowableRegion.right = mLayout.getWidth() - mIndividualBubbleSize + mBubbleOffscreen @@ -222,17 +214,17 @@ public class StackAnimationController extends - Math.max( insets.getSystemWindowInsetRight(), insets.getDisplayCutout() != null - ? insets.getDisplayCutout().getSafeInsetRight() - : 0); + ? insets.getDisplayCutout().getSafeInsetRight() + : 0); - mAllowableStackPositionRegion.top = + allowableRegion.top = mBubblePadding + Math.max( - insets.getSystemWindowInsetTop(), + mStatusBarHeight, insets.getDisplayCutout() != null - ? insets.getDisplayCutout().getSafeInsetTop() - : 0); - mAllowableStackPositionRegion.bottom = + ? insets.getDisplayCutout().getSafeInsetTop() + : 0); + allowableRegion.bottom = mLayout.getHeight() - mIndividualBubbleSize - mBubblePadding @@ -243,7 +235,7 @@ public class StackAnimationController extends : 0); } - return mAllowableStackPositionRegion; + return allowableRegion; } @Override @@ -287,31 +279,14 @@ public class StackAnimationController extends @Override void onChildAdded(View child, int index) { - // If this is the first child added, position the stack in its starting position. if (mLayout.getChildCount() == 1) { - moveStackToStartPosition(); - } - - if (mLayout.indexOfChild(child) == 0) { - child.setTranslationY(mStackPosition.y); - - // Pop in the new bubble. - child.setScaleX(ANIMATE_IN_STARTING_SCALE); - child.setScaleY(ANIMATE_IN_STARTING_SCALE); - mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_X, 0, 1f); - mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_Y, 0, 1f); - - // Fade in the new bubble. - child.setAlpha(0); - mLayout.animateValueForChildAtIndex(DynamicAnimation.ALPHA, 0, 1f); - - // Start the new bubble 4x the normal offset distance in the opposite direction. We'll - // animate in from this position. Since the animations are chained, when the new bubble - // flies in from the side, it will push the other ones out of the way. - float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X); - child.setTranslationX(mStackPosition.x - ANIMATE_TRANSLATION_FACTOR * xOffset); - mLayout.animateValueForChildAtIndex( - DynamicAnimation.TRANSLATION_X, 0, mStackPosition.x); + // If this is the first child added, position the stack in its starting position before + // animating in. + moveStackToStartPosition(() -> animateInBubble(child)); + } else if (mLayout.indexOfChild(child) == 0) { + // Otherwise, animate the bubble in if it's the newest bubble. If we're adding a bubble + // to the back of the stack, it'll be largely invisible so don't bother animating it in. + animateInBubble(child); } } @@ -334,10 +309,14 @@ public class StackAnimationController extends } /** Moves the stack, without any animation, to the starting position. */ - private void moveStackToStartPosition() { - mLayout.post(() -> setStackPosition( - getAllowableStackPositionRegion().right, - getAllowableStackPositionRegion().top + mStackStartingVerticalOffset)); + private void moveStackToStartPosition(Runnable after) { + // Post to ensure that the layout's width and height have been calculated. + mLayout.post(() -> { + setStackPosition( + getAllowableStackPositionRegion().right, + getAllowableStackPositionRegion().top + mStackStartingVerticalOffset); + after.run(); + }); } /** @@ -379,6 +358,29 @@ public class StackAnimationController extends } } + /** Animates in the given bubble. */ + private void animateInBubble(View child) { + child.setTranslationY(mStackPosition.y); + + // Pop in the new bubble. + child.setScaleX(ANIMATE_IN_STARTING_SCALE); + child.setScaleY(ANIMATE_IN_STARTING_SCALE); + mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_X, 0, 1f); + mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_Y, 0, 1f); + + // Fade in the new bubble. + child.setAlpha(0); + mLayout.animateValueForChildAtIndex(DynamicAnimation.ALPHA, 0, 1f); + + // Start the new bubble 4x the normal offset distance in the opposite direction. We'll + // animate in from this position. Since the animations are chained, when the new bubble + // flies in from the side, it will push the other ones out of the way. + float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X); + child.setTranslationX(mStackPosition.x - ANIMATE_TRANSLATION_FACTOR * xOffset); + mLayout.animateValueForChildAtIndex( + DynamicAnimation.TRANSLATION_X, 0, mStackPosition.x); + } + /** * Springs the first bubble to the given final position, with the rest of the stack 'following'. */ diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java b/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java index 4a2e06c058cb..f5cf149ba868 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java @@ -25,9 +25,4 @@ public interface DozeReceiver { * Invoked every time a minute is elapsed in doze mode */ void dozeTimeTick(); - - /** - * When view is double tapped in doze mode. - */ - void onDozeDoubleTap(); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 5efdc2f61d76..2d1dba6f79c8 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -131,7 +131,7 @@ public class DozeSensors { new PluginSensor( new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN), Settings.Secure.DOZE_WAKE_SCREEN_GESTURE, - mConfig.wakeScreenGestureAvailable(), + mConfig.wakeScreenGestureAvailable() && alwaysOn, DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, false /* reports touch coordinates */, false /* touchscreen */), diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 873fbc226629..e207cb13d311 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -161,7 +161,8 @@ public class DozeTriggers implements DozeMachine.Part { } else { mDozeHost.extendPulse(); } - }, sensorPerformedProxCheck || mDockManager.isDocked(), pulseReason); + }, sensorPerformedProxCheck + || (mDockManager != null && mDockManager.isDocked()), pulseReason); } if (isPickup) { @@ -224,7 +225,9 @@ public class DozeTriggers implements DozeMachine.Part { case INITIALIZED: mBroadcastReceiver.register(mContext); mDozeHost.addCallback(mHostCallback); - mDockManager.addListener(mDockEventListener); + if (mDockManager != null) { + mDockManager.addListener(mDockEventListener); + } checkTriggersAtInit(); break; case DOZE: @@ -250,7 +253,9 @@ public class DozeTriggers implements DozeMachine.Part { case FINISH: mBroadcastReceiver.unregister(mContext); mDozeHost.removeCallback(mHostCallback); - mDockManager.removeListener(mDockEventListener); + if (mDockManager != null) { + mDockManager.removeListener(mDockEventListener); + } mDozeSensors.setListening(false); mDozeSensors.setProxListening(false); break; diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index f5ac0d39d61f..7c9b2864f7f2 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -33,6 +33,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.UserInfo; +import android.content.res.Configuration; import android.database.ContentObserver; import android.graphics.Point; import android.graphics.drawable.Drawable; @@ -91,6 +92,7 @@ import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.EmergencyDialerConstants; +import com.android.systemui.util.leak.RotationUtils; import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator; import java.util.ArrayList; @@ -159,6 +161,8 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final ScreenshotHelper mScreenshotHelper; private final ScreenRecordHelper mScreenRecordHelper; + private int mLastRotation; + /** * @param context everything needs a context :( */ @@ -201,6 +205,8 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, mScreenshotHelper = new ScreenshotHelper(context); mScreenRecordHelper = new ScreenRecordHelper(context); + mLastRotation = RotationUtils.getRotation(mContext); + Dependency.get(ConfigurationController.class).addCallback(this); } @@ -426,6 +432,15 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, mContext.getTheme().applyStyle(mContext.getThemeResId(), true); } + @Override + public void onConfigChanged(Configuration newConfig) { + int rotation = RotationUtils.getRotation(mContext); + if (rotation != mLastRotation) { + mDialog.onRotate(); + } + mLastRotation = rotation; + } + public void destroy() { Dependency.get(ConfigurationController.class).removeCallback(this); } @@ -1091,7 +1106,7 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, } protected int getActionLayoutId(Context context) { - if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.GLOBAL_ACTIONS_GRID_ENABLED)) { + if (isGridEnabled(context)) { return com.android.systemui.R.layout.global_actions_grid_item; } return com.android.systemui.R.layout.global_actions_item; @@ -1465,7 +1480,7 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final Context mContext; private final MyAdapter mAdapter; - private final MultiListLayout mGlobalActionsLayout; + private MultiListLayout mGlobalActionsLayout; private final OnClickListener mClickListener; private final OnItemLongClickListener mLongClickListener; private final GradientDrawable mGradientDrawable; @@ -1505,8 +1520,13 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, window.setBackgroundDrawable(mGradientDrawable); window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY); + initializeLayout(); - setContentView(getGlobalActionsLayoutId(context)); + setTitle(R.string.global_actions); + } + + private void initializeLayout() { + setContentView(getGlobalActionsLayoutId(mContext)); mGlobalActionsLayout = (MultiListLayout) findViewById(com.android.systemui.R.id.global_actions_view); mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss()); @@ -1520,11 +1540,20 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, return true; } }); - setTitle(R.string.global_actions); + } + + public void onRotate() { + if (mShowing && isGridEnabled(mContext)) { + initializeLayout(); + updateList(); + } } private int getGlobalActionsLayoutId(Context context) { - if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.GLOBAL_ACTIONS_GRID_ENABLED)) { + if (isGridEnabled(context)) { + if (RotationUtils.getRotation(context) == RotationUtils.ROTATION_SEASCAPE) { + return com.android.systemui.R.layout.global_actions_grid_seascape; + } return com.android.systemui.R.layout.global_actions_grid; } return com.android.systemui.R.layout.global_actions_wrapped; @@ -1543,10 +1572,20 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, int separatedIndex = separatedActions.indexOf(action); ViewGroup parent; if (separatedIndex != -1) { - parent = mGlobalActionsLayout.getParentView(true, separatedIndex); + parent = mGlobalActionsLayout.getParentView(true, separatedIndex, false); } else { + boolean reverse = false; + + // If we're using the grid layout and we're in seascape, reverse the order + // of sublists to make sure they render in the correct positions, + // since we can't reverse vertical LinearLayouts through the layout xml. + + if (isGridEnabled(mContext) && RotationUtils.getRotation(mContext) + == RotationUtils.ROTATION_SEASCAPE) { + reverse = true; + } int listIndex = listActions.indexOf(action); - parent = mGlobalActionsLayout.getParentView(false, listIndex); + parent = mGlobalActionsLayout.getParentView(false, listIndex, reverse); } View v = mAdapter.getView(i, null, parent); final int pos = i; @@ -1665,4 +1704,11 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, mKeyguardShowing = keyguardShowing; } } + + /** + * Determines whether or not the Global Actions Dialog should use the newer grid-style layout. + */ + public static boolean isGridEnabled(Context context) { + return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.GLOBAL_ACTIONS_GRID_ENABLED); + } } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java index 0e49b5f3cd2a..1d042776efc9 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java @@ -83,11 +83,11 @@ public class GlobalActionsGridLayout extends MultiListLayout { } @Override - public ViewGroup getParentView(boolean separated, int index) { + public ViewGroup getParentView(boolean separated, int index, boolean reverseOrder) { if (separated) { return getSeparatedView(); } else { - return getListView().getParentView(index); + return getListView().getParentView(index, reverseOrder); } } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java index 37755155751f..d5dcd74c7ea8 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java @@ -28,16 +28,13 @@ import android.widget.LinearLayout; * * * Try to maintain a 'square' grid (equal number of columns and rows) based on the expected item * count. + * * Determine the position and parent of any item by its index and the total item count. * * Display and hide sub-lists as needed, depending on the expected item count. - * * Favor bias toward having more rows or columns depending on the orientation of the device - * (TODO(123344999): Implement this, currently always favors adding more rows.) - * * Change the orientation (horizontal vs. vertical) of the container and sub-lists to act as rows - * or columns depending on the orientation of the device. - * (TODO(123344999): Implement this, currently always columns.) * * While we could implement this behavior with a GridLayout, it would take significantly more * time and effort, and would require more substantial refactoring of the existing code in - * GlobalActionsDialog, since it would require manipulation of the child items themselves. + * GlobalActionsDialog, since it would require manipulation of layout properties on the child items + * themselves. * */ @@ -65,14 +62,25 @@ public class ListGridLayout extends LinearLayout { /** * Get the parent view associated with the item which should be placed at the given position. */ - public ViewGroup getParentView(int index) { - ViewGroup firstParent = (ViewGroup) getChildAt(0); + public ViewGroup getParentView(int index, boolean reverseSublists) { if (mRows == 0) { - return firstParent; + return null; } + int column = getParentViewIndex(index, reverseSublists); + return (ViewGroup) getChildAt(column); + } + + private int reverseSublistIndex(int index) { + return getChildCount() - (index + 1); + } + + private int getParentViewIndex(int index, boolean reverseSublists) { int column = (int) Math.floor(index / mRows); - ViewGroup parent = (ViewGroup) getChildAt(column); - return parent != null ? parent : firstParent; + int columnCount = getChildCount(); + if (reverseSublists) { + column = reverseSublistIndex(column); + } + return column; } /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 684175cf4212..85d975f36db5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -25,7 +25,9 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.graphics.Rect; import android.graphics.Typeface; +import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.icu.text.DateFormat; import android.icu.text.DisplayContext; @@ -35,10 +37,13 @@ import android.os.Handler; import android.os.Trace; import android.provider.Settings; import android.service.notification.ZenModeConfig; -import android.text.Spannable; import android.text.SpannableStringBuilder; +import android.text.Spanned; import android.text.TextUtils; +import android.text.style.DynamicDrawableSpan; +import android.text.style.ImageSpan; import android.text.style.StyleSpan; +import android.util.MathUtils; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; @@ -72,6 +77,8 @@ public class KeyguardSliceProvider extends SliceProvider implements private static final StyleSpan BOLD_STYLE = new StyleSpan(Typeface.BOLD); public static final String KEYGUARD_SLICE_URI = "content://com.android.systemui.keyguard/main"; + private static final String KEYGUARD_HEADER_URI = + "content://com.android.systemui.keyguard/header"; public static final String KEYGUARD_DATE_URI = "content://com.android.systemui.keyguard/date"; public static final String KEYGUARD_NEXT_ALARM_URI = "content://com.android.systemui.keyguard/alarm"; @@ -90,6 +97,7 @@ public class KeyguardSliceProvider extends SliceProvider implements private static KeyguardSliceProvider sInstance; protected final Uri mSliceUri; + protected final Uri mHeaderUri; protected final Uri mDateUri; protected final Uri mAlarmUri; protected final Uri mDndUri; @@ -163,6 +171,7 @@ public class KeyguardSliceProvider extends SliceProvider implements KeyguardSliceProvider(Handler handler) { mHandler = handler; mSliceUri = Uri.parse(KEYGUARD_SLICE_URI); + mHeaderUri = Uri.parse(KEYGUARD_HEADER_URI); mDateUri = Uri.parse(KEYGUARD_DATE_URI); mAlarmUri = Uri.parse(KEYGUARD_NEXT_ALARM_URI); mDndUri = Uri.parse(KEYGUARD_DND_URI); @@ -213,26 +222,32 @@ public class KeyguardSliceProvider extends SliceProvider implements protected void addMediaLocked(ListBuilder listBuilder) { if (mMediaMetaData != null) { SpannableStringBuilder builder = new SpannableStringBuilder(); + + Icon notificationIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon(); + if (notificationIcon != null) { + Drawable drawable = notificationIcon.loadDrawable(getContext()); + Rect mediaBounds = new Rect(0 /* left */, 0 /* top */, + drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + int iconHeaderSize = getContext().getResources() + .getDimensionPixelSize(R.dimen.header_icon_size); + MathUtils.fitRect(mediaBounds, iconHeaderSize); + drawable.setBounds(mediaBounds); + builder.append("# "); + builder.setSpan(new ImageSpan(drawable, DynamicDrawableSpan.ALIGN_CENTER), + 0 /* start */, 1 /* end */, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + CharSequence title = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_TITLE); if (TextUtils.isEmpty(title)) { title = getContext().getResources().getString(R.string.music_controls_no_title); } builder.append(title); - builder.setSpan(BOLD_STYLE, 0, title.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(builder)); CharSequence album = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_ARTIST); if (!TextUtils.isEmpty(album)) { - builder.append(" ").append(album); + listBuilder.addRow(new RowBuilder(mMediaUri).setTitle(album)); } - - RowBuilder mediaBuilder = new RowBuilder(mMediaUri).setTitle(builder); - Icon notificationIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon(); - if (notificationIcon != null) { - IconCompat icon = IconCompat.createFromIcon(notificationIcon); - mediaBuilder.addEndItem(icon, ListBuilder.ICON_IMAGE); - } - - listBuilder.addRow(mediaBuilder); } } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt index f7ca51d6f840..a6e48f8835c7 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt @@ -62,4 +62,6 @@ data class PrivacyApplication(val packageName: String, val uid: Int, val context context.packageManager.getApplicationLabel(it) as String } ?: packageName } + + override fun toString() = "PrivacyApplication(packageName=$packageName, uid=$uid)" } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt index faeacf767c9a..625eacd7e2a7 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -26,16 +26,26 @@ import android.os.Handler import android.os.UserHandle import android.os.UserManager import com.android.internal.annotations.VisibleForTesting -import com.android.systemui.Dependency +import com.android.systemui.Dependency.BG_HANDLER_NAME +import com.android.systemui.Dependency.MAIN_HANDLER_NAME +import com.android.systemui.R import com.android.systemui.appops.AppOpItem import com.android.systemui.appops.AppOpsController -import com.android.systemui.R +import com.android.systemui.Dumpable +import java.io.FileDescriptor +import java.io.PrintWriter import java.lang.ref.WeakReference import javax.inject.Inject +import javax.inject.Named import javax.inject.Singleton @Singleton -class PrivacyItemController @Inject constructor(private val context: Context) { +class PrivacyItemController @Inject constructor( + val context: Context, + private val appOpsController: AppOpsController, + @Named(MAIN_HANDLER_NAME) private val uiHandler: Handler, + @Named(BG_HANDLER_NAME) private val bgHandler: Handler +) : Dumpable { companion object { val OPS = intArrayOf(AppOpsManager.OP_CAMERA, @@ -48,16 +58,13 @@ class PrivacyItemController @Inject constructor(private val context: Context) { const val TAG = "PrivacyItemController" const val SYSTEM_UID = 1000 } - private var privacyList = emptyList<PrivacyItem>() - @Suppress("DEPRECATION") - private val appOpsController = Dependency.get(AppOpsController::class.java) + @VisibleForTesting + internal var privacyList = emptyList<PrivacyItem>() + get() = field.toList() // Provides a shallow copy of the list + private val userManager = context.getSystemService(UserManager::class.java) private var currentUserIds = emptyList<Int>() - @Suppress("DEPRECATION") - private val bgHandler = Handler(Dependency.get(Dependency.BG_LOOPER)) - @Suppress("DEPRECATION") - private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER) private var listening = false val systemApp = PrivacyApplication(context.getString(R.string.device_services), SYSTEM_UID, context) @@ -188,4 +195,22 @@ class PrivacyItemController @Inject constructor(private val context: Context) { callback?.privacyChanged(list) } } + + override fun dump(fd: FileDescriptor?, pw: PrintWriter?, args: Array<out String>?) { + pw?.println("PrivacyItemController state:") + pw?.println(" Listening: $listening") + pw?.println(" Current user ids: $currentUserIds") + pw?.println(" Privacy Items:") + privacyList.forEach { + pw?.print(" ") + pw?.println(it.toString()) + } + pw?.println(" Callbacks:") + callbacks.forEach { + it.get()?.let { + pw?.print(" ") + pw?.println(it.toString()) + } + } + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 01b0bb14c7ca..110d51563e2c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -348,15 +348,13 @@ public class NotificationShelf extends ActivatableNotificationView implements if (notGoneIndex == 0) { StatusBarIconView icon = row.getEntry().expandedIcon; NotificationIconContainer.IconState iconState = getIconState(icon); + // The icon state might be null in rare cases where the notification is actually + // added to the layout, but not to the shelf. An example are replied messages, since + // they don't show up on AOD if (iconState != null && iconState.clampedAppearAmount == 1.0f) { // only if the first icon is fully in the shelf we want to clip to it! backgroundTop = (int) (row.getTranslationY() - getTranslationY()); firstElementRoundness = row.getCurrentTopRoundness(); - } else if (iconState == null) { - Log.wtf(TAG, "iconState is null. ExpandedIcon: " + row.getEntry().expandedIcon - + (row.getEntry().expandedIcon != null - ? "\n icon parent: " + row.getEntry().expandedIcon.getParent() : "") - + " \n number of notifications: " + mHostLayout.getChildCount() ); } } if (row.isFirstInSection() && previousRow != null && previousRow.isLastInSection()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 662cf514b977..ee5ac7c602aa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -24,6 +24,7 @@ import android.view.View; import android.view.ViewGroup; import com.android.systemui.R; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -61,7 +62,7 @@ public class NotificationViewHierarchyManager { protected final NotificationLockscreenUserManager mLockscreenUserManager; protected final NotificationGroupManager mGroupManager; protected final VisualStabilityManager mVisualStabilityManager; - private final StatusBarStateControllerImpl mStatusBarStateController; + private final SysuiStatusBarStateController mStatusBarStateController; private final NotificationEntryManager mEntryManager; // Lazy @@ -82,13 +83,13 @@ public class NotificationViewHierarchyManager { NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationGroupManager groupManager, VisualStabilityManager visualStabilityManager, - StatusBarStateControllerImpl statusBarStateController, + StatusBarStateController statusBarStateController, NotificationEntryManager notificationEntryManager, Lazy<ShadeController> shadeController) { mLockscreenUserManager = notificationLockscreenUserManager; mGroupManager = groupManager; mVisualStabilityManager = visualStabilityManager; - mStatusBarStateController = statusBarStateController; + mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController; mEntryManager = notificationEntryManager; mShadeController = shadeController; Resources res = context.getResources(); 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 8f7778b4a351..69828c166463 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 @@ -1213,6 +1213,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView l.initView(); l.reInflateViews(); } + mStatusBarNotification.clearPackageContext(); mNotificationInflater.clearCachesAndReInflate(); onNotificationUpdated(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 2799191a886f..e0c5e59b73f3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -92,6 +92,8 @@ import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.NotificationChannels; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.List; import java.util.Locale; @@ -793,6 +795,15 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, boolean showMicrophone = false; boolean showLocation = false; for (PrivacyItem item : items) { + if (item == null /* b/124234367 */) { + if (DEBUG) { + Log.e(TAG, "updatePrivacyItems - null item found"); + StringWriter out = new StringWriter(); + mPrivacyItemController.dump(null, new PrintWriter(out), null); + Log.e(TAG, out.toString()); + } + continue; + } switch (item.getPrivacyType()) { case TYPE_CAMERA: showCamera = true; 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 6b6998d7c4a0..e42004a1ca13 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -189,7 +189,6 @@ import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; @@ -653,7 +652,7 @@ public class StatusBar extends SystemUI implements DemoMode, mColorExtractor.addOnColorsChangedListener(this); mStatusBarStateController.addCallback(this, - StatusBarStateControllerImpl.RANK_STATUS_BAR); + SysuiStatusBarStateController.RANK_STATUS_BAR); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); mDreamManager = IDreamManager.Stub.asInterface( @@ -1893,6 +1892,8 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarWindow.cancelExpandHelper(); mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor); + } else { + mBubbleController.collapseStack(); } } @@ -2533,6 +2534,9 @@ public class StatusBar extends SystemUI implements DemoMode, if (mRemoteInputManager.getController() != null) { mRemoteInputManager.getController().closeRemoteInputs(); } + if (mBubbleController.isStackExpanded()) { + mBubbleController.collapseStack(); + } if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) { int flags = CommandQueue.FLAG_EXCLUDE_NONE; String reason = intent.getStringExtra("reason"); @@ -2546,6 +2550,9 @@ public class StatusBar extends SystemUI implements DemoMode, if (mStatusBarWindowController != null) { mStatusBarWindowController.setNotTouchable(false); } + if (mBubbleController.isStackExpanded()) { + mBubbleController.collapseStack(); + } finishBarAnimations(); resetUserExpandedStates(); } @@ -4004,8 +4011,7 @@ public class StatusBar extends SystemUI implements DemoMode, float viewY = screenY - mTmpInt2[1]; if (0 <= viewX && viewX <= mAmbientIndicationContainer.getWidth() && 0 <= viewY && viewY <= mAmbientIndicationContainer.getHeight()) { - if (mAmbientIndicationContainer instanceof DozeReceiver) - ((DozeReceiver) mAmbientIndicationContainer).onDozeDoubleTap(); + dispatchTap(mAmbientIndicationContainer, viewX, viewY); } } } @@ -4020,6 +4026,12 @@ public class StatusBar extends SystemUI implements DemoMode, mScrimController.setAodFrontScrimAlpha(scrimOpacity); } + private void dispatchTap(View view, float x, float y) { + long now = SystemClock.elapsedRealtime(); + dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_DOWN); + dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_UP); + } + private void dispatchTouchEvent(View view, float x, float y, long now, int action) { MotionEvent ev = MotionEvent.obtain(now, now, action, x, y, 0 /* meta */); view.dispatchTouchEvent(ev); diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml index e73c70b29b74..efb4ff008b23 100644 --- a/packages/SystemUI/tests/AndroidManifest.xml +++ b/packages/SystemUI/tests/AndroidManifest.xml @@ -68,6 +68,13 @@ </intent-filter> </receiver> + <activity android:name="com.android.systemui.bubbles.BubblesTestActivity" + android:allowEmbedded="true" + android:documentLaunchMode="always" + android:excludeFromRecents="true" + android:exported="false" + android:resizeableActivity="true" /> + <provider android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer" tools:replace="android:authorities" diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java index 77895c97051c..190ce7550511 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java @@ -75,6 +75,17 @@ public class KeyguardSliceViewTest extends SysuiTestCase { } @Test + public void hasHeader_readsSliceData() { + ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY); + mKeyguardSliceView.onChanged(builder.build()); + Assert.assertFalse("View should not have a header", mKeyguardSliceView.hasHeader()); + + builder.setHeader(new ListBuilder.HeaderBuilder().setTitle("header title!")); + mKeyguardSliceView.onChanged(builder.build()); + Assert.assertTrue("View should have a header", mKeyguardSliceView.hasHeader()); + } + + @Test public void refresh_replacesSliceContentAndNotifiesListener() { AtomicBoolean notified = new AtomicBoolean(); mKeyguardSliceView.setContentChangeListener(()-> notified.set(true)); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java new file mode 100644 index 000000000000..f813ac693d42 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import static com.google.common.truth.Truth.assertThat; + +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.LeakCheck; +import android.testing.TestableLooper.RunWithLooper; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.dock.DockManager; +import com.android.systemui.dock.DockManagerFake; +import com.android.systemui.utils.leaks.FakeExtensionController; + +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; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public final class ClockManagerTest extends SysuiTestCase { + + private ClockManager mClockManager; + private LeakCheck mLeakCheck; + private FakeExtensionController mFakeExtensionController; + private DockManagerFake mFakeDockManager; + @Mock ClockManager.ClockChangedListener mMockListener; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mLeakCheck = new LeakCheck(); + mFakeExtensionController = new FakeExtensionController(mLeakCheck); + mFakeDockManager = new DockManagerFake(); + mClockManager = new ClockManager(getContext(), mFakeExtensionController, + mFakeDockManager); + mClockManager.addOnClockChangedListener(mMockListener); + } + + @After + public void tearDown() { + mClockManager.removeOnClockChangedListener(mMockListener); + } + + @Test + public void dockEvent() { + mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); + assertThat(mClockManager.isDocked()).isTrue(); + } + + @Test + public void undockEvent() { + mFakeDockManager.setDockEvent(DockManager.STATE_NONE); + assertThat(mClockManager.isDocked()).isFalse(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/DefaultClockSupplierTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/DefaultClockSupplierTest.java new file mode 100644 index 000000000000..1a3b198ac0d6 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/DefaultClockSupplierTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.view.LayoutInflater; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.ClockPlugin; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public final class DefaultClockSupplierTest extends SysuiTestCase { + + private static final String BUBBLE_CLOCK = BubbleClockController.class.getName(); + private static final Class<?> BUBBLE_CLOCK_CLASS = BubbleClockController.class; + + private DefaultClockSupplier mDefaultClockSupplier; + @Mock SettingsWrapper mMockSettingsWrapper; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mDefaultClockSupplier = new DefaultClockSupplier(mMockSettingsWrapper, + LayoutInflater.from(getContext())); + } + + @Test + public void get_default() { + // GIVEN that settings doesn't contain any values + when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(null); + when(mMockSettingsWrapper.getDockedClockFace()).thenReturn(null); + // WHEN get is called + ClockPlugin plugin = mDefaultClockSupplier.get(); + // THEN the result is null, indicated the default clock face should be used. + assertThat(plugin).isNull(); + } + + @Test + public void get_customClock() { + // GIVEN that settings is set to the bubble clock face + when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK); + // WHEN get is called + ClockPlugin plugin = mDefaultClockSupplier.get(); + // THEN the plugin is the bubble clock face. + assertThat(plugin).isInstanceOf(BUBBLE_CLOCK_CLASS); + } + + @Test + public void get_badSettingsValue() { + // GIVEN that settings contains a value that doesn't correspond to a + // custom clock face. + when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn("bad value"); + // WHEN get is called + ClockPlugin plugin = mDefaultClockSupplier.get(); + // THEN the result is null. + assertThat(plugin).isNull(); + } + + @Test + public void get_dockedDefault() { + // GIVEN docked + mDefaultClockSupplier.setDocked(true); + // WHEN get is called + ClockPlugin plugin = mDefaultClockSupplier.get(); + // THEN the result is null, indicating the default clock face. + assertThat(plugin).isNull(); + } + + @Test + public void get_dockedCustomClock() { + // GIVEN docked and settings is set to the bubble clock face + mDefaultClockSupplier.setDocked(true); + when(mMockSettingsWrapper.getDockedClockFace()).thenReturn(BUBBLE_CLOCK); + // WHEN get is called + ClockPlugin plugin = mDefaultClockSupplier.get(); + // THEN the plugin is the bubble clock face. + assertThat(plugin).isInstanceOf(BUBBLE_CLOCK_CLASS); + } + + @Test + public void get_badDockedSettingsValue() { + // GIVEN docked and settings contains a value that doesn't correspond to + // an available clock face. + mDefaultClockSupplier.setDocked(true); + when(mMockSettingsWrapper.getDockedClockFace()).thenReturn("bad value"); + // WHEN get is called + ClockPlugin plugin = mDefaultClockSupplier.get(); + // THEN the result is null. + assertThat(plugin).isNull(); + } + + @Test + public void get_badDockedSettingsFallback() { + // GIVEN docked and settings contains a value that doesn't correspond to + // an available clock face, but locked screen settings is set to bubble + // clock. + mDefaultClockSupplier.setDocked(true); + when(mMockSettingsWrapper.getDockedClockFace()).thenReturn("bad value"); + when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK); + // WHEN get is called + ClockPlugin plugin = mDefaultClockSupplier.get(); + // THEN the plugin is the bubble clock face. + assertThat(plugin).isInstanceOf(BUBBLE_CLOCK_CLASS); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index 2742577db860..ca72602f2c2b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -82,6 +82,8 @@ public class BubbleControllerTest extends SysuiTestCase { @Mock private BubbleController.BubbleExpandListener mBubbleExpandListener; + private BubbleData mBubbleData; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -104,7 +106,9 @@ public class BubbleControllerTest extends SysuiTestCase { when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(mRow.getEntry().channel); when(mNotificationData.getChannel(mNoChannelRow.getEntry().key)).thenReturn(null); - mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController); + mBubbleData = new BubbleData(); + mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController, + mBubbleData); mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener); mBubbleController.setExpandListener(mBubbleExpandListener); @@ -207,12 +211,12 @@ public class BubbleControllerTest extends SysuiTestCase { verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key); // Last added is the one that is expanded - assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry()); + assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry()); assertFalse(mRow2.getEntry().showInShadeWhenBubble()); // Switch which bubble is expanded stackView.setExpandedBubble(mRow.getEntry()); - assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry()); + assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry()); assertFalse(mRow.getEntry().showInShadeWhenBubble()); // collapse for previous bubble @@ -262,19 +266,19 @@ public class BubbleControllerTest extends SysuiTestCase { verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key); // Last added is the one that is expanded - assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry()); + assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry()); assertFalse(mRow2.getEntry().showInShadeWhenBubble()); // Dismiss currently expanded - mBubbleController.removeBubble(stackView.getExpandedBubble().getKey()); + mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey()); verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key); // Make sure next bubble is selected - assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry()); + assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key); // Dismiss that one - mBubbleController.removeBubble(stackView.getExpandedBubble().getKey()); + mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey()); // Make sure state changes and collapse happens verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key); @@ -297,8 +301,8 @@ public class BubbleControllerTest extends SysuiTestCase { static class TestableBubbleController extends BubbleController { TestableBubbleController(Context context, - StatusBarWindowController statusBarWindowController) { - super(context, statusBarWindowController); + StatusBarWindowController statusBarWindowController, BubbleData data) { + super(context, statusBarWindowController, data); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java new file mode 100644 index 000000000000..ea472da910f2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.bubbles; + +import android.app.Activity; +import android.os.Bundle; + +import com.android.systemui.R; + +/** + * Referenced by NotificationTestHelper#makeBubbleMetadata + */ +public class BubblesTestActivity extends Activity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt index 98bf3c2743a8..bb384dd52875 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.privacy import android.app.ActivityManager import android.app.AppOpsManager +import android.content.Context import android.content.Intent import android.content.pm.UserInfo import android.os.Handler @@ -28,11 +29,18 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper import com.android.systemui.Dependency +import com.android.systemui.Dependency.BG_HANDLER +import com.android.systemui.Dependency.MAIN_HANDLER import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.appops.AppOpItem import com.android.systemui.appops.AppOpsController +import org.hamcrest.Matchers.hasItem +import org.hamcrest.Matchers.not +import org.hamcrest.Matchers.nullValue import org.junit.Assert.assertEquals +import org.junit.Assert.assertThat +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -81,15 +89,20 @@ class PrivacyItemControllerTest : SysuiTestCase() { private lateinit var testableLooper: TestableLooper private lateinit var privacyItemController: PrivacyItemController + private lateinit var handler: Handler + + fun PrivacyItemController(context: Context) = + PrivacyItemController(context, appOpsController, handler, handler) @Before fun setup() { MockitoAnnotations.initMocks(this) testableLooper = TestableLooper.get(this) + handler = Handler(testableLooper.looper) appOpsController = mDependency.injectMockDependency(AppOpsController::class.java) - mDependency.injectTestDependency(Dependency.BG_LOOPER, testableLooper.looper) - mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler(testableLooper.looper)) + mDependency.injectTestDependency(Dependency.BG_HANDLER, handler) + mDependency.injectTestDependency(Dependency.MAIN_HANDLER, handler) mContext.addMockSystemService(UserManager::class.java, userManager) mContext.getOrCreateTestableResources().addOverride(R.string.device_services, DEVICE_SERVICES_STRING) @@ -232,4 +245,26 @@ class PrivacyItemControllerTest : SysuiTestCase() { verify(callback, never()).privacyChanged(anyList()) verify(otherCallback).privacyChanged(anyList()) } + + @Test + fun testListShouldNotHaveNull() { + doReturn(listOf(AppOpItem(AppOpsManager.OP_ACTIVATE_VPN, TEST_UID, "", 0), + AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0))) + .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) + privacyItemController.addCallback(callback) + testableLooper.processAllMessages() + + verify(callback).privacyChanged(capture(argCaptor)) + assertEquals(1, argCaptor.value.size) + assertThat(argCaptor.value, not(hasItem(nullValue()))) + } + + @Test + fun testListShouldBeCopy() { + val list = listOf(PrivacyItem(PrivacyType.TYPE_CAMERA, + PrivacyApplication("", TEST_UID, mContext))) + privacyItemController.privacyList = list + assertEquals(list, privacyItemController.privacyList) + assertTrue(list !== privacyItemController.privacyList) + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java index 0b24c2169ea1..a2c880a5bf9b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java @@ -36,6 +36,7 @@ import android.view.LayoutInflater; import android.widget.RemoteViews; import com.android.systemui.R; +import com.android.systemui.bubbles.BubblesTestActivity; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; @@ -290,7 +291,8 @@ public class NotificationTestHelper { } private Notification.BubbleMetadata makeBubbleMetadata() { - PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); + Intent target = new Intent(mContext, BubblesTestActivity.class); + PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, target, 0); return new Notification.BubbleMetadata.Builder() .setIntent(bubbleIntent) .setTitle("bubble title") diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java index 9b863a9f2d26..61f63d3bbc3d 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java @@ -47,7 +47,7 @@ final class RemoteAugmentedAutofillService private static final String TAG = RemoteAugmentedAutofillService.class.getSimpleName(); - private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS; + private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS; RemoteAugmentedAutofillService(Context context, ComponentName serviceName, int userId, RemoteAugmentedAutofillServiceCallbacks callbacks, @@ -106,6 +106,12 @@ final class RemoteAugmentedAutofillService activityComponent, focusedId, focusedValue)); } + @Override + public String toString() { + return "RemoteAugmentedAutofillService[" + + ComponentName.flattenToShortString(getComponentName()) + "]"; + } + /** * Called by {@link Session} when it's time to destroy all augmented autofill requests. */ @@ -181,11 +187,13 @@ final class RemoteAugmentedAutofillService @Override protected void onTimeout(RemoteAugmentedAutofillService remoteService) { - Slog.wtf(TAG, "timed out: " + this); + // TODO(b/122858578): must update the logged AUTOFILL_AUGMENTED_REQUEST with the + // timeout + Slog.w(TAG, "PendingAutofillRequest timed out (" + TIMEOUT_REMOTE_REQUEST_MILLIS + + "ms) for " + remoteService); // NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks finish(); } - } public interface RemoteAugmentedAutofillServiceCallbacks diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java index 17e9b350d368..00cb6d3a0a26 100644 --- a/services/backup/java/com/android/server/backup/Trampoline.java +++ b/services/backup/java/com/android/server/backup/Trampoline.java @@ -47,6 +47,8 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.DumpUtils; +import com.android.server.backup.utils.FileUtils; +import com.android.server.backup.utils.RandomAccessFileUtils; import java.io.File; import java.io.FileDescriptor; @@ -89,6 +91,12 @@ public class Trampoline extends IBackupManager.Stub { */ private static final String BACKUP_ACTIVATED_FILENAME = "backup-activated"; + /** + * Name of file for non-system users that remembers whether backup was explicitly activated or + * deactivated with a call to setBackupServiceActive. + */ + private static final String REMEMBER_ACTIVATED_FILENAME_PREFIX = "backup-remember-activated"; + // Product-level suppression of backup/restore. private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable"; @@ -134,11 +142,17 @@ public class Trampoline extends IBackupManager.Stub { } /** Stored in the system user's directory and the file is indexed by the user it refers to. */ + protected File getRememberActivatedFileForNonSystemUser(int userId) { + return FileUtils.createNewFile(UserBackupManagerFiles.getStateFileInSystemDir( + REMEMBER_ACTIVATED_FILENAME_PREFIX, userId)); + } + + /** Stored in the system user's directory and the file is indexed by the user it refers to. */ protected File getActivatedFileForNonSystemUser(int userId) { - return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM), - BACKUP_ACTIVATED_FILENAME + "-" + userId); + return UserBackupManagerFiles.getStateFileInSystemDir(BACKUP_ACTIVATED_FILENAME, userId); } + // TODO (b/124359804) move to util method in FileUtils private void createFile(File file) throws IOException { if (file.exists()) { return; @@ -150,6 +164,7 @@ public class Trampoline extends IBackupManager.Stub { } } + // TODO (b/124359804) move to util method in FileUtils private void deleteFile(File file) { if (!file.exists()) { return; @@ -312,6 +327,19 @@ public class Trampoline extends IBackupManager.Stub { public void setBackupServiceActive(int userId, boolean makeActive) { enforcePermissionsOnUser(userId); + // In Q, backup is OFF by default for non-system users. In the future, we will change that + // to ON unless backup was explicitly deactivated with a (permissioned) call to + // setBackupServiceActive. + // Therefore, remember this for use in the future. Basically the default in the future will + // be: rememberFile.exists() ? rememberFile.value() : ON + // Note that this has to be done right after the permission checks and before any other + // action since we need to remember that a permissioned call was made irrespective of + // whether the call changes the state or not. + if (userId != UserHandle.USER_SYSTEM) { + RandomAccessFileUtils.writeBoolean(getRememberActivatedFileForNonSystemUser(userId), + makeActive); + } + if (mGlobalDisable) { Slog.i(TAG, "Backup service not supported"); return; diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java index aabd41a611a1..4638ac63de4a 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java @@ -48,4 +48,9 @@ final class UserBackupManagerFiles { // is a staging dir, we dont need to copy below dir to new system user dir return new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR); } + + /** Stored in the system user's directory and the file is indexed by the user it refers to. */ + static File getStateFileInSystemDir(String prefix, int userId) { + return new File(getBaseStateDir(UserHandle.USER_SYSTEM), prefix + "-" + userId); + } } diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index b2afbc3ec5f9..d4ac731f8810 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -130,6 +130,7 @@ import com.android.server.backup.transport.TransportNotRegisteredException; import com.android.server.backup.utils.AppBackupUtils; import com.android.server.backup.utils.BackupManagerMonitorUtils; import com.android.server.backup.utils.BackupObserverUtils; +import com.android.server.backup.utils.FileUtils; import com.android.server.backup.utils.SparseArrayUtils; import com.google.android.collect.Sets; @@ -2319,6 +2320,7 @@ public class UserBackupManagerService { mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "setAncestralSerialNumber"); Slog.v(TAG, "Setting ancestral work profile id to " + ancestralSerialNumber); + // TODO (b/124359804) try (RandomAccessFile af = getAncestralSerialNumberFile()) { af.writeLong(ancestralSerialNumber); } catch (IOException e) { @@ -2331,6 +2333,7 @@ public class UserBackupManagerService { * {@link #setAncestralSerialNumber(long)}. Will return {@code -1} if not set. */ public long getAncestralSerialNumber() { + // TODO (b/124359804) try (RandomAccessFile af = getAncestralSerialNumberFile()) { return af.readLong(); } catch (IOException e) { @@ -2344,13 +2347,7 @@ public class UserBackupManagerService { mAncestralSerialNumberFile = new File( UserBackupManagerFiles.getBaseStateDir(getUserId()), SERIAL_ID_FILE); - if (!mAncestralSerialNumberFile.exists()) { - try { - mAncestralSerialNumberFile.createNewFile(); - } catch (IOException e) { - Slog.w(TAG, "serial number mapping file creation failed", e); - } - } + FileUtils.createNewFile(mAncestralSerialNumberFile); } return new RandomAccessFile(mAncestralSerialNumberFile, "rwd"); } diff --git a/services/backup/java/com/android/server/backup/utils/FileUtils.java b/services/backup/java/com/android/server/backup/utils/FileUtils.java new file mode 100644 index 000000000000..00686cba4777 --- /dev/null +++ b/services/backup/java/com/android/server/backup/utils/FileUtils.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.backup.utils; + +import static com.android.server.backup.BackupManagerService.TAG; + +import android.util.Slog; + +import java.io.File; +import java.io.IOException; + +/** Utility methods useful for working with backup related files. */ +public final class FileUtils { + /** + * Ensure that the file exists in the file system. If an IOException is thrown, it is ignored. + * This method is useful to avoid code duplication of the "try-catch-ignore exception" block. + */ + public static File createNewFile(File file) { + try { + file.createNewFile(); + } catch (IOException e) { + Slog.w(TAG, "Failed to create file:" + file.getAbsolutePath(), e); + } + return file; + } +} diff --git a/services/backup/java/com/android/server/backup/utils/RandomAccessFileUtils.java b/services/backup/java/com/android/server/backup/utils/RandomAccessFileUtils.java new file mode 100644 index 000000000000..abf906aee5dd --- /dev/null +++ b/services/backup/java/com/android/server/backup/utils/RandomAccessFileUtils.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.backup.utils; + +import static com.android.server.backup.BackupManagerService.TAG; + +import android.util.Slog; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; + +/** Utility methods useful for working with backup related RandomAccessFiles. */ +public final class RandomAccessFileUtils { + private static RandomAccessFile getRandomAccessFile(File file) throws FileNotFoundException { + return new RandomAccessFile(file, "rwd"); + } + + /** Write a boolean to a File by wrapping it using a RandomAccessFile. */ + public static void writeBoolean(File file, boolean b) { + try (RandomAccessFile af = getRandomAccessFile(file)) { + af.writeBoolean(b); + } catch (IOException e) { + Slog.w(TAG, "Error writing file:" + file.getAbsolutePath(), e); + } + } + + /** Read a boolean from a File by wrapping it using a RandomAccessFile. */ + public static boolean readBoolean(File file, boolean def) { + try (RandomAccessFile af = getRandomAccessFile(file)) { + return af.readBoolean(); + } catch (IOException e) { + Slog.w(TAG, "Error reading file:" + file.getAbsolutePath(), e); + } + return def; + } +} diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java index 3c52e17ce1e8..4ed5c3d263b6 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java @@ -37,6 +37,9 @@ final class ContentCaptureServerSession { final IBinder mActivityToken; private final ContentCapturePerUserService mService; private final RemoteContentCaptureService mRemoteService; + + // NOTE: this is the "internal" context (like package and taskId), not the explicit content + // set by apps - those are only send to the ContentCaptureService. private final ContentCaptureContext mContentCaptureContext; /** diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index d1cd072ee215..dad428acbfee 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2842,6 +2842,8 @@ public class ConnectivityService extends IConnectivityManager.Stub if (DBG) { log(nai.name() + " got DISCONNECTED, was satisfying " + nai.numNetworkRequests()); } + // Clear all notifications of this network. + mNotifier.clearNotification(nai.network.netId); // A network agent has disconnected. // TODO - if we move the logic to the network agent (have them disconnect // because they lost all their requests or because their score isn't good) diff --git a/services/core/java/com/android/server/ExtconUEventObserver.java b/services/core/java/com/android/server/ExtconUEventObserver.java index ebd4c5584fe1..eb591528bc90 100644 --- a/services/core/java/com/android/server/ExtconUEventObserver.java +++ b/services/core/java/com/android/server/ExtconUEventObserver.java @@ -49,7 +49,7 @@ public abstract class ExtconUEventObserver extends UEventObserver { private static final String TAG = "ExtconUEventObserver"; private static final boolean LOG = false; private static final String SELINUX_POLICIES_NEED_TO_BE_CHANGED = - "This probably mean the selinux policies need to be changed."; + "This probably means the selinux policies need to be changed."; private final Map<String, ExtconInfo> mExtconInfos = new ArrayMap<>(); @@ -159,6 +159,15 @@ public abstract class ExtconUEventObserver extends UEventObserver { /** Does the {@link /sys/class/extcon} directory exist */ public static boolean extconExists() { File extconDir = new File("/sys/class/extcon"); - return extconDir.exists() && extconDir.isDirectory(); + boolean retVal = extconDir.exists() && extconDir.isDirectory(); + // TODO(b/124364409): return the correct value after selinux policy is updated. + if (retVal) { + Slog.w(TAG, extconDir + " exists " + extconDir.exists() + " isDir " + + extconDir.isDirectory() + + " but reporting it does not exist until selinux policies are updated." + + " see b/124364409" + ); + } + return false; } } diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 3479e18b97c5..9d92ea2b5b45 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -1645,36 +1645,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private abstract static class LinkedListenerBase implements IBinder.DeathRecipient { - protected final CallerIdentity mCallerIdentity; - protected final String mListenerName; - - private LinkedListenerBase(@NonNull CallerIdentity callerIdentity, - @NonNull String listenerName) { - mCallerIdentity = callerIdentity; - mListenerName = listenerName; - } - } - - private static class LinkedListener<TListener> extends LinkedListenerBase { - private final TListener mListener; - private final Consumer<TListener> mBinderDeathCallback; - - private LinkedListener(@NonNull TListener listener, String listenerName, - @NonNull CallerIdentity callerIdentity, - @NonNull Consumer<TListener> binderDeathCallback) { - super(callerIdentity, listenerName); - mListener = listener; - mBinderDeathCallback = binderDeathCallback; - } - - @Override - public void binderDied() { - if (D) Log.d(TAG, "Remote " + mListenerName + " died."); - mBinderDeathCallback.accept(mListener); - } - } - @Override public void removeGnssBatchingCallback() { synchronized (mLock) { @@ -2069,7 +2039,7 @@ public class LocationManagerService extends ILocationManager.Stub { } if (!provider.isUseableLocked()) { if (isSettingsExemptLocked(record)) { - providerRequest.forceLocation = true; + providerRequest.locationSettingsIgnored = true; providerRequest.lowPowerMode = false; } else { continue; @@ -2079,8 +2049,9 @@ public class LocationManagerService extends ILocationManager.Stub { LocationRequest locationRequest = record.mRealRequest; long interval = locationRequest.getInterval(); + // if we're forcing location, don't apply any throttling - if (!providerRequest.forceLocation && !isThrottlingExemptLocked( + if (!providerRequest.locationSettingsIgnored && !isThrottlingExemptLocked( record.mReceiver.mCallerIdentity)) { if (!record.mIsForegroundUid) { interval = Math.max(interval, backgroundThrottleInterval); @@ -2710,77 +2681,85 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName) { - if (!hasGnssPermissions(packageName) || mGnssStatusProvider == null) { - return false; - } + return addGnssDataListener(listener, packageName, "GnssStatusListener", + mGnssStatusProvider, mGnssStatusListeners, + this::unregisterGnssStatusCallback); + } - CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), - Binder.getCallingPid(), packageName); - LinkedListener<IGnssStatusListener> linkedListener = new LinkedListener<>(listener, - "GnssStatusListener", callerIdentity, this::unregisterGnssStatusCallback); - IBinder binder = listener.asBinder(); - synchronized (mLock) { - if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) { - return false; - } + @Override + public void unregisterGnssStatusCallback(IGnssStatusListener listener) { + removeGnssDataListener(listener, mGnssStatusProvider, mGnssStatusListeners); + } - mGnssStatusListeners.put(binder, linkedListener); - long identity = Binder.clearCallingIdentity(); - try { - if (isThrottlingExemptLocked(callerIdentity) - || isImportanceForeground( - mActivityManager.getPackageImportance(packageName))) { - mGnssStatusProvider.addListener(listener, callerIdentity); - } - return true; - } finally { - Binder.restoreCallingIdentity(identity); - } - } + @Override + public boolean addGnssMeasurementsListener( + IGnssMeasurementsListener listener, String packageName) { + return addGnssDataListener(listener, packageName, "GnssMeasurementsListener", + mGnssMeasurementsProvider, mGnssMeasurementsListeners, + this::removeGnssMeasurementsListener); } @Override - public void unregisterGnssStatusCallback(IGnssStatusListener listener) { - if (mGnssStatusProvider == null) { - return; + public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) { + removeGnssDataListener(listener, mGnssMeasurementsProvider, mGnssMeasurementsListeners); + } + + private abstract static class LinkedListenerBase implements IBinder.DeathRecipient { + protected final CallerIdentity mCallerIdentity; + protected final String mListenerName; + + private LinkedListenerBase(@NonNull CallerIdentity callerIdentity, + @NonNull String listenerName) { + mCallerIdentity = callerIdentity; + mListenerName = listenerName; } + } - IBinder binder = listener.asBinder(); - synchronized (mLock) { - LinkedListener<IGnssStatusListener> linkedListener = - mGnssStatusListeners.remove(binder); - if (linkedListener == null) { - return; - } - unlinkFromListenerDeathNotificationLocked(binder, linkedListener); - mGnssStatusProvider.removeListener(listener); + private static class LinkedListener<TListener> extends LinkedListenerBase { + private final TListener mListener; + private final Consumer<TListener> mBinderDeathCallback; + + private LinkedListener(@NonNull TListener listener, String listenerName, + @NonNull CallerIdentity callerIdentity, + @NonNull Consumer<TListener> binderDeathCallback) { + super(callerIdentity, listenerName); + mListener = listener; + mBinderDeathCallback = binderDeathCallback; + } + + @Override + public void binderDied() { + if (D) Log.d(TAG, "Remote " + mListenerName + " died."); + mBinderDeathCallback.accept(mListener); } } - @Override - public boolean addGnssMeasurementsListener( - IGnssMeasurementsListener listener, String packageName) { - if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) { + private <TListener extends IInterface> boolean addGnssDataListener( + TListener listener, String packageName, String listenerName, + RemoteListenerHelper<TListener> gnssDataProvider, + ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners, + Consumer<TListener> binderDeathCallback) { + if (!hasGnssPermissions(packageName) || gnssDataProvider == null) { return false; } CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName); - LinkedListener<IGnssMeasurementsListener> linkedListener = new LinkedListener<>(listener, - "GnssMeasurementsListener", callerIdentity, this::removeGnssMeasurementsListener); + LinkedListener<TListener> linkedListener = new LinkedListener<>(listener, + listenerName, callerIdentity, binderDeathCallback); IBinder binder = listener.asBinder(); synchronized (mLock) { if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) { return false; } - mGnssMeasurementsListeners.put(binder, linkedListener); + gnssDataListeners.put(binder, linkedListener); long identity = Binder.clearCallingIdentity(); try { if (isThrottlingExemptLocked(callerIdentity) || isImportanceForeground( mActivityManager.getPackageImportance(packageName))) { - mGnssMeasurementsProvider.addListener(listener, callerIdentity); + gnssDataProvider.addListener(listener, callerIdentity); } return true; } finally { @@ -2789,25 +2768,24 @@ public class LocationManagerService extends ILocationManager.Stub { } } - @Override - public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) { - if (mGnssMeasurementsProvider == null) { + private <TListener extends IInterface> void removeGnssDataListener( + TListener listener, RemoteListenerHelper<TListener> gnssDataProvider, + ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners) { + if (gnssDataProvider == null) { return; } IBinder binder = listener.asBinder(); synchronized (mLock) { - LinkedListener<IGnssMeasurementsListener> linkedListener = - mGnssMeasurementsListeners.remove(binder); + LinkedListener<TListener> linkedListener = gnssDataListeners.remove(binder); if (linkedListener == null) { return; } unlinkFromListenerDeathNotificationLocked(binder, linkedListener); - mGnssMeasurementsProvider.removeListener(listener); + gnssDataProvider.removeListener(listener); } } - private boolean linkToListenerDeathNotificationLocked(IBinder binder, LinkedListenerBase linkedListener) { try { @@ -2863,52 +2841,15 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean addGnssNavigationMessageListener( IGnssNavigationMessageListener listener, String packageName) { - if (!hasGnssPermissions(packageName) || mGnssNavigationMessageProvider == null) { - return false; - } - - CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), - Binder.getCallingPid(), packageName); - LinkedListener<IGnssNavigationMessageListener> linkedListener = - new LinkedListener<>(listener, "GnssNavigationMessageListener", callerIdentity, - this::removeGnssNavigationMessageListener); - IBinder binder = listener.asBinder(); - synchronized (mLock) { - if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) { - return false; - } - - mGnssNavigationMessageListeners.put(binder, linkedListener); - long identity = Binder.clearCallingIdentity(); - try { - if (isThrottlingExemptLocked(callerIdentity) - || isImportanceForeground( - mActivityManager.getPackageImportance(packageName))) { - mGnssNavigationMessageProvider.addListener(listener, callerIdentity); - } - return true; - } finally { - Binder.restoreCallingIdentity(identity); - } - } + return addGnssDataListener(listener, packageName, "GnssNavigationMessageListener", + mGnssNavigationMessageProvider, mGnssNavigationMessageListeners, + this::removeGnssNavigationMessageListener); } @Override public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) { - if (mGnssNavigationMessageProvider == null) { - return; - } - - IBinder binder = listener.asBinder(); - synchronized (mLock) { - LinkedListener<IGnssNavigationMessageListener> linkedListener = - mGnssNavigationMessageListeners.remove(binder); - if (linkedListener == null) { - return; - } - unlinkFromListenerDeathNotificationLocked(binder, linkedListener); - mGnssNavigationMessageProvider.removeListener(listener); - } + removeGnssDataListener(listener, mGnssNavigationMessageProvider, + mGnssNavigationMessageListeners); } @Override diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index f505b76178a4..dc394d0ad482 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -20,18 +20,18 @@ import static android.Manifest.permission.CONNECTIVITY_INTERNAL; import static android.Manifest.permission.NETWORK_SETTINGS; import static android.Manifest.permission.NETWORK_STACK; import static android.Manifest.permission.SHUTDOWN; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; +import static android.net.INetd.FIREWALL_BLACKLIST; +import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; +import static android.net.INetd.FIREWALL_CHAIN_NONE; +import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; +import static android.net.INetd.FIREWALL_CHAIN_STANDBY; +import static android.net.INetd.FIREWALL_RULE_ALLOW; +import static android.net.INetd.FIREWALL_RULE_DENY; +import static android.net.INetd.FIREWALL_WHITELIST; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY; -import static android.net.NetworkPolicyManager.FIREWALL_TYPE_BLACKLIST; -import static android.net.NetworkPolicyManager.FIREWALL_TYPE_WHITELIST; import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_ALL; @@ -1946,7 +1946,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub int numUids = 0; if (DBG) Slog.d(TAG, "Closing sockets after enabling chain " + chainName); - if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) { + if (getFirewallType(chain) == FIREWALL_WHITELIST) { // Close all sockets on all non-system UIDs... ranges = new UidRange[] { // TODO: is there a better way of finding all existing users? If so, we could @@ -1958,7 +1958,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub final SparseIntArray rules = getUidFirewallRulesLR(chain); exemptUids = new int[rules.size()]; for (int i = 0; i < exemptUids.length; i++) { - if (rules.valueAt(i) == NetworkPolicyManager.FIREWALL_RULE_ALLOW) { + if (rules.valueAt(i) == FIREWALL_RULE_ALLOW) { exemptUids[numUids] = rules.keyAt(i); numUids++; } @@ -1980,7 +1980,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub final SparseIntArray rules = getUidFirewallRulesLR(chain); ranges = new UidRange[rules.size()]; for (int i = 0; i < ranges.length; i++) { - if (rules.valueAt(i) == NetworkPolicyManager.FIREWALL_RULE_DENY) { + if (rules.valueAt(i) == FIREWALL_RULE_DENY) { int uid = rules.keyAt(i); ranges[numUids] = new UidRange(uid, uid); numUids++; @@ -2052,13 +2052,13 @@ public class NetworkManagementService extends INetworkManagementService.Stub private int getFirewallType(int chain) { switch (chain) { case FIREWALL_CHAIN_STANDBY: - return FIREWALL_TYPE_BLACKLIST; + return FIREWALL_BLACKLIST; case FIREWALL_CHAIN_DOZABLE: - return FIREWALL_TYPE_WHITELIST; + return FIREWALL_WHITELIST; case FIREWALL_CHAIN_POWERSAVE: - return FIREWALL_TYPE_WHITELIST; + return FIREWALL_WHITELIST; default: - return isFirewallEnabled() ? FIREWALL_TYPE_WHITELIST : FIREWALL_TYPE_BLACKLIST; + return isFirewallEnabled() ? FIREWALL_WHITELIST : FIREWALL_BLACKLIST; } } @@ -2160,14 +2160,14 @@ public class NetworkManagementService extends INetworkManagementService.Stub private @NonNull String getFirewallRuleName(int chain, int rule) { String ruleName; - if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) { - if (rule == NetworkPolicyManager.FIREWALL_RULE_ALLOW) { + if (getFirewallType(chain) == FIREWALL_WHITELIST) { + if (rule == FIREWALL_RULE_ALLOW) { ruleName = "allow"; } else { ruleName = "deny"; } } else { // Blacklist mode - if (rule == NetworkPolicyManager.FIREWALL_RULE_DENY) { + if (rule == FIREWALL_RULE_DENY) { ruleName = "deny"; } else { ruleName = "allow"; @@ -2194,7 +2194,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub private int getFirewallRuleType(int chain, int rule) { if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) { - return getFirewallType(chain) == FIREWALL_TYPE_WHITELIST + return getFirewallType(chain) == FIREWALL_WHITELIST ? INetd.FIREWALL_RULE_DENY : INetd.FIREWALL_RULE_ALLOW; } return rule; diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 2f1510e32311..81209763d3a8 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -26,6 +26,7 @@ import android.content.pm.PackageManager; import android.net.LinkProperties; import android.net.NetworkCapabilities; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -246,7 +247,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private PreciseDataConnectionState mPreciseDataConnectionState = new PreciseDataConnectionState(); - static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = + // Nothing here yet, but putting it here in case we want to add more in the future. + static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = 0; + + static final int ENFORCE_FINE_LOCATION_PERMISSION_MASK = PhoneStateListener.LISTEN_CELL_LOCATION | PhoneStateListener.LISTEN_CELL_INFO; @@ -637,8 +641,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { try { if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]); - r.callback.onServiceStateChanged( - new ServiceState(mServiceState[phoneId])); + ServiceState rawSs = new ServiceState(mServiceState[phoneId]); + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onServiceStateChanged(rawSs); + } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(false)); + } else { + r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(true)); + } } catch (RemoteException ex) { remove(r.binder); } @@ -673,7 +683,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if (DBG_LOC) log("listen: mCellLocation = " + mCellLocation[phoneId]); - if (checkLocationAccess(r)) { + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onCellLocationChanged( new Bundle(mCellLocation[phoneId])); } @@ -722,7 +732,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = " + mCellInfo.get(phoneId)); - if (checkLocationAccess(r)) { + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); } } catch (RemoteException ex) { @@ -1009,13 +1019,22 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SERVICE_STATE) && idMatch(r.subId, subId, phoneId)) { + try { + ServiceState stateToSend; + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + stateToSend = new ServiceState(state); + } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) { + stateToSend = state.sanitizeLocationInfo(false); + } else { + stateToSend = state.sanitizeLocationInfo(true); + } if (DBG) { log("notifyServiceStateForSubscriber: callback.onSSC r=" + r + " subId=" + subId + " phoneId=" + phoneId + " state=" + state); } - r.callback.onServiceStateChanged(new ServiceState(state)); + r.callback.onServiceStateChanged(stateToSend); } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -1198,7 +1217,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) && idMatch(r.subId, subId, phoneId) && - checkLocationAccess(r)) { + checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { try { if (DBG_LOC) { log("notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r); @@ -1500,7 +1519,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) && idMatch(r.subId, subId, phoneId) && - checkLocationAccess(r)) { + checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { try { if (DBG_LOC) { log("notifyCellLocation: cellLocation=" + cellLocation @@ -2108,12 +2127,35 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private boolean checkListenerPermission( int events, int subId, String callingPackage, String message) { + LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder = + new LocationAccessPolicy.LocationPermissionQuery.Builder() + .setCallingPackage(callingPackage) + .setMethod(message + " events: " + events) + .setCallingPid(Binder.getCallingPid()) + .setCallingUid(Binder.getCallingUid()); + + boolean shouldCheckLocationPermissions = false; if ((events & ENFORCE_COARSE_LOCATION_PERMISSION_MASK) != 0) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_COARSE_LOCATION, null); - if (mAppOps.noteOp(AppOpsManager.OP_COARSE_LOCATION, Binder.getCallingUid(), - callingPackage) != AppOpsManager.MODE_ALLOWED) { - return false; + locationQueryBuilder.setMinSdkVersionForCoarse(0); + shouldCheckLocationPermissions = true; + } + + if ((events & ENFORCE_FINE_LOCATION_PERMISSION_MASK) != 0) { + // Everything that requires fine location started in Q. So far... + locationQueryBuilder.setMinSdkVersionForFine(Build.VERSION_CODES.Q); + shouldCheckLocationPermissions = true; + } + + if (shouldCheckLocationPermissions) { + LocationAccessPolicy.LocationPermissionResult result = + LocationAccessPolicy.checkLocationPermission( + mContext, locationQueryBuilder.build()); + switch (result) { + case DENIED_HARD: + throw new SecurityException("Unable to listen for events " + events + " due to " + + "insufficient location permissions."); + case DENIED_SOFT: + return false; } } @@ -2228,15 +2270,38 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - private boolean checkLocationAccess(Record r) { - long token = Binder.clearCallingIdentity(); - try { - return LocationAccessPolicy.canAccessCellLocation(mContext, - r.callingPackage, r.callerUid, r.callerPid, - /*throwOnDeniedPermission*/ false); - } finally { - Binder.restoreCallingIdentity(token); - } + private boolean checkFineLocationAccess(Record r, int minSdk) { + LocationAccessPolicy.LocationPermissionQuery query = + new LocationAccessPolicy.LocationPermissionQuery.Builder() + .setCallingPackage(r.callingPackage) + .setCallingPid(r.callerPid) + .setCallingUid(r.callerUid) + .setMethod("TelephonyRegistry push") + .setMinSdkVersionForFine(minSdk) + .build(); + + return Binder.withCleanCallingIdentity(() -> { + LocationAccessPolicy.LocationPermissionResult locationResult = + LocationAccessPolicy.checkLocationPermission(mContext, query); + return locationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED; + }); + } + + private boolean checkCoarseLocationAccess(Record r, int minSdk) { + LocationAccessPolicy.LocationPermissionQuery query = + new LocationAccessPolicy.LocationPermissionQuery.Builder() + .setCallingPackage(r.callingPackage) + .setCallingPid(r.callerPid) + .setCallingUid(r.callerUid) + .setMethod("TelephonyRegistry push") + .setMinSdkVersionForCoarse(minSdk) + .build(); + + return Binder.withCleanCallingIdentity(() -> { + LocationAccessPolicy.LocationPermissionResult locationResult = + LocationAccessPolicy.checkLocationPermission(mContext, query); + return locationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED; + }); } private void checkPossibleMissNotify(Record r, int phoneId) { @@ -2286,7 +2351,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = " + mCellInfo.get(phoneId)); } - if (checkLocationAccess(r)) { + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); } } catch (RemoteException ex) { @@ -2336,7 +2401,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if (DBG_LOC) log("checkPossibleMissNotify: onCellLocationChanged mCellLocation = " + mCellLocation[phoneId]); - if (checkLocationAccess(r)) { + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId])); } } catch (RemoteException ex) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index c4a9db6b7262..2f20572caa99 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -18100,8 +18100,10 @@ public class ActivityManagerService extends IActivityManager.Stub if (!queue.isIdle()) { final String msg = "Waiting for queue " + queue + " to become idle..."; pw.println(msg); + pw.println(queue.describeState()); pw.flush(); Slog.v(TAG, msg); + queue.cancelDeferrals(); idle = false; } } diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 24543b7974df..236797b57556 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -477,6 +477,10 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { mStats.updateRpmStatsLocked(); } + if ((updateFlags & UPDATE_RAIL) != 0) { + mStats.updateRailStatsLocked(); + } + if (bluetoothInfo != null) { if (bluetoothInfo.isValid()) { mStats.updateBluetoothStateLocked(bluetoothInfo); diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 08900328a200..4d5cb8cb4473 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -59,6 +59,7 @@ import com.android.internal.app.IBatteryStats; import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.PowerProfile; +import com.android.internal.os.RailStats; import com.android.internal.os.RpmStats; import com.android.internal.util.DumpUtils; import com.android.internal.util.ParseUtils; @@ -84,7 +85,8 @@ import java.util.concurrent.Future; */ public final class BatteryStatsService extends IBatteryStats.Stub implements PowerManagerInternal.LowPowerModeListener, - BatteryStatsImpl.PlatformIdleStateCallback { + BatteryStatsImpl.PlatformIdleStateCallback, + BatteryStatsImpl.RailEnergyDataCallback { static final String TAG = "BatteryStatsService"; static final boolean DBG = false; @@ -98,6 +100,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub private native void getLowPowerStats(RpmStats rpmStats); private native int getPlatformLowPowerStats(ByteBuffer outBuffer); private native int getSubsystemLowPowerStats(ByteBuffer outBuffer); + private native void getRailEnergyPowerStats(RailStats railStats); private CharsetDecoder mDecoderStat = StandardCharsets.UTF_8 .newDecoder() .onMalformedInput(CodingErrorAction.REPLACE) @@ -121,6 +124,16 @@ public final class BatteryStatsService extends IBatteryStats.Stub } @Override + public void fillRailDataStats(RailStats railStats) { + if (DBG) Slog.d(TAG, "begin getRailEnergyPowerStats"); + try { + getRailEnergyPowerStats(railStats); + } finally { + if (DBG) Slog.d(TAG, "end getRailEnergyPowerStats"); + } + } + + @Override public String getPlatformLowPowerStats() { if (DBG) Slog.d(TAG, "begin getPlatformLowPowerStats"); try { @@ -177,7 +190,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub return (umi != null) ? umi.getUserIds() : null; } }; - mStats = new BatteryStatsImpl(systemDir, handler, this, mUserManagerUserInfoProvider); + mStats = new BatteryStatsImpl(systemDir, handler, this, + this, mUserManagerUserInfoProvider); mWorker = new BatteryExternalStatsWorker(context, mStats); mStats.setExternalStatsSyncLocked(mWorker); mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger( @@ -1460,7 +1474,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub in.unmarshall(raw, 0, raw.length); in.setDataPosition(0); BatteryStatsImpl checkinStats = new BatteryStatsImpl( - null, mStats.mHandler, null, mUserManagerUserInfoProvider); + null, mStats.mHandler, null, null, + mUserManagerUserInfoProvider); checkinStats.readSummaryFromParcel(in); in.recycle(); checkinStats.dumpProtoLocked( @@ -1498,7 +1513,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub in.unmarshall(raw, 0, raw.length); in.setDataPosition(0); BatteryStatsImpl checkinStats = new BatteryStatsImpl( - null, mStats.mHandler, null, mUserManagerUserInfoProvider); + null, mStats.mHandler, null, null, + mUserManagerUserInfoProvider); checkinStats.readSummaryFromParcel(in); in.recycle(); checkinStats.dumpCheckinLocked(mContext, pw, apps, flags, diff --git a/services/core/java/com/android/server/am/BroadcastDispatcher.java b/services/core/java/com/android/server/am/BroadcastDispatcher.java index 6371cd376d19..0b38ef94de1e 100644 --- a/services/core/java/com/android/server/am/BroadcastDispatcher.java +++ b/services/core/java/com/android/server/am/BroadcastDispatcher.java @@ -65,6 +65,14 @@ public class BroadcastDispatcher { broadcasts.add(br); } + int size() { + return broadcasts.size(); + } + + boolean isEmpty() { + return broadcasts.isEmpty(); + } + void writeToProto(ProtoOutputStream proto, long fieldId) { for (BroadcastRecord br : broadcasts) { br.writeToProto(proto, fieldId); @@ -252,22 +260,48 @@ public class BroadcastDispatcher { synchronized (mLock) { return mCurrentBroadcast == null && mOrderedBroadcasts.isEmpty() - && mDeferredBroadcasts.isEmpty() - && mAlarmBroadcasts.isEmpty(); + && isDeferralsListEmpty(mDeferredBroadcasts) + && isDeferralsListEmpty(mAlarmBroadcasts); + } + } + + private static int pendingInDeferralsList(ArrayList<Deferrals> list) { + int pending = 0; + final int numEntries = list.size(); + for (int i = 0; i < numEntries; i++) { + pending += list.get(i).size(); } + return pending; + } + + private static boolean isDeferralsListEmpty(ArrayList<Deferrals> list) { + return pendingInDeferralsList(list) == 0; } /** - * Not quite the traditional size() measurement; includes any in-process but - * not yet retired active outbound broadcast. + * Strictly for logging, describe the currently pending contents in a human- + * readable way */ - public int totalUndelivered() { - synchronized (mLock) { - return mAlarmBroadcasts.size() - + mDeferredBroadcasts.size() - + mOrderedBroadcasts.size() - + (mCurrentBroadcast == null ? 0 : 1); - } + public String describeStateLocked() { + final StringBuilder sb = new StringBuilder(128); + if (mCurrentBroadcast != null) { + sb.append("1 in flight, "); + } + sb.append(mOrderedBroadcasts.size()); + sb.append(" ordered"); + int n = pendingInDeferralsList(mAlarmBroadcasts); + if (n > 0) { + sb.append(", "); + sb.append(n); + sb.append(" deferrals in alarm recipients"); + } + n = pendingInDeferralsList(mDeferredBroadcasts); + if (n > 0) { + sb.append(", "); + sb.append(n); + sb.append(" deferred"); + } + return sb.toString(); } // ---------------------------------- @@ -579,6 +613,26 @@ public class BroadcastDispatcher { } } + /** + * Cancel all current deferrals; that is, make all currently-deferred broadcasts + * immediately deliverable. Used by the wait-for-broadcast-idle mechanism. + */ + public void cancelDeferrals() { + synchronized (mLock) { + zeroDeferralTimes(mAlarmBroadcasts); + zeroDeferralTimes(mDeferredBroadcasts); + } + } + + private static void zeroDeferralTimes(ArrayList<Deferrals> list) { + final int num = list.size(); + for (int i = 0; i < num; i++) { + Deferrals d = list.get(i); + // Safe to do this in-place because it won't break ordering + d.deferUntil = d.deferredBy = 0; + } + } + // ---------------------------------- /** diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index f0b137a6ccb1..d9ea1da79f56 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -898,6 +898,11 @@ public final class BroadcastQueue { for (int i = perms.length-1; i >= 0; i--) { try { PermissionInfo pi = pm.getPermissionInfo(perms[i], "android", 0); + if (pi == null) { + // a required permission that no package has actually + // defined cannot be signature-required. + return false; + } if ((pi.protectionLevel & (PermissionInfo.PROTECTION_MASK_BASE | PermissionInfo.PROTECTION_FLAG_PRIVILEGED)) != PermissionInfo.PROTECTION_SIGNATURE) { @@ -923,8 +928,8 @@ public final class BroadcastQueue { if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast [" + mQueueName + "]: " - + mParallelBroadcasts.size() + " parallel broadcasts, " - + mDispatcher.totalUndelivered() + " ordered broadcasts"); + + mParallelBroadcasts.size() + " parallel broadcasts; " + + mDispatcher.describeStateLocked()); mService.updateCpuStats(); @@ -1822,11 +1827,24 @@ public final class BroadcastQueue { record.intent == null ? "" : record.intent.getAction()); } - final boolean isIdle() { + boolean isIdle() { return mParallelBroadcasts.isEmpty() && mDispatcher.isEmpty() && (mPendingBroadcast == null); } + // Used by wait-for-broadcast-idle : fast-forward all current deferrals to + // be immediately deliverable. + void cancelDeferrals() { + mDispatcher.cancelDeferrals(); + } + + String describeState() { + synchronized (mService) { + return mParallelBroadcasts.size() + " parallel; " + + mDispatcher.describeStateLocked(); + } + } + void writeToProto(ProtoOutputStream proto, long fieldId) { long token = proto.start(fieldId); proto.write(BroadcastQueueProto.QUEUE_NAME, mQueueName); diff --git a/services/core/java/com/android/server/appbinding/AppBindingService.java b/services/core/java/com/android/server/appbinding/AppBindingService.java index 0b6a4329d15b..bbe4ed15b3a0 100644 --- a/services/core/java/com/android/server/appbinding/AppBindingService.java +++ b/services/core/java/com/android/server/appbinding/AppBindingService.java @@ -177,13 +177,12 @@ public class AppBindingService extends Binder { * Handle boot phase PHASE_ACTIVITY_MANAGER_READY. */ private void onPhaseActivityManagerReady() { + // RoleManager doesn't tell us about upgrade, so we still need to listen for app upgrades. + // (app uninstall/disable will be notified by RoleManager.) final IntentFilter packageFilter = new IntentFilter(); packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); - packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); - packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); packageFilter.addDataScheme("package"); - packageFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiverAsUser(mPackageUserMonitor, UserHandle.ALL, packageFilter, null, mHandler); @@ -256,14 +255,6 @@ public class AppBindingService extends Binder { handlePackageAddedReplacing(packageName, userId); } break; - case Intent.ACTION_PACKAGE_REMOVED: - if (!replacing) { - handlePackageRemoved(packageName, userId); - } - break; - case Intent.ACTION_PACKAGE_CHANGED: - handlePackageChanged(packageName, userId); - break; } } }; @@ -371,31 +362,6 @@ public class AppBindingService extends Binder { } } - private void handlePackageRemoved(String packageName, int userId) { - if (DEBUG) { - Slog.d(TAG, "handlePackageRemoved: u" + userId + " " + packageName); - } - synchronized (mLock) { - final AppServiceFinder finder = findFinderLocked(userId, packageName); - if (finder != null) { - unbindServicesLocked(userId, finder, "package uninstall"); - } - } - } - - private void handlePackageChanged(String packageName, int userId) { - if (DEBUG) { - Slog.d(TAG, "handlePackageChanged: u" + userId + " " + packageName); - } - synchronized (mLock) { - final AppServiceFinder finder = findFinderLocked(userId, packageName); - if (finder != null) { - unbindServicesLocked(userId, finder, "package changed"); - bindServicesLocked(userId, finder, "package changed"); - } - } - } - private void rebindAllLocked(String reason) { for (int i = 0; i < mRunningUsers.size(); i++) { if (!mRunningUsers.valueAt(i)) { diff --git a/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java index 4c5f1a1c7b49..753d3b0cc10e 100644 --- a/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java +++ b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java @@ -16,14 +16,10 @@ package com.android.server.appbinding.finders; -import static android.provider.Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL; - import android.Manifest.permission; -import android.content.BroadcastReceiver; -import android.content.ComponentName; +import android.app.role.OnRoleHoldersChangedListener; +import android.app.role.RoleManager; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.ServiceInfo; import android.os.Handler; import android.os.IBinder; @@ -35,7 +31,8 @@ import android.text.TextUtils; import android.util.Slog; import com.android.internal.R; -import com.android.internal.telephony.SmsApplication; +import com.android.internal.os.BackgroundThread; +import com.android.internal.util.CollectionUtils; import com.android.server.appbinding.AppBindingConstants; import java.util.function.BiConsumer; @@ -45,10 +42,15 @@ import java.util.function.BiConsumer; */ public class CarrierMessagingClientServiceFinder extends AppServiceFinder<CarrierMessagingClientService, ICarrierMessagingClientService> { + + private final RoleManager mRoleManager; + public CarrierMessagingClientServiceFinder(Context context, BiConsumer<AppServiceFinder, Integer> listener, Handler callbackHandler) { super(context, listener, callbackHandler); + + mRoleManager = context.getSystemService(RoleManager.class); } @Override @@ -84,9 +86,8 @@ public class CarrierMessagingClientServiceFinder @Override public String getTargetPackage(int userId) { - final ComponentName cn = SmsApplication.getDefaultSmsApplicationAsUser( - mContext, /* updateIfNeeded= */ true, userId); - String ret = cn == null ? null : cn.getPackageName(); + final String ret = CollectionUtils.firstOrNull(mRoleManager.getRoleHoldersAsUser( + RoleManager.ROLE_SMS, UserHandle.of(userId))); if (DEBUG) { Slog.d(TAG, "getTargetPackage()=" + ret); @@ -97,9 +98,8 @@ public class CarrierMessagingClientServiceFinder @Override public void startMonitoring() { - final IntentFilter filter = new IntentFilter(ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL); - mContext.registerReceiverAsUser(mSmsAppChangedWatcher, UserHandle.ALL, filter, - /* permission= */ null, mHandler); + mRoleManager.addOnRoleHoldersChangedListenerAsUser( + mContext.getMainExecutor(), mRoleHolderChangedListener, UserHandle.ALL); } @Override @@ -118,12 +118,11 @@ public class CarrierMessagingClientServiceFinder return constants.SMS_APP_BIND_FLAGS; } - private final BroadcastReceiver mSmsAppChangedWatcher = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL.equals(intent.getAction())) { - mListener.accept(CarrierMessagingClientServiceFinder.this, getSendingUserId()); - } + private final OnRoleHoldersChangedListener mRoleHolderChangedListener = (role, user) -> { + if (RoleManager.ROLE_SMS.equals(role)) { + BackgroundThread.getHandler().post(() -> { + mListener.accept(CarrierMessagingClientServiceFinder.this, user.getIdentifier()); + }); } }; } diff --git a/services/core/java/com/android/server/gpu/GpuService.java b/services/core/java/com/android/server/gpu/GpuService.java index a68ceed750ad..6899c3ffcbb1 100644 --- a/services/core/java/com/android/server/gpu/GpuService.java +++ b/services/core/java/com/android/server/gpu/GpuService.java @@ -22,24 +22,33 @@ import static android.content.Intent.ACTION_PACKAGE_REMOVED; import android.annotation.NonNull; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.database.ContentObserver; +import android.gamedriver.GameDriverProto.Blacklist; +import android.gamedriver.GameDriverProto.Blacklists; import android.net.Uri; import android.os.Build; +import android.os.Handler; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; +import android.util.Base64; import android.util.Slog; +import com.android.framework.protobuf.InvalidProtocolBufferException; +import com.android.internal.annotations.GuardedBy; import com.android.server.SystemService; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.List; /** * Service to manage GPU related features. @@ -52,17 +61,25 @@ public class GpuService extends SystemService { public static final boolean DEBUG = false; private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0"; - private static final String WHITELIST_FILENAME = "whitelist.txt"; + private static final String GAME_DRIVER_WHITELIST_FILENAME = "whitelist.txt"; + private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP; private final Context mContext; private final String mDriverPackageName; private final PackageManager mPackageManager; + private final Object mLock = new Object(); + private ContentResolver mContentResolver; + private long mGameDriverVersionCode; + private SettingsObserver mSettingsObserver; + @GuardedBy("mLock") + private Blacklists mBlacklists; public GpuService(Context context) { super(context); mContext = context; mDriverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER); + mGameDriverVersionCode = -1; mPackageManager = context.getPackageManager(); if (mDriverPackageName != null && !mDriverPackageName.isEmpty()) { final IntentFilter packageFilter = new IntentFilter(); @@ -82,10 +99,37 @@ public class GpuService extends SystemService { @Override public void onBootPhase(int phase) { if (phase == PHASE_BOOT_COMPLETED) { + mContentResolver = mContext.getContentResolver(); + mSettingsObserver = new SettingsObserver(); if (mDriverPackageName == null || mDriverPackageName.isEmpty()) { return; } fetchGameDriverPackageProperties(); + processBlacklists(); + setBlacklist(); + } + } + + private final class SettingsObserver extends ContentObserver { + private final Uri mGameDriverBlackUri = + Settings.Global.getUriFor(Settings.Global.GAME_DRIVER_BLACKLISTS); + + SettingsObserver() { + super(new Handler()); + mContentResolver.registerContentObserver(mGameDriverBlackUri, false, this, + UserHandle.USER_ALL); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + if (uri == null) { + return; + } + + if (mGameDriverBlackUri.equals(uri)) { + processBlacklists(); + setBlacklist(); + } } } @@ -109,6 +153,7 @@ public class GpuService extends SystemService { case ACTION_PACKAGE_CHANGED: case ACTION_PACKAGE_REMOVED: fetchGameDriverPackageProperties(); + setBlacklist(); break; default: // do nothing @@ -138,16 +183,22 @@ public class GpuService extends SystemService { return; } + // Reset the whitelist. + Settings.Global.putString(mContentResolver, + Settings.Global.GAME_DRIVER_WHITELIST, ""); + mGameDriverVersionCode = driverInfo.longVersionCode; + try { final Context driverContext = mContext.createPackageContext(mDriverPackageName, Context.CONTEXT_RESTRICTED); final BufferedReader reader = new BufferedReader( - new InputStreamReader(driverContext.getAssets().open(WHITELIST_FILENAME))); + new InputStreamReader(driverContext.getAssets() + .open(GAME_DRIVER_WHITELIST_FILENAME))); final ArrayList<String> whitelistedPackageNames = new ArrayList<>(); for (String packageName; (packageName = reader.readLine()) != null; ) { whitelistedPackageNames.add(packageName); } - Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.putString(mContentResolver, Settings.Global.GAME_DRIVER_WHITELIST, String.join(",", whitelistedPackageNames)); } catch (PackageManager.NameNotFoundException e) { @@ -160,4 +211,48 @@ public class GpuService extends SystemService { } } } + + private void processBlacklists() { + // TODO(b/121350991) Switch to DeviceConfig with property listener. + String base64String = + Settings.Global.getString(mContentResolver, Settings.Global.GAME_DRIVER_BLACKLISTS); + if (base64String == null || base64String.isEmpty()) { + return; + } + + synchronized (mLock) { + // Reset all blacklists + mBlacklists = null; + try { + mBlacklists = Blacklists.parseFrom(Base64.decode(base64String, BASE64_FLAGS)); + } catch (IllegalArgumentException e) { + if (DEBUG) { + Slog.w(TAG, "Can't parse blacklist, skip and continue..."); + } + } catch (InvalidProtocolBufferException e) { + if (DEBUG) { + Slog.w(TAG, "Can't parse blacklist, skip and continue..."); + } + } + } + } + + private void setBlacklist() { + Settings.Global.putString(mContentResolver, + Settings.Global.GAME_DRIVER_BLACKLIST, ""); + synchronized (mLock) { + if (mBlacklists == null) { + return; + } + List<Blacklist> blacklists = mBlacklists.getBlacklistsList(); + for (Blacklist blacklist : blacklists) { + if (blacklist.getVersionCode() == mGameDriverVersionCode) { + Settings.Global.putString(mContentResolver, + Settings.Global.GAME_DRIVER_BLACKLIST, + String.join(",", blacklist.getPackageNamesList())); + return; + } + } + } + } } diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 3abacc2c9c10..e71b156c3e86 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -917,7 +917,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements synchronized (mLock) { boolean enabled = ((mProviderRequest != null && mProviderRequest.reportLocation - && mProviderRequest.forceLocation) || ( + && mProviderRequest.locationSettingsIgnored) || ( mContext.getSystemService(LocationManager.class).isLocationEnabled() && !mDisableGps)) && !mShutdown; if (enabled == mEnabled) { diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index b6ef180f4b59..b221241c25e2 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -1207,6 +1207,15 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } } + public void setPlaybackSpeed(String packageName, int pid, int uid, + ControllerCallbackLink caller, float speed) { + try { + mCb.notifySetPlaybackSpeed(packageName, pid, uid, caller, speed); + } catch (RuntimeException e) { + Slog.e(TAG, "Remote failure in setPlaybackSpeed.", e); + } + } + public void adjustVolume(String packageName, int pid, int uid, ControllerCallbackLink caller, boolean asSystemService, int direction) { try { @@ -1446,6 +1455,13 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override + public void setPlaybackSpeed(String packageName, ControllerCallbackLink caller, + float speed) { + mSessionCb.setPlaybackSpeed(packageName, Binder.getCallingPid(), Binder.getCallingUid(), + caller, speed); + } + + @Override public void sendCustomAction(String packageName, ControllerCallbackLink caller, String action, Bundle args) { mSessionCb.sendCustomAction(packageName, Binder.getCallingPid(), Binder.getCallingUid(), diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java index 9e5b92a6b944..3f15b381c18b 100644 --- a/services/core/java/com/android/server/net/LockdownVpnTracker.java +++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java @@ -17,9 +17,6 @@ package com.android.server.net; import static android.Manifest.permission.CONNECTIVITY_INTERNAL; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.provider.Settings.ACTION_VPN_SETTINGS; import android.app.Notification; @@ -30,17 +27,14 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; -import android.net.LinkProperties; import android.net.LinkAddress; +import android.net.LinkProperties; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkInfo.State; -import android.net.NetworkPolicyManager; import android.os.INetworkManagementService; -import android.os.RemoteException; import android.security.Credentials; import android.security.KeyStore; -import android.system.Os; import android.text.TextUtils; import android.util.Slog; diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index 4bd8f450c76b..6d82c1c257ab 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -15,15 +15,15 @@ */ package com.android.server.net; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; +import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; +import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; +import static android.net.INetd.FIREWALL_CHAIN_STANDBY; +import static android.net.INetd.FIREWALL_RULE_ALLOW; +import static android.net.INetd.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY; import android.app.ActivityManager; import android.net.NetworkPolicyManager; diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index af55605975ca..75b62cb349af 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -38,6 +38,11 @@ import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLE import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED; import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED; import static android.net.ConnectivityManager.TYPE_MOBILE; +import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; +import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; +import static android.net.INetd.FIREWALL_CHAIN_STANDBY; +import static android.net.INetd.FIREWALL_RULE_ALLOW; +import static android.net.INetd.FIREWALL_RULE_DENY; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; @@ -45,12 +50,7 @@ import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.SNOOZE_NEVER; import static android.net.NetworkPolicy.WARNING_DISABLED; import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS; import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS; import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 400443a16fab..61a1a2f81101 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -985,6 +985,9 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mPackages") private PackageManagerInternal.DefaultBrowserProvider mDefaultBrowserProvider; + @GuardedBy("mPackages") + private PackageManagerInternal.DefaultHomeProvider mDefaultHomeProvider; + private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> { private Context mContext; private ComponentName mIntentFilterVerifierComponent; @@ -1349,7 +1352,7 @@ public class PackageManagerService extends IPackageManager.Stub final @Nullable String mRequiredVerifierPackage; final @NonNull String mRequiredInstallerPackage; final @NonNull String mRequiredUninstallerPackage; - final String mRequiredPermissionControllerPackage; + final @NonNull String mRequiredPermissionControllerPackage; final @Nullable String mSetupWizardPackage; final @Nullable String mStorageManagerPackage; final @Nullable String mSystemTextClassifierPackage; @@ -1940,6 +1943,10 @@ public class PackageManagerService extends IPackageManager.Stub // We may also need to apply pending (restored) runtime // permission grants within these users. mSettings.applyPendingPermissionGrantsLPw(packageName, userId); + + // Persistent preferred activity might have came into effect due to this + // install. + updateDefaultHomeLPw(userId); } } } @@ -3306,7 +3313,8 @@ public class PackageManagerService extends IPackageManager.Stub // feature flags should cause us to invalidate any caches. final String cacheName = SystemProperties.digestOf( "ro.build.fingerprint", - "persist.sys.isolated_storage"); + StorageManager.PROP_ISOLATED_STORAGE, + StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT); // Reconcile cache directories, keeping only what we'd actually use. for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) { @@ -12803,7 +12811,7 @@ public class PackageManagerService extends IPackageManager.Stub public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames, int restrictionFlags, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS, - "setPackagesSuspendedAsUser"); + "setDistractingPackageRestrictionsAsUser"); final int callingUid = Binder.getCallingUid(); if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID @@ -19076,6 +19084,7 @@ public class PackageManagerService extends IPackageManager.Stub pir.addFilter(new PreferredActivity(filter, match, set, activity, always)); scheduleWritePackageRestrictionsLocked(userId); postPreferredActivityChangedBroadcast(userId); + updateDefaultHomeLPw(userId); } } @@ -19226,6 +19235,13 @@ public class PackageManagerService extends IPackageManager.Stub /** This method takes a specific user id as well as UserHandle.USER_ALL. */ @GuardedBy("mPackages") boolean clearPackagePreferredActivitiesLPw(String packageName, int userId) { + return clearPackagePreferredActivitiesLPw(packageName, false, userId); + } + + /** This method takes a specific user id as well as UserHandle.USER_ALL. */ + @GuardedBy("mPackages") + private boolean clearPackagePreferredActivitiesLPw(String packageName, + boolean skipUpdateDefaultHome, int userId) { ArrayList<PreferredActivity> removed = null; boolean changed = false; for (int i=0; i<mSettings.mPreferredActivities.size(); i++) { @@ -19254,6 +19270,9 @@ public class PackageManagerService extends IPackageManager.Stub pir.removeFilter(pa); } changed = true; + if (!skipUpdateDefaultHome) { + updateDefaultHomeLPw(thisUserId); + } } } if (changed) { @@ -19313,8 +19332,9 @@ public class PackageManagerService extends IPackageManager.Stub // writer try { synchronized (mPackages) { - clearPackagePreferredActivitiesLPw(null, userId); + clearPackagePreferredActivitiesLPw(null, true, userId); mSettings.applyDefaultPreferredAppsLPw(userId); + updateDefaultHomeLPw(userId); // TODO: We have to reset the default SMS and Phone. This requires // significant refactoring to keep all default apps in the package // manager (cleaner but more work) or have the services provide @@ -19383,6 +19403,7 @@ public class PackageManagerService extends IPackageManager.Stub new PersistentPreferredActivity(filter, activity)); scheduleWritePackageRestrictionsLocked(userId); postPreferredActivityChangedBroadcast(userId); + updateDefaultHomeLPw(userId); } } @@ -19426,6 +19447,7 @@ public class PackageManagerService extends IPackageManager.Stub if (changed) { scheduleWritePackageRestrictionsLocked(userId); postPreferredActivityChangedBroadcast(userId); + updateDefaultHomeLPw(userId); } } } @@ -19513,6 +19535,7 @@ public class PackageManagerService extends IPackageManager.Stub (readParser, readUserId) -> { synchronized (mPackages) { mSettings.readPreferredActivitiesLPw(readParser, readUserId); + updateDefaultHomeLPw(readUserId); } }); } catch (Exception e) { @@ -19568,8 +19591,17 @@ public class PackageManagerService extends IPackageManager.Stub parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name()); restoreFromXml(parser, userId, TAG_DEFAULT_APPS, (parser1, userId1) -> { + String defaultBrowser; synchronized (mPackages) { mSettings.readDefaultAppsLPw(parser1, userId1); + defaultBrowser = mSettings.removeDefaultBrowserPackageNameLPw(userId1); + } + if (defaultBrowser != null) { + PackageManagerInternal.DefaultBrowserProvider provider; + synchronized (mPackages) { + provider = mDefaultBrowserProvider; + } + provider.setDefaultBrowser(defaultBrowser, userId1); } }); } catch (Exception e) { @@ -19927,19 +19959,59 @@ public class PackageManagerService extends IPackageManager.Stub ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates, int userId) { Intent intent = getHomeIntent(); - List<ResolveInfo> list = queryIntentActivitiesInternal(intent, null, + List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null, PackageManager.GET_META_DATA, userId); - ResolveInfo preferred = findPreferredActivity(intent, null, 0, list, 0, - true, false, false, userId); - allHomeCandidates.clear(); - if (list != null) { - allHomeCandidates.addAll(list); + if (resolveInfos == null) { + return null; + } + allHomeCandidates.addAll(resolveInfos); + + PackageManagerInternal.DefaultHomeProvider provider; + synchronized (mPackages) { + provider = mDefaultHomeProvider; + } + if (provider == null) { + Slog.e(TAG, "mDefaultHomeProvider is null"); + return null; } - return (preferred == null || preferred.activityInfo == null) - ? null - : new ComponentName(preferred.activityInfo.packageName, - preferred.activityInfo.name); + String packageName = provider.getDefaultHome(userId); + if (packageName == null) { + return null; + } + int resolveInfosSize = resolveInfos.size(); + for (int i = 0; i < resolveInfosSize; i++) { + ResolveInfo resolveInfo = resolveInfos.get(i); + + if (resolveInfo.activityInfo != null && TextUtils.equals( + resolveInfo.activityInfo.packageName, packageName)) { + return new ComponentName(resolveInfo.activityInfo.packageName, + resolveInfo.activityInfo.name); + } + } + return null; + } + + private void updateDefaultHomeLPw(int userId) { + Intent intent = getHomeIntent(); + List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null, + PackageManager.GET_META_DATA, userId); + ResolveInfo preferredResolveInfo = findPreferredActivity(intent, null, 0, resolveInfos, + 0, true, false, false, userId); + String packageName = preferredResolveInfo != null + && preferredResolveInfo.activityInfo != null + ? preferredResolveInfo.activityInfo.packageName : null; + String currentPackageName = mDefaultHomeProvider.getDefaultHome(userId); + if (TextUtils.equals(currentPackageName, packageName)) { + return; + } + String[] callingPackages = getPackagesForUid(Binder.getCallingUid()); + if (callingPackages != null && ArrayUtils.contains(callingPackages, + mRequiredPermissionControllerPackage)) { + // PermissionController manages default home directly. + return; + } + mDefaultHomeProvider.setDefaultHomeAsync(packageName, userId); } @Override @@ -23829,6 +23901,13 @@ public class PackageManagerService extends IPackageManager.Stub mDefaultBrowserProvider = provider; } } + + @Override + public void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider) { + synchronized (mPackages) { + mDefaultHomeProvider = provider; + } + } } @GuardedBy("mPackages") diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 30c2281b07f1..6c212d63d77c 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -156,12 +156,8 @@ public class StagingManager { boolean success = true; // STOPSHIP: TODO(b/123753157): Verify APKs through Package Verifier. - if (!sessionContainsApex(session)) { - // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs, - // right away. - session.setStagedSessionReady(); - return; - } + // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs, + // right away. final ApexInfoList apexInfoList = new ApexInfoList(); // APEX checks. For single-package sessions, check if they contain an APEX. For @@ -227,7 +223,8 @@ public class StagingManager { } session.setStagedSessionReady(); - if (!mApexManager.markStagedSessionReady(session.sessionId)) { + if (sessionContainsApex(session) + && !mApexManager.markStagedSessionReady(session.sessionId)) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, "APEX staging failed, check logcat messages from apexd for more " + "details."); 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 2036ed73fdf2..bd577598a617 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -721,7 +721,7 @@ public final class DefaultPermissionGrantPolicy { grantSystemFixedPermissionsToSystemPackage( getDefaultSystemHandlerActivityPackage( RingtoneManager.ACTION_RINGTONE_PICKER, userId), - userId, STORAGE_PERMISSIONS); + userId, STORAGE_PERMISSIONS, MEDIA_AURAL_PERMISSIONS); // TextClassifier Service String textClassifierPackageName = diff --git a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java index 3534cf30e2bf..888dd9992bdf 100644 --- a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java +++ b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java @@ -17,10 +17,13 @@ package com.android.server.policy.role; import android.annotation.NonNull; +import android.annotation.UserIdInt; import android.app.role.RoleManager; import android.content.ComponentName; import android.content.Context; +import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; +import android.content.pm.ResolveInfo; import android.os.Debug; import android.provider.Settings; import android.telecom.TelecomManager; @@ -33,6 +36,7 @@ import com.android.internal.util.CollectionUtils; import com.android.server.LocalServices; import com.android.server.role.RoleManagerService; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -54,19 +58,44 @@ public class LegacyRoleResolutionPolicy implements RoleManagerService.RoleHolder @NonNull private final Context mContext; - public LegacyRoleResolutionPolicy(Context context) { + public LegacyRoleResolutionPolicy(@NonNull Context context) { mContext = context; } + @NonNull @Override - public List<String> getRoleHolders(String roleName, int userId) { + public List<String> getRoleHolders(@NonNull String roleName, @UserIdInt int userId) { switch (roleName) { + case RoleManager.ROLE_ASSISTANT: { + String legacyAssistant = Settings.Secure.getStringForUser( + mContext.getContentResolver(), Settings.Secure.ASSISTANT, userId); + if (legacyAssistant == null || legacyAssistant.isEmpty()) { + return Collections.emptyList(); + } else { + return Collections.singletonList( + ComponentName.unflattenFromString(legacyAssistant).getPackageName()); + } + } + case RoleManager.ROLE_BROWSER: { + PackageManagerInternal packageManagerInternal = LocalServices.getService( + PackageManagerInternal.class); + String packageName = packageManagerInternal.removeLegacyDefaultBrowserPackageName( + userId); + return CollectionUtils.singletonOrEmpty(packageName); + } + case RoleManager.ROLE_DIALER: { + String setting = Settings.Secure.getStringForUser( + mContext.getContentResolver(), + Settings.Secure.DIALER_DEFAULT_APPLICATION, userId); + return CollectionUtils.singletonOrEmpty(!TextUtils.isEmpty(setting) + ? setting + : mContext.getSystemService(TelecomManager.class).getSystemDialerPackage()); + } case RoleManager.ROLE_SMS: { // Moved over from SmsApplication#getApplication String result = Settings.Secure.getStringForUser( mContext.getContentResolver(), Settings.Secure.SMS_DEFAULT_APPLICATION, userId); - // TODO: STOPSHIP: Remove the following code once we read the value of // config_defaultSms in RoleControllerService. if (result == null) { @@ -92,34 +121,13 @@ public class LegacyRoleResolutionPolicy implements RoleManagerService.RoleHolder SmsApplication.SmsApplicationData app = applicationData; result = app == null ? null : app.mPackageName; } - return CollectionUtils.singletonOrEmpty(result); } - case RoleManager.ROLE_ASSISTANT: { - String legacyAssistant = Settings.Secure.getStringForUser( - mContext.getContentResolver(), Settings.Secure.ASSISTANT, userId); - - if (legacyAssistant == null || legacyAssistant.isEmpty()) { - return Collections.emptyList(); - } else { - return Collections.singletonList( - ComponentName.unflattenFromString(legacyAssistant).getPackageName()); - } - } - case RoleManager.ROLE_DIALER: { - String setting = Settings.Secure.getStringForUser( - mContext.getContentResolver(), - Settings.Secure.DIALER_DEFAULT_APPLICATION, userId); - - return CollectionUtils.singletonOrEmpty(!TextUtils.isEmpty(setting) - ? setting - : mContext.getSystemService(TelecomManager.class).getSystemDialerPackage()); - } - case RoleManager.ROLE_BROWSER: { - PackageManagerInternal packageManagerInternal = LocalServices.getService( - PackageManagerInternal.class); - String packageName = packageManagerInternal.removeLegacyDefaultBrowserPackageName( - userId); + case RoleManager.ROLE_HOME: { + PackageManager packageManager = mContext.getPackageManager(); + List<ResolveInfo> resolveInfos = new ArrayList<>(); + ComponentName componentName = packageManager.getHomeActivities(resolveInfos); + String packageName = componentName != null ? componentName.getPackageName() : null; return CollectionUtils.singletonOrEmpty(packageName); } default: { diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java index 4186154016e2..8740256af04d 100644 --- a/services/core/java/com/android/server/power/AttentionDetector.java +++ b/services/core/java/com/android/server/power/AttentionDetector.java @@ -123,6 +123,9 @@ public class AttentionDetector { public AttentionDetector(Runnable onUserAttention, Object lock) { mOnUserAttention = onUserAttention; mLock = lock; + + // Device starts with an awake state upon boot. + mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; } public void systemReady(Context context) { @@ -145,7 +148,7 @@ public class AttentionDetector { if (DEBUG) { Slog.d(TAG, "Do not check for attention yet, wait " + (whenToCheck - now)); } - return nextScreenDimming; + return whenToCheck; } else if (whenToStopExtending < whenToCheck) { if (DEBUG) { Slog.d(TAG, "Let device sleep to avoid false results and improve security " diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index 21bf9de5c8b0..d8531210cad8 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -111,7 +111,8 @@ public class RoleManagerService extends SystemService implements RoleUserState.C /** @see #getRoleHolders(String, int) */ public interface RoleHoldersResolver { /** @return a list of packages that hold a given role for a given user */ - List<String> getRoleHolders(String roleName, int userId); + @NonNull + List<String> getRoleHolders(@NonNull String roleName, @UserIdInt int userId); } /** @@ -154,6 +155,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C PackageManagerInternal packageManagerInternal = LocalServices.getService( PackageManagerInternal.class); packageManagerInternal.setDefaultBrowserProvider(new DefaultBrowserProvider()); + packageManagerInternal.setDefaultHomeProvider(new DefaultHomeProvider()); registerUserRemovedReceiver(); } @@ -741,4 +743,33 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } } } + + private class DefaultHomeProvider implements PackageManagerInternal.DefaultHomeProvider { + + @Nullable + @Override + public String getDefaultHome(@UserIdInt int userId) { + return CollectionUtils.firstOrNull(getOrCreateUserState(userId).getRoleHolders( + RoleManager.ROLE_HOME)); + } + + @Override + public void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId) { + IRoleManagerCallback callback = new IRoleManagerCallback.Stub() { + @Override + public void onSuccess() {} + @Override + public void onFailure() { + Slog.e(LOG_TAG, "Failed to set default home: " + packageName); + } + }; + if (packageName != null) { + getOrCreateControllerService(userId).onAddRoleHolder(RoleManager.ROLE_HOME, + packageName, 0, callback); + } else { + getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_HOME, 0, + callback); + } + } + } } diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 95c3f4c43313..ceaf829290d7 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -40,6 +40,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.ParcelFileDescriptor; import android.os.Process; +import android.provider.DeviceConfig; import android.util.IntArray; import android.util.Log; import android.util.SparseBooleanArray; @@ -61,6 +62,7 @@ import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; /** * Implementation of service that manages APK level rollbacks. @@ -71,13 +73,19 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // Rollbacks expire after 48 hours. // TODO: How to test rollback expiration works properly? - private static final long ROLLBACK_LIFETIME_DURATION_MILLIS = 48 * 60 * 60 * 1000; + private static final long DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS = + TimeUnit.HOURS.toMillis(48); // Lock used to synchronize accesses to in-memory rollback data // structures. By convention, methods with the suffix "Locked" require // mLock is held when they are called. private final Object mLock = new Object(); + // No need for guarding with lock because value is only accessed in handler thread + // and the value will be written on boot complete. Initialization here happens before + // handler threads are running so that's fine. + private long mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS; + // Used for generating rollback IDs. private final Random mRandom = new SecureRandom(); @@ -484,7 +492,25 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { }); } + private void updateRollbackLifetimeDurationInMillis() { + String strRollbackLifetimeInMillis = DeviceConfig.getProperty( + DeviceConfig.Rollback.BOOT_NAMESPACE, + DeviceConfig.Rollback.ROLLBACK_LIFETIME_IN_MILLIS); + + try { + mRollbackLifetimeDurationInMillis = (strRollbackLifetimeInMillis == null) + ? DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS + : Long.parseLong(strRollbackLifetimeInMillis); + } catch (NumberFormatException e) { + mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS; + } + } + void onBootCompleted() { + getHandler().post(() -> updateRollbackLifetimeDurationInMillis()); + // Also posts to handler thread + scheduleExpiration(0); + getHandler().post(() -> { // Check to see if any staged sessions with rollback enabled have // been applied. @@ -565,8 +591,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { for (RollbackInfo info : mRecentlyExecutedRollbacks) { mAllocatedRollbackIds.put(info.getRollbackId(), true); } - - scheduleExpiration(0); } /** @@ -700,8 +724,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { if (!data.isAvailable) { continue; } - - if (!now.isBefore(data.timestamp.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS))) { + if (!now.isBefore(data.timestamp.plusMillis(mRollbackLifetimeDurationInMillis))) { iter.remove(); deleteRollback(data); } else if (oldest == null || oldest.isAfter(data.timestamp)) { @@ -711,7 +734,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } if (oldest != null) { - scheduleExpiration(now.until(oldest.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS), + scheduleExpiration(now.until(oldest.plusMillis(mRollbackLifetimeDurationInMillis), ChronoUnit.MILLIS)); } } @@ -1144,8 +1167,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { packages.add(data.packages.get(i).getPackageName()); } mPackageHealthObserver.startObservingHealth(packages, - ROLLBACK_LIFETIME_DURATION_MILLIS); - scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS); + mRollbackLifetimeDurationInMillis); + scheduleExpiration(mRollbackLifetimeDurationInMillis); } catch (IOException e) { Log.e(TAG, "Unable to enable rollback", e); deleteRollback(data); diff --git a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java index ff01d46e6909..b12129835ca1 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java +++ b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java @@ -19,6 +19,7 @@ import static android.app.StatusBarManager.DEFAULT_SETUP_DISABLE_FLAGS; import static android.app.StatusBarManager.DISABLE2_NONE; import static android.app.StatusBarManager.DISABLE_NONE; +import android.app.StatusBarManager.DisableInfo; import android.content.ComponentName; import android.content.Context; import android.os.Binder; @@ -26,6 +27,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.ShellCommand; import android.service.quicksettings.TileService; +import android.util.Pair; import java.io.PrintWriter; @@ -68,6 +70,8 @@ public class StatusBarShellCommand extends ShellCommand { return runGetStatusIcons(); case "disable-for-setup": return runDisableForSetup(); + case "send-disable-flag": + return runSendDisableFlag(); default: return handleDefaultCommands(cmd); } @@ -132,6 +136,47 @@ public class StatusBarShellCommand extends ShellCommand { return 0; } + private int runSendDisableFlag() { + String pkg = mContext.getPackageName(); + int disable1 = DISABLE_NONE; + int disable2 = DISABLE2_NONE; + + DisableInfo info = new DisableInfo(); + + String arg = getNextArg(); + while (arg != null) { + switch (arg) { + case "search": + info.setSearchDisabled(true); + break; + case "home": + info.setNagivationHomeDisabled(true); + break; + case "recents": + info.setRecentsDisabled(true); + break; + case "notification-alerts": + info.setNotificationPeekingDisabled(true); + break; + case "statusbar-expansion": + info.setStatusBarExpansionDisabled(true); + break; + + default: + break; + } + + arg = getNextArg(); + } + + Pair<Integer, Integer> flagPair = info.toFlags(); + + mInterface.disable(flagPair.first, sToken, pkg); + mInterface.disable2(flagPair.second, sToken, pkg); + + return 0; + } + @Override public void onHelp() { final PrintWriter pw = getOutPrintWriter(); @@ -166,6 +211,17 @@ public class StatusBarShellCommand extends ShellCommand { pw.println(" disable-for-setup DISABLE"); pw.println(" If true, disable status bar components unsuitable for device setup"); pw.println(""); + pw.println(" send-disable-flag FLAG..."); + pw.println(" Send zero or more disable flags (parsed individually) to StatusBarManager"); + pw.println(" Valid options:"); + pw.println(" <blank> - equivalent to \"none\""); + pw.println(" none - re-enables all components"); + pw.println(" search - disable search"); + pw.println(" home - disable naviagation home"); + pw.println(" recents - disable recents/overview"); + pw.println(" notification-peek - disable notification peeking"); + pw.println(" statusbar-expansion - disable status bar expansion"); + pw.println(""); } /** diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 0251efb872bc..f33c518941e5 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2677,8 +2677,9 @@ final class ActivityRecord extends ConfigurationContainer { * Get the configuration orientation by the requested screen orientation * ({@link ActivityInfo.ScreenOrientation}) of this activity. * - * @return orientation in ({@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT}, - * {@link #ORIENTATION_UNDEFINED}). + * @return orientation in ({@link Configuration#ORIENTATION_LANDSCAPE}, + * {@link Configuration#ORIENTATION_PORTRAIT}, + * {@link Configuration#ORIENTATION_UNDEFINED}). */ int getRequestedConfigurationOrientation() { final int screenOrientation = getOrientation(); @@ -2936,14 +2937,36 @@ final class ActivityRecord extends ConfigurationContainer { // should be given the aspect ratio. activityWidth = (int) ((activityHeight * maxAspectRatio) + 0.5f); } - } else if (containingRatio < minAspectRatio && minAspectRatio != 0) { - if (containingAppWidth < containingAppHeight) { - // Width is the shorter side, so we use the height to figure-out what the max. width - // should be given the aspect ratio. + } else if (containingRatio < minAspectRatio) { + boolean adjustWidth; + switch (getRequestedConfigurationOrientation()) { + case ORIENTATION_LANDSCAPE: + // Width should be the longer side for this landscape app, so we use the width + // to figure-out what the max. height should be given the aspect ratio. + adjustWidth = false; + break; + case ORIENTATION_PORTRAIT: + // Height should be the longer side for this portrait app, so we use the height + // to figure-out what the max. width should be given the aspect ratio. + adjustWidth = true; + break; + default: + // This app doesn't have a preferred orientation, so we keep the length of the + // longer side, and use it to figure-out the length of the shorter side. + if (containingAppWidth < containingAppHeight) { + // Width is the shorter side, so we use the height to figure-out what the + // max. width should be given the aspect ratio. + adjustWidth = true; + } else { + // Height is the shorter side, so we use the width to figure-out what the + // max. height should be given the aspect ratio. + adjustWidth = false; + } + break; + } + if (adjustWidth) { activityWidth = (int) ((activityHeight / minAspectRatio) + 0.5f); } else { - // Height is the shorter side, so we use the width to figure-out what the max. - // height should be given the aspect ratio. activityHeight = (int) ((activityWidth / minAspectRatio) + 0.5f); } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 5cfc20b6339f..4795555e8ed2 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1500,8 +1500,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity; final int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity; - mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp); mDisplayPolicy.configure(width, height, shortSizeDp); + mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp); mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo, calculateDisplayCutoutForRotation(mDisplayInfo.rotation)); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 2ee30ac5c8ff..91d573defc16 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -2617,9 +2617,8 @@ public class DisplayPolicy { DisplayCutout displayCutout) { int width = fullWidth; if (hasNavigationBar()) { - // For a basic navigation bar, when we are in landscape mode we place - // the navigation bar to the side. - if (navigationBarCanMove() && fullWidth > fullHeight) { + final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation); + if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) { width -= getNavigationBarWidth(rotation, uiMode); } } @@ -2646,9 +2645,8 @@ public class DisplayPolicy { DisplayCutout displayCutout) { int height = fullHeight; if (hasNavigationBar()) { - // For a basic navigation bar, when we are in portrait mode we place - // the navigation bar to the bottom. - if (!navigationBarCanMove() || fullWidth < fullHeight) { + final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation); + if (navBarPosition == NAV_BAR_BOTTOM) { height -= getNavigationBarHeight(rotation, uiMode); } } diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 5f341ee8002c..543f19655350 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -38,6 +38,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; import android.util.SparseArray; +import android.view.DisplayCutout; import android.view.Surface; import com.android.internal.annotations.VisibleForTesting; @@ -70,6 +71,8 @@ public class DisplayRotation { private final int mDeskDockRotation; private final int mUndockedHdmiRotation; + private final float mCloseToSquareMaxAspectRatio; + private OrientationListener mOrientationListener; private StatusBarManagerInternal mStatusBarManagerInternal; private SettingsObserver mSettingsObserver; @@ -132,6 +135,9 @@ public class DisplayRotation { mUndockedHdmiRotation = readRotation( com.android.internal.R.integer.config_undockedHdmiRotation); + mCloseToSquareMaxAspectRatio = mContext.getResources().getFloat( + com.android.internal.R.dimen.config_closeToSquareDisplayMaxAspectRatio); + if (isDefaultDisplay) { final Handler uiHandler = UiThread.getHandler(); mOrientationListener = new OrientationListener(mContext, uiHandler); @@ -212,10 +218,12 @@ 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); + final boolean isCloseToSquare = + isNonDecorDisplayCloseToSquare(Surface.ROTATION_0, width, height); final boolean forceDefaultOrientationInRes = res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation); final boolean forceDefaultOrienation = - ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) + ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv || isCloseToSquare) && forceDefaultOrientationInRes // For debug purposes the next line turns this feature off with: // $ adb shell setprop config.override_forced_orient true @@ -227,6 +235,18 @@ public class DisplayRotation { setFixedToUserRotation(forceDefaultOrienation); } + private boolean isNonDecorDisplayCloseToSquare(int rotation, int width, int height) { + final DisplayCutout displayCutout = + mDisplayContent.calculateDisplayCutoutForRotation(rotation).getDisplayCutout(); + final int uiMode = mService.mPolicy.getUiMode(); + final int w = mDisplayPolicy.getNonDecorDisplayWidth( + width, height, rotation, uiMode, displayCutout); + final int h = mDisplayPolicy.getNonDecorDisplayHeight( + width, height, rotation, uiMode, displayCutout); + final float aspectRatio = Math.max(w, h) / (float) Math.min(w, h); + return aspectRatio <= mCloseToSquareMaxAspectRatio; + } + void setRotation(int rotation) { if (mOrientationListener != null) { mOrientationListener.setCurrentRotation(rotation); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c747c6d95fcd..168c9adb61be 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -446,7 +446,8 @@ public class WindowManagerService extends IWindowManager.Stub final boolean mLimitedAlphaCompositing; final int mMaxUiWidth; - final WindowManagerPolicy mPolicy; + @VisibleForTesting + WindowManagerPolicy mPolicy; final IActivityManager mActivityManager; // TODO: Probably not needed once activities are fully in WM. @@ -4263,9 +4264,12 @@ public class WindowManagerService extends IWindowManager.Stub if (mMaxUiWidth > 0) { mRoot.forAllDisplays(displayContent -> displayContent.setMaxUiWidth(mMaxUiWidth)); } - applyForcedPropertiesForDefaultDisplay(); + final boolean changed = applyForcedPropertiesForDefaultDisplay(); mAnimator.ready(); mDisplayReady = true; + if (changed) { + reconfigureDisplayLocked(getDefaultDisplayContentLocked()); + } mIsTouchDevice = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_TOUCHSCREEN); } @@ -4865,7 +4869,8 @@ public class WindowManagerService extends IWindowManager.Stub } /** The global settings only apply to default display. */ - private void applyForcedPropertiesForDefaultDisplay() { + private boolean applyForcedPropertiesForDefaultDisplay() { + boolean changed = false; final DisplayContent displayContent = getDefaultDisplayContentLocked(); // Display size. String sizeStr = Settings.Global.getString(mContext.getContentResolver(), @@ -4885,6 +4890,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.i(TAG_WM, "FORCED DISPLAY SIZE: " + width + "x" + height); displayContent.updateBaseDisplayMetrics(width, height, displayContent.mBaseDisplayDensity); + changed = true; } } catch (NumberFormatException ex) { } @@ -4893,17 +4899,20 @@ public class WindowManagerService extends IWindowManager.Stub // Display density. final int density = getForcedDisplayDensityForUserLocked(mCurrentUserId); - if (density != 0) { + if (density != 0 && density != displayContent.mBaseDisplayDensity) { displayContent.mBaseDisplayDensity = density; + changed = true; } // Display scaling mode. int mode = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.DISPLAY_SCALING_FORCE, 0); - if (mode != 0) { + if (displayContent.mDisplayScalingDisabled != (mode != 0)) { Slog.i(TAG_WM, "FORCED DISPLAY SCALING DISABLED"); displayContent.mDisplayScalingDisabled = true; + changed = true; } + return changed; } @Override diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp index 5c19ad33617c..9cbb58d35b9f 100644 --- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp +++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "BatteryStatsService" //#define LOG_NDEBUG 0 +#include <climits> #include <errno.h> #include <fcntl.h> #include <inttypes.h> @@ -28,6 +29,7 @@ #include <sys/types.h> #include <unistd.h> #include <unordered_map> +#include <utility> #include <android/hardware/power/1.0/IPower.h> #include <android/hardware/power/1.1/IPower.h> @@ -87,6 +89,15 @@ std::function<void(JNIEnv*, jobject)> gGetLowPowerStatsImpl = {}; std::function<jint(JNIEnv*, jobject)> gGetPlatformLowPowerStatsImpl = {}; std::function<jint(JNIEnv*, jobject)> gGetSubsystemLowPowerStatsImpl = {}; +// Cellular/Wifi power monitor rail information +static jmethodID jupdateRailData = NULL; +static jmethodID jsetRailStatsAvailability = NULL; + +std::function<void(JNIEnv*, jobject)> gGetRailEnergyPowerStatsImpl = {}; + +std::unordered_map<uint32_t, std::pair<std::string, std::string>> gPowerStatsHalRailNames = {}; +static bool power_monitor_available = false; + // The caller must be holding gPowerHalMutex. static void deinitPowerStatsLocked() { gPowerStatsHalV1_0 = nullptr; @@ -258,6 +269,7 @@ static bool initializePowerStats() { gPowerStatsHalStateNames.clear(); gPowerStatsHalPlatformIds.clear(); gPowerStatsHalSubsystemIds.clear(); + gPowerStatsHalRailNames.clear(); Return<void> ret; ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) { @@ -301,6 +313,27 @@ static bool initializePowerStats() { return false; } + // Get Power monitor rails available + ret = gPowerStatsHalV1_0->getRailInfo([](auto rails, auto status) { + if (status != Status::SUCCESS) { + ALOGW("Rail information is not available"); + power_monitor_available = false; + return; + } + + // Fill out rail names/subsystems into gPowerStatsHalRailNames + for (auto rail : rails) { + gPowerStatsHalRailNames.emplace(rail.index, + std::make_pair(rail.railName, rail.subsysName)); + } + if (!gPowerStatsHalRailNames.empty()) { + power_monitor_available = true; + } + }); + if (!checkResultLocked(ret, __func__)) { + return false; + } + return (!gPowerStatsHalEntityNames.empty()) && (!gPowerStatsHalStateNames.empty()); } @@ -517,6 +550,50 @@ static jint getPowerStatsHalSubsystemData(JNIEnv* env, jobject outBuf) { return total_added; } +static void getPowerStatsHalRailEnergyData(JNIEnv* env, jobject jrailStats) { + using android::hardware::power::stats::V1_0::Status; + using android::hardware::power::stats::V1_0::EnergyData; + + if (!getPowerStatsHalLocked()) { + ALOGE("failed to get power stats"); + return; + } + + if (!power_monitor_available) { + env->CallVoidMethod(jrailStats, jsetRailStatsAvailability, false); + ALOGW("Rail energy data is not available"); + return; + } + + // Get power rail energySinceBoot data + Return<void> ret = gPowerStatsHalV1_0->getEnergyData({}, + [&env, &jrailStats](auto energyData, auto status) { + if (status == Status::NOT_SUPPORTED) { + ALOGW("getEnergyData is not supported"); + return; + } + + for (auto data : energyData) { + if (!(data.timestamp > LLONG_MAX || data.energy > LLONG_MAX)) { + env->CallVoidMethod(jrailStats, + jupdateRailData, + data.index, + env->NewStringUTF( + gPowerStatsHalRailNames.at(data.index).first.c_str()), + env->NewStringUTF( + gPowerStatsHalRailNames.at(data.index).second.c_str()), + data.timestamp, + data.energy); + } else { + ALOGE("Java long overflow seen. Rail index %d not updated", data.index); + } + } + }); + if (!checkResultLocked(ret, __func__)) { + ALOGE("getEnergyData failed"); + } +} + // The caller must be holding powerHalMutex. static void getPowerHalLowPowerData(JNIEnv* env, jobject jrpmStats) { sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0(); @@ -761,11 +838,13 @@ static void setUpPowerStatsLocked() { gGetLowPowerStatsImpl = getPowerStatsHalLowPowerData; gGetPlatformLowPowerStatsImpl = getPowerStatsHalPlatformData; gGetSubsystemLowPowerStatsImpl = getPowerStatsHalSubsystemData; + gGetRailEnergyPowerStatsImpl = getPowerStatsHalRailEnergyData; } else if (android::hardware::power::V1_0::IPower::getService() != nullptr) { ALOGI("Using power HAL"); gGetLowPowerStatsImpl = getPowerHalLowPowerData; gGetPlatformLowPowerStatsImpl = getPowerHalPlatformData; gGetSubsystemLowPowerStatsImpl = getPowerHalSubsystemData; + gGetRailEnergyPowerStatsImpl = NULL; } } @@ -835,11 +914,44 @@ static jint getSubsystemLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject return -1; } +static void getRailEnergyPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrailStats) { + if (jrailStats == NULL) { + jniThrowException(env, "java/lang/NullPointerException", + "The railstats jni input jobject jrailStats is null."); + return; + } + if (jupdateRailData == NULL) { + ALOGE("A railstats jni jmethodID is null."); + return; + } + + std::lock_guard<std::mutex> lock(gPowerHalMutex); + + if (!gGetRailEnergyPowerStatsImpl) { + setUpPowerStatsLocked(); + } + + if (gGetRailEnergyPowerStatsImpl) { + gGetRailEnergyPowerStatsImpl(env, jrailStats); + return; + } + + if (jsetRailStatsAvailability == NULL) { + ALOGE("setRailStatsAvailability jni jmethodID is null."); + return; + } + env->CallVoidMethod(jrailStats, jsetRailStatsAvailability, false); + ALOGE("Unable to load Power.Stats.HAL. Setting rail availability to false"); + return; +} + static const JNINativeMethod method_table[] = { { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup }, { "getLowPowerStats", "(Lcom/android/internal/os/RpmStats;)V", (void*)getLowPowerStats }, { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats }, { "getSubsystemLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getSubsystemLowPowerStats }, + { "getRailEnergyPowerStats", "(Lcom/android/internal/os/RailStats;)V", + (void*)getRailEnergyPowerStats }, }; int register_android_server_BatteryStatsService(JNIEnv *env) @@ -850,8 +962,9 @@ int register_android_server_BatteryStatsService(JNIEnv *env) env->FindClass("com/android/internal/os/RpmStats$PowerStatePlatformSleepState"); jclass clsPowerStateSubsystem = env->FindClass("com/android/internal/os/RpmStats$PowerStateSubsystem"); + jclass clsRailStats = env->FindClass("com/android/internal/os/RailStats"); if (clsRpmStats == NULL || clsPowerStatePlatformSleepState == NULL - || clsPowerStateSubsystem == NULL) { + || clsPowerStateSubsystem == NULL || clsRailStats == NULL) { ALOGE("A rpmstats jni jclass is null."); } else { jgetAndUpdatePlatformState = env->GetMethodID(clsRpmStats, "getAndUpdatePlatformState", @@ -862,6 +975,10 @@ int register_android_server_BatteryStatsService(JNIEnv *env) "(Ljava/lang/String;JI)V"); jputState = env->GetMethodID(clsPowerStateSubsystem, "putState", "(Ljava/lang/String;JI)V"); + jupdateRailData = env->GetMethodID(clsRailStats, "updateRailData", + "(JLjava/lang/String;Ljava/lang/String;JJ)V"); + jsetRailStatsAvailability = env->GetMethodID(clsRailStats, "setRailStatsAvailability", + "(Z)V"); } return jniRegisterNativeMethods(env, "com/android/server/am/BatteryStatsService", diff --git a/services/net/Android.bp b/services/net/Android.bp index 638ec95ec544..9946cc3db0e8 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -1,6 +1,9 @@ java_library_static { name: "services.net", srcs: ["java/**/*.java"], + static_libs: [ + "netd_aidl_interface-java", + ] } filegroup { diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java index 4bac200a22c6..ebbebcb02923 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java @@ -16,12 +16,12 @@ package com.android.server; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW; +import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; +import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; +import static android.net.INetd.FIREWALL_CHAIN_STANDBY; +import static android.net.INetd.FIREWALL_RULE_ALLOW; +import static android.net.INetd.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY; import static android.util.DebugUtils.valueToString; import static org.junit.Assert.assertEquals; diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java index a3f36b720398..3e5ce46e8e3a 100644 --- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java @@ -57,6 +57,8 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.server.backup.utils.RandomAccessFileUtils; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -125,6 +127,7 @@ public class TrampolineTest { private File mTestDir; private File mSuppressFile; private File mActivatedFile; + private File mRememberActivatedFile; @Before public void setUp() throws Exception { @@ -153,6 +156,8 @@ public class TrampolineTest { mActivatedFile = new File(mTestDir, "activate-" + NON_USER_SYSTEM); TrampolineTestable.sActivatedFiles.append(NON_USER_SYSTEM, mActivatedFile); + mRememberActivatedFile = new File(mTestDir, "rem-activate-" + NON_USER_SYSTEM); + TrampolineTestable.sRememberActivatedFiles.append(NON_USER_SYSTEM, mRememberActivatedFile); mTrampoline = new TrampolineTestable(mContextMock); } @@ -411,6 +416,34 @@ public class TrampolineTest { } @Test + public void setBackupServiceActive_forNonSystemUser_remembersActivated() { + mTrampoline.initializeService(); + + mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true); + + assertTrue(RandomAccessFileUtils.readBoolean(mRememberActivatedFile, false)); + } + + @Test + public void setBackupServiceActiveFalse_forNonSystemUser_remembersActivated() { + mTrampoline.initializeService(); + + mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, false); + + assertFalse(RandomAccessFileUtils.readBoolean(mRememberActivatedFile, true)); + } + + @Test + public void setBackupServiceActiveTwice_forNonSystemUser_remembersLastActivated() { + mTrampoline.initializeService(); + + mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true); + mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, false); + + assertFalse(RandomAccessFileUtils.readBoolean(mRememberActivatedFile, true)); + } + + @Test public void dataChanged_calledBeforeInitialize_ignored() throws Exception { mTrampoline.dataChanged(PACKAGE_NAME); verifyNoMoreInteractions(mBackupManagerServiceMock); @@ -1291,6 +1324,7 @@ public class TrampolineTest { static BackupManagerService sBackupManagerServiceMock = null; static File sSuppressFile = null; static SparseArray<File> sActivatedFiles = new SparseArray<>(); + static SparseArray<File> sRememberActivatedFiles = new SparseArray<>(); static UserManager sUserManagerMock = null; private int mCreateServiceCallsCount = 0; @@ -1314,6 +1348,11 @@ public class TrampolineTest { } @Override + protected File getRememberActivatedFileForNonSystemUser(int userId) { + return sRememberActivatedFiles.get(userId); + } + + @Override protected File getActivatedFileForNonSystemUser(int userId) { return sActivatedFiles.get(userId); } diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java new file mode 100644 index 000000000000..eaa9c4520979 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.backup.utils; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.google.common.io.Files; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.IOException; + +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class FileUtilsTest { + private static File sTemporaryDir; + private File mTemporaryFile; + + @BeforeClass + public static void setUpClass() { + sTemporaryDir = Files.createTempDir(); + } + + @AfterClass + public static void tearDownClass() { + if (sTemporaryDir != null) { + sTemporaryDir.delete(); + } + } + + @Before + public void setUp() throws Exception { + mTemporaryFile = new File(sTemporaryDir, "fileutilstest.txt"); + } + + /** Test that if file does not exist, {@link FileUtils#createNewFile()} creates the file. */ + @Test + public void testEnsureFileExists_fileDoesNotAlreadyExist_getsCreated() { + assertThat(!mTemporaryFile.exists()); + + FileUtils.createNewFile(mTemporaryFile); + + assertThat(mTemporaryFile.exists()); + } + + /** Test that if file does exist, {@link FileUtils#createNewFile()} does not error out. */ + @Test + public void testEnsureFileExists_fileAlreadyExists_doesNotErrorOut() throws IOException { + mTemporaryFile.createNewFile(); + + FileUtils.createNewFile(mTemporaryFile); + + assertThat(mTemporaryFile.exists()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java new file mode 100644 index 000000000000..ca699bd7b85c --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.backup.utils; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; + +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class RandomAccessFileUtilsTest { + private File mTemporaryFile; + + @Before + public void setUp() throws Exception { + mTemporaryFile = File.createTempFile("fileutilstest", ".txt"); + } + + @After + public void tearDown() throws Exception { + if (mTemporaryFile != null) { + mTemporaryFile.delete(); + } + } + + /** + * Test that if we write true, we read back true. + */ + @Test + public void testWriteTrue_readReturnsTrue() { + RandomAccessFileUtils.writeBoolean(mTemporaryFile, true); + + assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, false)).isEqualTo(true); + } + + /** + * Test that if we write false, we read back false. + */ + @Test + public void testWriteFalse_readReturnsFalse() { + RandomAccessFileUtils.writeBoolean(mTemporaryFile, false); + + assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, true)).isEqualTo(false); + } + + /** + * Test that if we write true twice, we read back true. + */ + @Test + public void testWriteTrueTwice_readReturnsTrue() { + RandomAccessFileUtils.writeBoolean(mTemporaryFile, true); + RandomAccessFileUtils.writeBoolean(mTemporaryFile, true); + + assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, false)).isEqualTo(true); + } + + /** + * Test that if we write false twice, we read back false. + */ + @Test + public void testWriteFalseTwice_readReturnsFalse() { + RandomAccessFileUtils.writeBoolean(mTemporaryFile, false); + RandomAccessFileUtils.writeBoolean(mTemporaryFile, false); + + assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, true)).isEqualTo(false); + } + + /** + * Test that if we write true and then false, we read back false. + */ + @Test + public void testWriteTrueFalse_readReturnsFalse() { + RandomAccessFileUtils.writeBoolean(mTemporaryFile, true); + RandomAccessFileUtils.writeBoolean(mTemporaryFile, false); + + assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, true)).isEqualTo(false); + } + + /** + * Test that if we write false and then true, we read back true. + */ + @Test + public void testWriteFalseTrue_readReturnsTrue() { + RandomAccessFileUtils.writeBoolean(mTemporaryFile, false); + RandomAccessFileUtils.writeBoolean(mTemporaryFile, true); + + assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, false)).isEqualTo(true); + } +} diff --git a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java index 9f1cbcd7ec27..6a937fabd3ec 100644 --- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java +++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java @@ -98,6 +98,15 @@ public class AttentionDetectorTest extends AndroidTestCase { } @Test + public void testUpdateUserActivity_schedulesTheNextCheck() { + long now = SystemClock.uptimeMillis(); + mNextDimming = now; + mAttentionDetector.onUserActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH); + long nextTimeout = mAttentionDetector.updateUserActivity(mNextDimming + 5000L); + assertThat(nextTimeout).isEqualTo(mNextDimming + 5000L); + } + + @Test public void testOnUserActivity_ignoresAfterMaximumExtension() { long now = SystemClock.uptimeMillis(); mAttentionDetector.onUserActivity(now - 15000L, PowerManager.USER_ACTIVITY_EVENT_TOUCH); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java index a7520dcbcff9..2627ec762d7a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java @@ -30,6 +30,7 @@ import static org.junit.Assert.assertTrue; import android.graphics.Rect; import android.os.IBinder; +import android.platform.test.annotations.Presubmit; import android.view.Display; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; @@ -48,6 +49,7 @@ import org.junit.Test; * atest WmTests:AppChangeTransitionTests */ @SmallTest +@Presubmit public class AppChangeTransitionTests extends WindowTestsBase { private TaskStack mStack; diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index cd1320986972..1dd72ec4fd71 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -51,7 +51,6 @@ import android.platform.test.annotations.Presubmit; import android.view.Surface; import android.view.WindowManager; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Before; @@ -142,7 +141,6 @@ public class AppWindowTokenTests extends WindowTestsBase { mToken.removeImmediately(); } - @FlakyTest(detail = "Promote to presubmit when shown to be stable.") @Test public void testLandscapeSeascapeRotationByApp() { // Some plumbing to get the service ready for rotation updates. @@ -303,7 +301,6 @@ public class AppWindowTokenTests extends WindowTestsBase { } @Test - @FlakyTest(detail = "Promote once confirmed non-flaky") public void testStuckExitingWindow() { final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, "closingWindow"); @@ -346,7 +343,6 @@ public class AppWindowTokenTests extends WindowTestsBase { assertNoStartingWindow(mToken); } - @FlakyTest(detail = "Promote to presubmit when shown to be stable.") @Test public void testAddRemoveRace() { // There was once a race condition between adding and removing starting windows diff --git a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java index 3f83caea613a..1e02a12e83db 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java @@ -46,6 +46,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.platform.test.annotations.Presubmit; import android.util.Log; import android.view.IWindowManager; @@ -71,6 +72,7 @@ import java.util.concurrent.TimeUnit; * atest WmTests:AssistDataRequesterTest */ @MediumTest +@Presubmit public class AssistDataRequesterTest extends ActivityTestsBase { private static final String TAG = AssistDataRequesterTest.class.getSimpleName(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java index 198e7ce63f52..b15e99aaa8c2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -60,6 +60,7 @@ import com.android.server.LocalServices; import com.android.server.UiThread; import com.android.server.policy.WindowManagerPolicy; import com.android.server.statusbar.StatusBarManagerInternal; +import com.android.server.wm.utils.WmDisplayCutout; import org.junit.After; import org.junit.Before; @@ -113,6 +114,7 @@ public class DisplayRotationTests { public static void setUpOnce() { sMockWm = mock(WindowManagerService.class); sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class); + sMockWm.mPolicy = mock(WindowManagerPolicy.class); } @Before @@ -807,6 +809,8 @@ public class DisplayRotationTests { mMockDisplayContent = mock(WindowTestUtils.TestDisplayContent.class); mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay; + when(mMockDisplayContent.calculateDisplayCutoutForRotation(anyInt())) + .thenReturn(WmDisplayCutout.NO_CUTOUT); mMockDisplayPolicy = mock(DisplayPolicy.class); diff --git a/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java b/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java index ce22788f7cb9..df26679dc1fb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java @@ -34,6 +34,7 @@ import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.UserHandle; +import android.platform.test.annotations.Presubmit; import android.util.SparseBooleanArray; import com.android.server.wm.LockTaskController.LockTaskToken; @@ -43,6 +44,7 @@ import org.junit.Test; import java.lang.reflect.Constructor; +@Presubmit public class KeyguardDisableHandlerTest { private KeyguardDisableHandler mKeyguardDisable; diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index cc6a58a81635..763ea6293fcc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -112,8 +112,8 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { } } - @FlakyTest(bugId = 117117823) @Test + @FlakyTest(bugId = 117117823) public void testIncludedApps_expectTargetAndVisible() { mWm.setRecentsAnimationController(mController); final AppWindowToken homeAppWindow = createAppWindowToken(mDisplayContent, diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java index c595868db484..2377df406fbc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java @@ -34,10 +34,12 @@ import static org.junit.Assert.assertTrue; import android.app.IActivityTaskManager; import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Before; @@ -50,6 +52,8 @@ import org.junit.Test; * atest WmTests:TaskPositionerTests */ @SmallTest +@Presubmit +@FlakyTest public class TaskPositionerTests extends WindowTestsBase { private static final boolean DEBUGGING = false; diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java index 649b785c992b..99ceb2011db3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java @@ -31,6 +31,7 @@ import static org.junit.Assert.*; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.PointF; +import android.platform.test.annotations.Presubmit; import android.view.DisplayInfo; import org.junit.Before; @@ -42,6 +43,7 @@ import org.junit.rules.ErrorCollector; * Build/Install/Run: * atest WmTests:CoordinateTransformsTest */ +@Presubmit public class CoordinateTransformsTest { private static final int W = 200; diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 93f758c2a9ad..b9440ebd88f6 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -1193,7 +1193,7 @@ public class VoiceInteractionManagerService extends SystemService { } @Override - public void setTranscription(IVoiceInteractionService service, String transcription) { + public void setUiHints(IVoiceInteractionService service, Bundle hints) { synchronized (this) { enforceIsCurrentVoiceInteractionService(service); @@ -1202,47 +1202,9 @@ public class VoiceInteractionManagerService extends SystemService { final IVoiceInteractionSessionListener listener = mVoiceInteractionSessionListeners.getBroadcastItem(i); try { - listener.onTranscriptionUpdate(transcription); + listener.onSetUiHints(hints); } catch (RemoteException e) { - Slog.e(TAG, "Error delivering voice transcription.", e); - } - } - mVoiceInteractionSessionListeners.finishBroadcast(); - } - } - - @Override - public void clearTranscription(IVoiceInteractionService service, boolean immediate) { - synchronized (this) { - enforceIsCurrentVoiceInteractionService(service); - - final int size = mVoiceInteractionSessionListeners.beginBroadcast(); - for (int i = 0; i < size; ++i) { - final IVoiceInteractionSessionListener listener = - mVoiceInteractionSessionListeners.getBroadcastItem(i); - try { - listener.onTranscriptionComplete(immediate); - } catch (RemoteException e) { - Slog.e(TAG, "Error delivering transcription complete event.", e); - } - } - mVoiceInteractionSessionListeners.finishBroadcast(); - } - } - - @Override - public void setVoiceState(IVoiceInteractionService service, int state) { - synchronized (this) { - enforceIsCurrentVoiceInteractionService(service); - - final int size = mVoiceInteractionSessionListeners.beginBroadcast(); - for (int i = 0; i < size; ++i) { - final IVoiceInteractionSessionListener listener = - mVoiceInteractionSessionListeners.getBroadcastItem(i); - try { - listener.onVoiceStateChange(state); - } catch (RemoteException e) { - Slog.e(TAG, "Error delivering voice state change.", e); + Slog.e(TAG, "Error delivering UI hints.", e); } } mVoiceInteractionSessionListeners.finishBroadcast(); diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index e99a289729a4..d5091680285b 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -291,6 +291,19 @@ public class TelecomManager { "android.telecom.extra.OUTGOING_CALL_EXTRAS"; /** + * An optional boolean extra on {@link android.content.Intent#ACTION_CALL_EMERGENCY} to tell + * whether the user's dial intent is emergency; this is required to specify when the dialed + * number is ambiguous, identified as both emergency number and any other non-emergency number; + * e.g. in some situation, 611 could be both an emergency number in a country and a + * non-emergency number of a carrier's customer service hotline. + * + * @hide + */ + @SystemApi + public static final String EXTRA_IS_USER_INTENT_EMERGENCY_CALL = + "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL"; + + /** * @hide */ public static final String EXTRA_UNKNOWN_CALL_HANDLE = diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java index 38143335dbf1..dba437a3a007 100644 --- a/telephony/java/android/telephony/CellIdentityTdscdma.java +++ b/telephony/java/android/telephony/CellIdentityTdscdma.java @@ -141,6 +141,14 @@ public final class CellIdentityTdscdma extends CellIdentity { return mCpid; } + /** + * @return 16-bit UMTS Absolute RF Channel Number, + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable. + */ + public int getUarfcn() { + return mUarfcn; + } + /** @hide */ @Override public int getChannelNumber() { diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java index 53d69f447a56..24db438580c9 100644 --- a/telephony/java/android/telephony/LocationAccessPolicy.java +++ b/telephony/java/android/telephony/LocationAccessPolicy.java @@ -26,11 +26,12 @@ import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.location.LocationManager; import android.os.Binder; +import android.os.Build; import android.os.Process; -import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; +import android.widget.Toast; import java.util.List; @@ -41,61 +42,236 @@ import java.util.List; public final class LocationAccessPolicy { private static final String TAG = "LocationAccessPolicy"; private static final boolean DBG = false; + public static final int MAX_SDK_FOR_ANY_ENFORCEMENT = Build.VERSION_CODES.P; - /** - * API to determine if the caller has permissions to get cell location. - * - * @param pkgName Package name of the application requesting access - * @param uid The uid of the package - * @param pid The pid of the package - * @param throwOnDeniedPermission Whether to throw if the location permission is denied. - * @return boolean true or false if permissions is granted - */ - public static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName, - int uid, int pid, boolean throwOnDeniedPermission) throws SecurityException { - Trace.beginSection("TelephonyLocationCheck"); - try { - // Always allow the phone process and system server to access location. This avoid - // breaking legacy code that rely on public-facing APIs to access cell location, and - // it doesn't create an info leak risk because the cell location is stored in the phone - // process anyway, and the system server already has location access. - if (uid == Process.PHONE_UID || uid == Process.SYSTEM_UID || uid == Process.ROOT_UID) { - return true; + public enum LocationPermissionResult { + ALLOWED, + /** + * Indicates that the denial is due to a transient device state + * (e.g. app-ops, location master switch) + */ + DENIED_SOFT, + /** + * Indicates that the denial is due to a misconfigured app (e.g. missing entry in manifest) + */ + DENIED_HARD, + } + + public static class LocationPermissionQuery { + public final String callingPackage; + public final int callingUid; + public final int callingPid; + public final int minSdkVersionForCoarse; + public final int minSdkVersionForFine; + public final String method; + + private LocationPermissionQuery(String callingPackage, int callingUid, int callingPid, + int minSdkVersionForCoarse, int minSdkVersionForFine, String method) { + this.callingPackage = callingPackage; + this.callingUid = callingUid; + this.callingPid = callingPid; + this.minSdkVersionForCoarse = minSdkVersionForCoarse; + this.minSdkVersionForFine = minSdkVersionForFine; + this.method = method; + } + + public static class Builder { + private String mCallingPackage; + private int mCallingUid; + private int mCallingPid; + private int mMinSdkVersionForCoarse = Integer.MAX_VALUE; + private int mMinSdkVersionForFine = Integer.MAX_VALUE; + private String mMethod; + + /** + * Mandatory parameter, used for performing permission checks. + */ + public Builder setCallingPackage(String callingPackage) { + mCallingPackage = callingPackage; + return this; } - // We always require the location permission and also require the - // location mode to be on for non-legacy apps. Legacy apps are - // required to be in the foreground to at least mitigate the case - // where a legacy app the user is not using tracks their location. - // Granting ACCESS_FINE_LOCATION to an app automatically grants it - // ACCESS_COARSE_LOCATION. - if (throwOnDeniedPermission) { - context.enforcePermission(Manifest.permission.ACCESS_COARSE_LOCATION, - pid, uid, "canAccessCellLocation"); - } else if (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION, - pid, uid) == PackageManager.PERMISSION_DENIED) { - if (DBG) Log.w(TAG, "Permission checked failed (" + pid + "," + uid + ")"); - return false; + /** + * Mandatory parameter, used for performing permission checks. + */ + public Builder setCallingUid(int callingUid) { + mCallingUid = callingUid; + return this; } - final int opCode = AppOpsManager.permissionToOpCode( - Manifest.permission.ACCESS_COARSE_LOCATION); - if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class) - .noteOpNoThrow(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) { - if (DBG) Log.w(TAG, "AppOp check failed (" + uid + "," + pkgName + ")"); - return false; + + /** + * Mandatory parameter, used for performing permission checks. + */ + public Builder setCallingPid(int callingPid) { + mCallingPid = callingPid; + return this; } - if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) { - if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")"); - return false; + + /** + * Apps that target at least this sdk version will be checked for coarse location + * permission. Defaults to INT_MAX (which means don't check) + */ + public Builder setMinSdkVersionForCoarse( + int minSdkVersionForCoarse) { + mMinSdkVersionForCoarse = minSdkVersionForCoarse; + return this; } - // If the user or profile is current, permission is granted. - // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission. - return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context); - } finally { - Trace.endSection(); + + /** + * Apps that target at least this sdk version will be checked for fine location + * permission. Defaults to INT_MAX (which means don't check) + */ + public Builder setMinSdkVersionForFine( + int minSdkVersionForFine) { + mMinSdkVersionForFine = minSdkVersionForFine; + return this; + } + + /** + * Optional, for logging purposes only. + */ + public Builder setMethod(String method) { + mMethod = method; + return this; + } + + public LocationPermissionQuery build() { + return new LocationPermissionQuery(mCallingPackage, mCallingUid, + mCallingPid, mMinSdkVersionForCoarse, mMinSdkVersionForFine, mMethod); + } + } + } + + private static void logError(Context context, String errorMsg) { + Log.e(TAG, errorMsg); + try { + if (Build.IS_DEBUGGABLE) { + Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show(); + } + } catch (Throwable t) { + // whatever, not important + } + } + + private static LocationPermissionResult appOpsModeToPermissionResult(int appOpsMode) { + switch (appOpsMode) { + case AppOpsManager.MODE_ALLOWED: + return LocationPermissionResult.ALLOWED; + case AppOpsManager.MODE_ERRORED: + return LocationPermissionResult.DENIED_HARD; + default: + return LocationPermissionResult.DENIED_SOFT; } } + private static LocationPermissionResult checkAppLocationPermissionHelper(Context context, + LocationPermissionQuery query, String permissionToCheck) { + String locationTypeForLog = + Manifest.permission.ACCESS_FINE_LOCATION.equals(permissionToCheck) + ? "fine" : "coarse"; + + // Do the app-ops and the manifest check without any of the allow-overrides first. + boolean hasManifestPermission = checkManifestPermission(context, query.callingPid, + query.callingUid, permissionToCheck); + + int appOpMode = context.getSystemService(AppOpsManager.class) + .noteOpNoThrow(AppOpsManager.permissionToOpCode(permissionToCheck), + query.callingUid, query.callingPackage); + + if (hasManifestPermission && appOpMode == AppOpsManager.MODE_ALLOWED) { + // If the app did everything right, return without logging. + return LocationPermissionResult.ALLOWED; + } + + // If the app has the manifest permission but not the app-op permission, it means that + // it's aware of the requirement and the user denied permission explicitly. If we see + // this, don't let any of the overrides happen. + if (hasManifestPermission) { + Log.i(TAG, query.callingPackage + " is aware of " + locationTypeForLog + " but the" + + " app-ops permission is specifically denied."); + return appOpsModeToPermissionResult(appOpMode); + } + + int minSdkVersion = Manifest.permission.ACCESS_FINE_LOCATION.equals(permissionToCheck) + ? query.minSdkVersionForFine : query.minSdkVersionForCoarse; + + // If the app fails for some reason, see if it should be allowed to proceed. + if (minSdkVersion > MAX_SDK_FOR_ANY_ENFORCEMENT) { + String errorMsg = "Allowing " + query.callingPackage + " " + locationTypeForLog + + " because we're not enforcing API " + query.minSdkVersionForFine + " yet." + + " Please fix this app because it will break in the future. Called from " + + query.method; + logError(context, errorMsg); + return null; + } else if (!isAppAtLeastSdkVersion(context, query.callingPackage, minSdkVersion)) { + String errorMsg = "Allowing " + query.callingPackage + " " + locationTypeForLog + + " because it doesn't target API " + query.minSdkVersionForFine + " yet." + + " Please fix this app. Called from " + query.method; + logError(context, errorMsg); + return null; + } else { + // If we're not allowing it due to the above two conditions, this means that the app + // did not declare the permission in their manifest. + return LocationPermissionResult.DENIED_HARD; + } + } + + public static LocationPermissionResult checkLocationPermission( + Context context, LocationPermissionQuery query) { + // Always allow the phone process and system server to access location. This avoid + // breaking legacy code that rely on public-facing APIs to access cell location, and + // it doesn't create an info leak risk because the cell location is stored in the phone + // process anyway, and the system server already has location access. + if (query.callingUid == Process.PHONE_UID || query.callingUid == Process.SYSTEM_UID + || query.callingUid == Process.ROOT_UID) { + return LocationPermissionResult.ALLOWED; + } + + // Check the system-wide requirements. If the location master switch is off or + // the app's profile isn't in foreground, return a soft denial. + if (!checkSystemLocationAccess(context, query.callingUid, query.callingPid)) { + return LocationPermissionResult.DENIED_SOFT; + } + + // Do the check for fine, then for coarse. + if (query.minSdkVersionForFine < Integer.MAX_VALUE) { + LocationPermissionResult resultForFine = checkAppLocationPermissionHelper( + context, query, Manifest.permission.ACCESS_FINE_LOCATION); + if (resultForFine != null) { + return resultForFine; + } + } + + if (query.minSdkVersionForCoarse < Integer.MAX_VALUE) { + LocationPermissionResult resultForCoarse = checkAppLocationPermissionHelper( + context, query, Manifest.permission.ACCESS_COARSE_LOCATION); + if (resultForCoarse != null) { + return resultForCoarse; + } + } + + // At this point, we're out of location checks to do. If the app bypassed all the previous + // ones due to the SDK grandfathering schemes, allow it access. + return LocationPermissionResult.ALLOWED; + } + + + private static boolean checkManifestPermission(Context context, int pid, int uid, + String permissionToCheck) { + return context.checkPermission(permissionToCheck, pid, uid) + == PackageManager.PERMISSION_GRANTED; + } + + private static boolean checkSystemLocationAccess(@NonNull Context context, int uid, int pid) { + if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) { + if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")"); + return false; + } + // If the user or profile is current, permission is granted. + // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission. + return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context, uid, pid); + } + private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) { LocationManager locationManager = context.getSystemService(LocationManager.class); if (locationManager == null) { @@ -105,10 +281,10 @@ public final class LocationAccessPolicy { return locationManager.isLocationEnabledForUser(UserHandle.of(userId)); } - private static boolean checkInteractAcrossUsersFull(@NonNull Context context) { - return context.checkCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) - == PackageManager.PERMISSION_GRANTED; + private static boolean checkInteractAcrossUsersFull( + @NonNull Context context, int pid, int uid) { + return checkManifestPermission(context, pid, uid, + Manifest.permission.INTERACT_ACROSS_USERS_FULL); } private static boolean isCurrentProfile(@NonNull Context context, int uid) { @@ -132,4 +308,18 @@ public final class LocationAccessPolicy { Binder.restoreCallingIdentity(token); } } -} + + private static boolean isAppAtLeastSdkVersion(Context context, String pkgName, int sdkVersion) { + try { + if (context.getPackageManager().getApplicationInfo(pkgName, 0).targetSdkVersion + >= sdkVersion) { + return true; + } + } catch (PackageManager.NameNotFoundException e) { + // In case of exception, assume known app (more strict checking) + // Note: This case will never happen since checkPackage is + // called to verify validity before checking app's version. + } + return false; + } +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java index c37b492a9cf1..6e6d59e62148 100644 --- a/telephony/java/android/telephony/NetworkRegistrationState.java +++ b/telephony/java/android/telephony/NetworkRegistrationState.java @@ -152,7 +152,7 @@ public class NetworkRegistrationState implements Parcelable { private final int[] mAvailableServices; @Nullable - private final CellIdentity mCellIdentity; + private CellIdentity mCellIdentity; @Nullable private VoiceSpecificRegistrationStates mVoiceSpecificStates; @@ -521,4 +521,22 @@ public class NetworkRegistrationState implements Parcelable { return new NetworkRegistrationState[size]; } }; + + /** + * @hide + */ + public NetworkRegistrationState sanitizeLocationInfo() { + NetworkRegistrationState result = copy(); + result.mCellIdentity = null; + return result; + } + + private NetworkRegistrationState copy() { + Parcel p = Parcel.obtain(); + this.writeToParcel(p, 0); + p.setDataPosition(0); + NetworkRegistrationState result = new NetworkRegistrationState(p); + p.recycle(); + return result; + } } diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index 2c9ba1dfff7b..3ce646cb400b 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -46,7 +46,7 @@ import java.util.concurrent.Executor; * Override the methods for the state that you wish to receive updates for, and * pass your PhoneStateListener object, along with bitwise-or of the LISTEN_ * flags to {@link TelephonyManager#listen TelephonyManager.listen()}. Methods are - * called when the state changes, os well as once on initial registration. + * called when the state changes, as well as once on initial registration. * <p> * Note that access to some telephony information is * permission-protected. Your application won't receive updates for protected diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 402763e52cfa..a1aee6d8217f 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -36,6 +36,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; /** * Contains phone state and service related information. @@ -1885,4 +1886,29 @@ public class ServiceState implements Parcelable { ? range1 : range2; } + + /** + * Returns a copy of self with location-identifying information removed. + * Always clears the NetworkRegistrationState's CellIdentity fields, but if removeCoarseLocation + * is true, clears other info as well. + * @hide + */ + public ServiceState sanitizeLocationInfo(boolean removeCoarseLocation) { + ServiceState state = new ServiceState(this); + if (state.mNetworkRegistrationStates != null) { + state.mNetworkRegistrationStates = state.mNetworkRegistrationStates.stream() + .map(NetworkRegistrationState::sanitizeLocationInfo) + .collect(Collectors.toList()); + } + if (!removeCoarseLocation) return state; + + state.mDataOperatorAlphaLong = null; + state.mDataOperatorAlphaShort = null; + state.mDataOperatorNumeric = null; + state.mVoiceOperatorAlphaLong = null; + state.mVoiceOperatorAlphaShort = null; + state.mVoiceOperatorNumeric = null; + + return state; + } } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 3a4d33c18485..836a50bf05c7 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -866,7 +866,8 @@ public class SubscriptionManager { } /** - * Callback invoked when there is any change to any SubscriptionInfo. Typically + * Callback invoked when there is any change to any SubscriptionInfo, as well as once on + * registering for changes with {@link #addOnSubscriptionsChangedListener}. Typically * this method would invoke {@link #getActiveSubscriptionInfoList} */ public void onSubscriptionsChanged() { @@ -918,7 +919,9 @@ public class SubscriptionManager { /** * Register for changes to the list of active {@link SubscriptionInfo} records or to the * individual records themselves. When a change occurs the onSubscriptionsChanged method of - * the listener will be invoked immediately if there has been a notification. + * the listener will be invoked immediately if there has been a notification. The + * onSubscriptionChanged method will also be triggered once initially when calling this + * function. * * @param listener an instance of {@link OnSubscriptionsChangedListener} with * onSubscriptionsChanged overridden. @@ -1861,7 +1864,7 @@ public class SubscriptionManager { iSub.setDefaultSmsSubId(subscriptionId); } } catch (RemoteException ex) { - // ignore it + ex.rethrowFromSystemServer(); } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index f9cf25319e99..ced4f4ab6573 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1698,10 +1698,7 @@ public class TelephonyManager { * @deprecated use {@link #getAllCellInfo} instead, which returns a superset of this API. */ @Deprecated - @RequiresPermission(anyOf = { - android.Manifest.permission.ACCESS_COARSE_LOCATION, - android.Manifest.permission.ACCESS_FINE_LOCATION - }) + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public CellLocation getCellLocation() { try { ITelephony telephony = getITelephony(); @@ -4951,7 +4948,7 @@ public class TelephonyManager { * @return List of {@link android.telephony.CellInfo}; null if cell * information is unavailable. */ - @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public List<CellInfo> getAllCellInfo() { try { ITelephony telephony = getITelephony(); @@ -5028,7 +5025,7 @@ public class TelephonyManager { * @param executor the executor on which callback will be invoked. * @param callback a callback to receive CellInfo. */ - @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate( @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) { try { @@ -5067,7 +5064,7 @@ public class TelephonyManager { * @hide */ @SystemApi - @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_COARSE_LOCATION, + @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull WorkSource workSource, @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) { @@ -6657,9 +6654,10 @@ public class TelephonyManager { * * <p> Note that this scan can take a long time (sometimes minutes) to happen. * - * <p>Requires Permission: + * <p>Requires Permissions: * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or that the calling app has carrier * privileges (see {@link #hasCarrierPrivileges}) + * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. * * @return {@link CellNetworkScanResult} with the status * {@link CellNetworkScanResult#STATUS_SUCCESS} and a list of @@ -6668,12 +6666,15 @@ public class TelephonyManager { * * @hide */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresPermission(allOf = { + android.Manifest.permission.MODIFY_PHONE_STATE, + Manifest.permission.ACCESS_COARSE_LOCATION + }) public CellNetworkScanResult getAvailableNetworks() { try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.getCellNetworkScanResults(getSubId()); + return telephony.getCellNetworkScanResults(getSubId(), getOpPackageName()); } } catch (RemoteException ex) { Rlog.e(TAG, "getAvailableNetworks RemoteException", ex); @@ -6692,7 +6693,8 @@ public class TelephonyManager { * * <p>Requires Permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling - * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * app has carrier privileges (see {@link #hasCarrierPrivileges}) + * and {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * @param request Contains all the RAT with bands/channels that need to be scanned. * @param executor The executor through which the callback should be invoked. Since the scan @@ -6703,7 +6705,10 @@ public class TelephonyManager { * @return A NetworkScan obj which contains a callback which can be used to stop the scan. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresPermission(allOf = { + android.Manifest.permission.MODIFY_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION + }) public NetworkScan requestNetworkScan( NetworkScanRequest request, Executor executor, TelephonyScanManager.NetworkScanCallback callback) { @@ -6712,7 +6717,8 @@ public class TelephonyManager { mTelephonyScanManager = new TelephonyScanManager(); } } - return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback); + return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback, + getOpPackageName()); } /** @@ -6722,7 +6728,10 @@ public class TelephonyManager { * @removed */ @Deprecated - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresPermission(allOf = { + android.Manifest.permission.MODIFY_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION + }) public NetworkScan requestNetworkScan( NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) { return requestNetworkScan(request, AsyncTask.SERIAL_EXECUTOR, callback); @@ -8723,10 +8732,14 @@ public class TelephonyManager { * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} * * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) + * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresPermission(allOf = { + Manifest.permission.READ_PHONE_STATE, + Manifest.permission.ACCESS_COARSE_LOCATION + }) public ServiceState getServiceState() { return getServiceStateForSubscriber(getSubId()); } diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java index 96ff33255b53..91f74b867fa0 100644 --- a/telephony/java/android/telephony/TelephonyScanManager.java +++ b/telephony/java/android/telephony/TelephonyScanManager.java @@ -29,14 +29,14 @@ import android.os.Messenger; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; -import android.util.Log; import android.util.SparseArray; + +import com.android.internal.telephony.ITelephony; + import java.util.Arrays; import java.util.List; import java.util.concurrent.Executor; -import com.android.internal.telephony.ITelephony; - /** * Manages the radio access network scan requests and callbacks. */ @@ -183,6 +183,7 @@ public final class TelephonyScanManager { * * <p> * Requires Permission: + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} * Or the calling app has carrier privileges. @see #hasCarrierPrivileges * @@ -192,11 +193,13 @@ public final class TelephonyScanManager { * @hide */ public NetworkScan requestNetworkScan(int subId, - NetworkScanRequest request, Executor executor, NetworkScanCallback callback) { + NetworkScanRequest request, Executor executor, NetworkScanCallback callback, + String callingPackage) { try { ITelephony telephony = getITelephony(); if (telephony != null) { - int scanId = telephony.requestNetworkScan(subId, request, mMessenger, new Binder()); + int scanId = telephony.requestNetworkScan( + subId, request, mMessenger, new Binder(), callingPackage); saveScanInfo(scanId, request, executor, callback); return new NetworkScan(scanId, subId); } diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index 59167b7d5bba..73f055649b3f 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -350,6 +350,9 @@ public final class ImsCallProfile implements Parcelable { /** Indicates if the call is for testing purpose */ private boolean mEmergencyCallTesting = false; + /** Indicates if we have known the intent of the user for the call is emergency */ + private boolean mHasKnownUserIntentEmergency = false; + /** * Extras associated with this {@link ImsCallProfile}. * <p> @@ -789,12 +792,13 @@ public final class ImsCallProfile implements Parcelable { * * @hide */ - public void setEmergencyCallInfo(EmergencyNumber num) { + public void setEmergencyCallInfo(EmergencyNumber num, boolean hasKnownUserIntentEmergency) { setEmergencyServiceCategories(num.getEmergencyServiceCategoryBitmaskInternalDial()); setEmergencyUrns(num.getEmergencyUrns()); setEmergencyCallRouting(num.getEmergencyCallRouting()); setEmergencyCallTesting(num.getEmergencyNumberSourceBitmask() == EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST); + setHasKnownUserIntentEmergency(hasKnownUserIntentEmergency); } /** @@ -860,6 +864,19 @@ public final class ImsCallProfile implements Parcelable { } /** + * Set if we have known the user intent of the call is emergency. + * + * This is only used to specify when the dialed number is ambiguous when it can be identified + * as both emergency number and any other non-emergency number; e.g. in some situation, 611 + * could be both an emergency number in a country and a non-emergency number of a carrier's + * customer service hotline. + */ + @VisibleForTesting + public void setHasKnownUserIntentEmergency(boolean hasKnownUserIntentEmergency) { + mHasKnownUserIntentEmergency = hasKnownUserIntentEmergency; + } + + /** * Get the emergency service categories, only valid if {@link #getServiceType} returns * {@link #SERVICE_TYPE_EMERGENCY} * @@ -916,4 +933,16 @@ public final class ImsCallProfile implements Parcelable { public boolean isEmergencyCallTesting() { return mEmergencyCallTesting; } + + /** + * Checks if we have known the user intent of the call is emergency. + * + * This is only used to specify when the dialed number is ambiguous when it can be identified + * as both emergency number and any other non-emergency number; e.g. in some situation, 611 + * could be both an emergency number in a country and a non-emergency number of a carrier's + * customer service hotline. + */ + public boolean hasKnownUserIntentEmergency() { + return mHasKnownUserIntentEmergency; + } } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index caa367fbaafe..7089ee5671c2 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -765,7 +765,7 @@ interface ITelephony { * @param subId the id of the subscription. * @return CellNetworkScanResult containing status of scan and networks. */ - CellNetworkScanResult getCellNetworkScanResults(int subId); + CellNetworkScanResult getCellNetworkScanResults(int subId, String callingPackage); /** * Perform a radio network scan and return the id of this scan. @@ -774,10 +774,11 @@ interface ITelephony { * @param request Defines all the configs for network scan. * @param messenger Callback messages will be sent using this messenger. * @param binder the binder object instantiated in TelephonyManager. + * @param callingPackage the calling package * @return An id for this scan. */ int requestNetworkScan(int subId, in NetworkScanRequest request, in Messenger messenger, - in IBinder binder); + in IBinder binder, in String callingPackage); /** * Stop an existing radio network scan. diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java index d5549cc1355e..07b3a97817a3 100644 --- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java +++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java @@ -439,6 +439,14 @@ public class ActivityTestMain extends Activity { return true; } }); + menu.add("Require unknown permission").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + final Intent intent = new Intent(SLOW_RECEIVER_ACTION); + intent.putExtra(SLOW_RECEIVER_EXTRA, 5038); + sendOrderedBroadcast(intent, "com.google.android.test.activity.permission.UNDEFINED"); + return true; + } + }); menu.add("Stack Doc").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { ActivityManager.AppTask task = findDocTask(); diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index 1a4ec94d77b4..7b8c154dea1e 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -1028,8 +1028,28 @@ </activity> <activity - android:name="PositionListenerActivity" - android:label="RenderNode/PositionListener" + android:name="PositionListenerActivity" + android:label="RenderNode/PositionListener" + android:screenOrientation="fullSensor"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.android.test.hwui.TEST" /> + </intent-filter> + </activity> + + <activity + android:name="CustomRenderer" + android:label="HardwareRenderer/HelloTakeSurface" + android:screenOrientation="fullSensor"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.android.test.hwui.TEST" /> + </intent-filter> + </activity> + + <activity + android:name="MyLittleTextureView" + android:label="HardwareRenderer/MyLittleTextureView" android:screenOrientation="fullSensor"> <intent-filter> <action android:name="android.intent.action.MAIN" /> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java new file mode 100644 index 000000000000..60bd60f0bfd1 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.hwui; + +import android.app.Activity; +import android.graphics.Color; +import android.graphics.HardwareRenderer; +import android.graphics.Paint; +import android.graphics.RecordingCanvas; +import android.graphics.RenderNode; +import android.os.Bundle; +import android.util.Log; +import android.view.SurfaceHolder; + +public class CustomRenderer extends Activity { + private RenderNode mContent = new RenderNode("CustomRenderer"); + private HardwareRenderer mRenderer = new HardwareRenderer(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().takeSurface(mSurfaceCallbacks); + } + + private SurfaceHolder.Callback2 mSurfaceCallbacks = new SurfaceHolder.Callback2() { + + @Override + public void surfaceRedrawNeeded(SurfaceHolder holder) { + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + mContent.setLeftTopRightBottom(0, 0, width, height); + RecordingCanvas canvas = mContent.startRecording(); + canvas.drawColor(Color.WHITE); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setColor(Color.BLACK); + paint.setTextAlign(Paint.Align.CENTER); + paint.setTextSize(Math.min(width, height) * .05f); + canvas.drawText("Hello custom renderer!", width / 2, height / 2, paint); + mContent.endRecording(); + + mRenderer.setContentRoot(mContent); + mRenderer.setSurface(holder.getSurface()); + mRenderer.createRenderRequest() + .setVsyncTime(System.nanoTime()) + .setFrameCommitCallback(Runnable::run, () -> { + Log.d("CustomRenderer", "Frame committed!"); + }) + .syncAndDraw(); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + mRenderer.destroy(); + } + }; +} diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java new file mode 100644 index 000000000000..8bd7d797aea3 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.hwui; + +import android.app.Activity; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorSpace; +import android.graphics.HardwareRenderer; +import android.graphics.Outline; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.RenderNode; +import android.hardware.HardwareBuffer; +import android.media.Image; +import android.media.ImageReader; +import android.os.Bundle; +import android.widget.ImageView; + +public class MyLittleTextureView extends Activity { + private RenderNode mContent = new RenderNode("CustomRenderer"); + private HardwareRenderer mRenderer = new HardwareRenderer(); + private ImageView mImageView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mImageView = new ImageView(this); + mImageView.setScaleType(ImageView.ScaleType.FIT_CENTER); + setContentView(mImageView); + + ImageReader reader = ImageReader.newInstance(100, 100, PixelFormat.RGBA_8888, 3, + HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT); + mRenderer.setSurface(reader.getSurface()); + mRenderer.setLightSourceAlpha(0.0f, 1.0f); + mRenderer.setLightSourceGeometry(100 / 2f, 0f, 800.0f, 20.0f); + mContent.setLeftTopRightBottom(0, 0, 100, 100); + + Rect childRect = new Rect(25, 25, 65, 65); + RenderNode childNode = new RenderNode("shadowCaster"); + childNode.setLeftTopRightBottom(childRect.left, childRect.top, + childRect.right, childRect.bottom); + Outline outline = new Outline(); + outline.setRect(new Rect(0, 0, childRect.width(), childRect.height())); + outline.setAlpha(1f); + childNode.setOutline(outline); + { + Canvas canvas = childNode.startRecording(); + canvas.drawColor(Color.BLUE); + } + childNode.endRecording(); + childNode.setElevation(20f); + + { + Canvas canvas = mContent.startRecording(); + canvas.drawColor(Color.WHITE); + canvas.enableZ(); + canvas.drawRenderNode(childNode); + canvas.disableZ(); + } + mContent.endRecording(); + mRenderer.setContentRoot(mContent); + mRenderer.createRenderRequest() + .setWaitForPresent(true) + .syncAndDraw(); + Image image = reader.acquireNextImage(); + Bitmap bitmap = Bitmap.wrapHardwareBuffer(image.getHardwareBuffer(), + ColorSpace.get(ColorSpace.Named.SRGB)); + mImageView.setImageBitmap(bitmap); + image.close(); + } +} diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index a7c95c78d05d..e57d838bcf92 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -3817,11 +3817,14 @@ public class ConnectivityServiceTest { } @Test - public void testNattSocketKeepalives() throws Exception { + public void testNattSocketKeepalives_SingleThreadExecutor() throws Exception { final ExecutorService executorSingleThread = Executors.newSingleThreadExecutor(); doTestNattSocketKeepalivesWithExecutor(executorSingleThread); executorSingleThread.shutdown(); + } + @Test + public void testNattSocketKeepalives_InlineExecutor() throws Exception { final Executor executorInline = (Runnable r) -> r.run(); doTestNattSocketKeepalivesWithExecutor(executorInline); } @@ -3963,6 +3966,7 @@ public class ConnectivityServiceTest { testSocket2.close(); mWiFiNetworkAgent.disconnect(); + waitFor(mWiFiNetworkAgent.getDisconnectedCV()); } @Test diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto index 73b568e77689..9a1d94288363 100644 --- a/tools/aapt2/Resources.proto +++ b/tools/aapt2/Resources.proto @@ -151,10 +151,12 @@ message Overlayable { // Represents an overlayable <item> declaration within an <overlayable> tag. message OverlayableItem { enum Policy { - PUBLIC = 0; - SYSTEM = 1; - VENDOR = 2; - PRODUCT = 3; + NONE = 0; + PUBLIC = 1; + SYSTEM = 2; + VENDOR = 3; + PRODUCT = 4; + SIGNATURE = 5; } // The location of the <item> declaration in source. diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index 488de8789178..af9fdfbd364d 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -276,14 +276,19 @@ public class WifiInfo implements Parcelable { /** * Returns the service set identifier (SSID) of the current 802.11 network. + * <p> * If the SSID can be decoded as UTF-8, it will be returned surrounded by double - * quotation marks. Otherwise, it is returned as a string of hex digits. The - * SSID may be <unknown ssid> if there is no network currently connected, - * or if the caller has insufficient permissions to access the SSID. - * + * quotation marks. Otherwise, it is returned as a string of hex digits. + * The SSID may be + * <lt><unknown ssid>, if there is no network currently connected or if the caller has + * insufficient permissions to access the SSID.<lt> + * </p> + * <p> * Prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method * always returned the SSID with no quotes around it. - * @return the SSID + * </p> + * + * @return the SSID. */ public String getSSID() { if (mWifiSsid != null) { @@ -312,7 +317,13 @@ public class WifiInfo implements Parcelable { /** * Return the basic service set identifier (BSSID) of the current access point. - * The BSSID may be {@code null} if there is no network currently connected. + * <p> + * The BSSID may be + * <lt>{@code null}, if there is no network currently connected.</lt> + * <lt>{@code "02:00:00:00:00:00"}, if the caller has insufficient permissions to access the + * BSSID.<lt> + * </p> + * * @return the BSSID, in the form of a six-byte MAC address: {@code XX:XX:XX:XX:XX:XX} */ public String getBSSID() { @@ -511,9 +522,13 @@ public class WifiInfo implements Parcelable { /** * Each configured network has a unique small integer ID, used to identify - * the network when performing operations on the supplicant. This method - * returns the ID for the currently connected network. - * @return the network ID, or -1 if there is no currently connected network + * the network. This method returns the ID for the currently connected network. + * <p> + * The networkId may be {@code -1} if there is no currently connected network or if the caller + * has insufficient permissions to access the network ID. + * </p> + * + * @return the network ID. */ public int getNetworkId() { return mNetworkId; |